{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/evolve/activity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAM1D,MAAM,MAAM,eAAe,GACxB;IACA,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,YAAY,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC7B,GACD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAEhE,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,gBAAgB,CASrE;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAQ9E;AAOD,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe/G;AA2ED,wBAAsB,oBAAoB,CACzC,YAAY,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,EACtD,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,GAC5F,OAAO,CAAC,eAAe,EAAE,CAAC,CAuF5B","sourcesContent":["import { loadCompiledBundle } from \"../bundle/compile.ts\";\nimport { buildTrialComparison } from \"../comparison.ts\";\nimport type { EvoPaths } from \"../paths.ts\";\nimport { readScheduleConfig } from \"../scheduler.ts\";\nimport type { EvoService } from \"../service.ts\";\nimport type { EvolutionRun, Proposal } from \"../types.ts\";\nimport { listEvolutionRuns } from \"./run.ts\";\nimport { readPendingVerification } from \"./verification-decision.ts\";\n\nconst TERMINAL_RUN_STATUSES = new Set([\"completed\", \"failed\", \"cancelled\"]);\n\nexport type EvoActivityItem =\n\t| {\n\t\t\tkey: string;\n\t\t\tkind: \"run\";\n\t\t\ttext: string;\n\t\t\trun: EvolutionRun;\n\t\t\tproposal?: Proposal;\n\t\t\tcomponent?: string;\n\t\t\t/** The run is parked on a recommended-verification execute/skip/reject decision. */\n\t\t\tpendingVerification?: boolean;\n\t  }\n\t| { key: string; kind: \"proposal\"; text: string; proposal: Proposal; component?: string };\n\n/** What the user should do with an item next: decide, watch, or nothing. */\nexport type EvoActivityGroup = \"action\" | \"running\" | \"history\";\n\nexport function activityGroup(item: EvoActivityItem): EvoActivityGroup {\n\tif (item.kind === \"proposal\") {\n\t\tconst status = item.proposal.status;\n\t\treturn status === \"pending\" || status === \"deferred\" ? \"action\" : \"running\";\n\t}\n\tconst status = item.run.status;\n\tif (status === \"awaiting-canary-approval\" || status === \"awaiting-decision\") return \"action\";\n\tif (status === \"awaiting-evidence\" && item.pendingVerification) return \"action\";\n\treturn TERMINAL_RUN_STATUSES.has(status) ? \"history\" : \"running\";\n}\n\n/** Stable-reorder items: decisions first, then live work, then history. */\nexport function groupActivityItems(items: EvoActivityItem[]): EvoActivityItem[] {\n\tconst rank: Record<EvoActivityGroup, number> = { action: 0, running: 1, history: 2 };\n\treturn items\n\t\t.map((item, index) => ({ item, index }))\n\t\t.sort(\n\t\t\t(left, right) => rank[activityGroup(left.item)] - rank[activityGroup(right.item)] || left.index - right.index,\n\t\t)\n\t\t.map((entry) => entry.item);\n}\n\nfunction compact(value: string, maxLength = 54): string {\n\tconst normalized = value.replace(/\\s+/g, \" \").trim();\n\treturn normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1)}…`;\n}\n\nexport async function componentNameForProposal(paths: EvoPaths, proposal: Proposal): Promise<string | undefined> {\n\tif (!proposal.candidateDigest || !proposal.targetAbi) return undefined;\n\tconst [parent, candidate] = await Promise.all([\n\t\tloadCompiledBundle(paths, proposal.parentBundleDigest),\n\t\tloadCompiledBundle(paths, proposal.candidateDigest),\n\t]);\n\tfor (const [surface, selection] of Object.entries(candidate.policy.components ?? {})) {\n\t\tif (\n\t\t\tselection.abi === proposal.targetAbi &&\n\t\t\tJSON.stringify(selection) !== JSON.stringify(parent.policy.components?.[surface])\n\t\t) {\n\t\t\treturn selection.id;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction runStatusLabel(run: EvolutionRun): string {\n\treturn (\n\t\t{\n\t\t\tqueued: \"等待执行\",\n\t\t\tresearching: \"研究中\",\n\t\t\tplanned: \"计划已冻结\",\n\t\t\tbuilding: \"构建候选\",\n\t\t\tvalidating: \"确定性验证\",\n\t\t\treplaying: \"历史回放\",\n\t\t\tevaluating: \"独立评估\",\n\t\t\t\"awaiting-evidence\": \"等待实验数据\",\n\t\t\t\"awaiting-canary-approval\": \"等待 Canary 确认\",\n\t\t\ttrialing: \"Canary 运行中\",\n\t\t\t\"awaiting-decision\": \"等待最终决定\",\n\t\t\tpaused: \"已暂停\",\n\t\t\tcompleted: \"已完成\",\n\t\t\tfailed: \"执行失败\",\n\t\t\tcancelled: \"已取消\",\n\t\t}[run.status] ?? run.status\n\t);\n}\n\ninterface TrialDisplay {\n\tdue: boolean;\n\tcurrentSessions: number;\n\tminimumSessions: number;\n\tmaximumDurationDays: number;\n}\n\nfunction componentLabel(proposal: Proposal | undefined, component: string): string {\n\tconst surface = proposal?.targetAbi?.split(\"/\", 1)[0] ?? \"component\";\n\tconst short = component.endsWith(`-${surface}`) ? component.slice(0, -surface.length - 1) : component;\n\treturn `${surface}/${short}`;\n}\n\nfunction canaryStateText(proposal: Proposal | undefined, trial: TrialDisplay | undefined, noun: string): string {\n\tif (proposal?.artifacts.retrospective) return \"comparison ready\";\n\tif (trial?.due) return \"comparison pending\";\n\treturn trial ? `${noun} ${trial.currentSessions}/${trial.minimumSessions}` : `${noun} 运行中`;\n}\n\nfunction runText(\n\trun: EvolutionRun,\n\tcomponent: string | undefined,\n\tproposal: Proposal | undefined,\n\ttrial: TrialDisplay | undefined,\n\tpendingVerification = false,\n): string {\n\tif (component && run.status === \"trialing\") {\n\t\tconst bound = trial ? ` · ≤${trial.maximumDurationDays}d` : \"\";\n\t\treturn `Evo: ${canaryStateText(proposal, trial, \"Canary\")} · ${componentLabel(proposal, component)}${bound}`;\n\t}\n\tif (component && run.status === \"awaiting-canary-approval\") {\n\t\treturn `Evo: 等待发布确认 · ${componentLabel(proposal, component)}`;\n\t}\n\tif (run.status === \"awaiting-evidence\" && pendingVerification) {\n\t\treturn `Evo: 等待验证决策 · ${compact(run.request ?? \"定时演化\", 48)}`;\n\t}\n\treturn `Evo: ${runStatusLabel(run)} · ${compact(run.request ?? \"定时演化\", 48)}`;\n}\n\nfunction proposalText(proposal: Proposal, component: string | undefined, trial: TrialDisplay | undefined): string {\n\tif (component && proposal.status === \"trialing\") {\n\t\tconst bound = trial ? ` · ≤${trial.maximumDurationDays}d` : \"\";\n\t\treturn `Evo: ${canaryStateText(proposal, trial, \"Canary\")} · ${componentLabel(proposal, component)}${bound}`;\n\t}\n\tif (proposal.status === \"trialing\") {\n\t\treturn `Evo: ${canaryStateText(proposal, trial, \"Trial\")} · ${compact(proposal.motivation, 48)}`;\n\t}\n\tconst status = proposal.status === \"deferred\" ? \"已推迟\" : \"等待处理\";\n\treturn `Evo: ${status} · ${compact(proposal.motivation, 48)}`;\n}\n\nexport async function listEvoActivityItems(\n\tdependencies: { paths: EvoPaths; service: EvoService },\n\toptions: { includeHistory?: boolean; includeTrialComparison?: boolean; now?: () => Date } = {},\n): Promise<EvoActivityItem[]> {\n\tconst [runs, proposals, status] = await Promise.all([\n\t\tlistEvolutionRuns(dependencies.paths),\n\t\tdependencies.service.listProposals(),\n\t\tdependencies.service.status(),\n\t]);\n\tconst proposalById = new Map(proposals.map((proposal) => [proposal.id, proposal]));\n\tconst componentNames = new Map<string, string | undefined>();\n\tconst componentName = async (proposal: Proposal | undefined): Promise<string | undefined> => {\n\t\tif (!proposal) return undefined;\n\t\tif (!componentNames.has(proposal.id)) {\n\t\t\tcomponentNames.set(proposal.id, await componentNameForProposal(dependencies.paths, proposal));\n\t\t}\n\t\treturn componentNames.get(proposal.id);\n\t};\n\tconst visibleRuns = options.includeHistory ? runs : runs.filter((run) => !TERMINAL_RUN_STATUSES.has(run.status));\n\tlet trialDisplay: TrialDisplay | undefined;\n\tif (status.trial) {\n\t\tconst trialProposal = proposalById.get(status.trial.proposalId);\n\t\tif (trialProposal) {\n\t\t\tconst [schedule, comparison] = await Promise.all([\n\t\t\t\treadScheduleConfig(dependencies.paths),\n\t\t\t\tbuildTrialComparison(dependencies.paths, trialProposal, status.trial),\n\t\t\t]);\n\t\t\tconst maximumDurationDays = status.trial.canary?.maximumDurationDays ?? schedule.trialDueAfterDays;\n\t\t\tconst minimumSessions = status.trial.canary?.minimumSamples ?? schedule.trialDueAfterSessions;\n\t\t\tconst dueAt = Date.parse(status.trial.startedAt) + maximumDurationDays * 24 * 60 * 60 * 1_000;\n\t\t\ttrialDisplay = {\n\t\t\t\tdue:\n\t\t\t\t\ttrialProposal.artifacts.retrospective !== undefined ||\n\t\t\t\t\t(Number.isFinite(dueAt) && (options.now ?? (() => new Date()))().getTime() >= dueAt) ||\n\t\t\t\t\tcomparison.after.totals.sessions >= minimumSessions,\n\t\t\t\tcurrentSessions: comparison.after.totals.sessions,\n\t\t\t\tminimumSessions,\n\t\t\t\tmaximumDurationDays,\n\t\t\t};\n\t\t}\n\t}\n\tconst items: EvoActivityItem[] = [];\n\tconst linkedProposalIds = new Set<string>();\n\tfor (const run of visibleRuns) {\n\t\tconst proposal = run.proposalId ? proposalById.get(run.proposalId) : undefined;\n\t\tif (proposal && !TERMINAL_RUN_STATUSES.has(run.status)) linkedProposalIds.add(proposal.id);\n\t\tconst component = await componentName(proposal);\n\t\tconst pendingVerification =\n\t\t\trun.status === \"awaiting-evidence\" &&\n\t\t\t(await readPendingVerification(dependencies.paths, run.id)) !== undefined;\n\t\titems.push({\n\t\t\tkey: `run:${run.id}`,\n\t\t\tkind: \"run\",\n\t\t\ttext: runText(\n\t\t\t\trun,\n\t\t\t\tcomponent,\n\t\t\t\tproposal,\n\t\t\t\tproposal?.id === status.trial?.proposalId ? trialDisplay : undefined,\n\t\t\t\tpendingVerification,\n\t\t\t),\n\t\t\trun,\n\t\t\t...(proposal ? { proposal } : {}),\n\t\t\t...(component ? { component } : {}),\n\t\t\t...(pendingVerification ? { pendingVerification: true } : {}),\n\t\t});\n\t}\n\tfor (const proposal of proposals) {\n\t\tif (linkedProposalIds.has(proposal.id)) continue;\n\t\tif (proposal.status !== \"pending\" && proposal.status !== \"deferred\" && proposal.status !== \"trialing\") continue;\n\t\tconst component = await componentName(proposal);\n\t\titems.push({\n\t\t\tkey: `proposal:${proposal.id}`,\n\t\t\tkind: \"proposal\",\n\t\t\ttext: proposalText(proposal, component, proposal.id === status.trial?.proposalId ? trialDisplay : undefined),\n\t\t\tproposal,\n\t\t\t...(component ? { component } : {}),\n\t\t});\n\t}\n\treturn items.sort((left, right) => {\n\t\tconst priority = (item: EvoActivityItem): number => {\n\t\t\tconst status = item.kind === \"run\" ? item.run.status : item.proposal.status;\n\t\t\tif (status === \"trialing\") return 0;\n\t\t\tif (status === \"awaiting-canary-approval\") return 1;\n\t\t\tif (item.kind === \"run\" && item.pendingVerification) return 1;\n\t\t\tif (item.kind === \"run\" && !TERMINAL_RUN_STATUSES.has(item.run.status)) return 2;\n\t\t\tif (status === \"pending\" || status === \"deferred\") return 3;\n\t\t\treturn 4;\n\t\t};\n\t\treturn priority(left) - priority(right);\n\t});\n}\n"]}