@posthog/wizard 2.74.0 → 2.74.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/{add-mcp-server-to-clients-BtWwRG7O.js → add-mcp-server-to-clients-CBtInAoH.js} +4 -4
- package/dist/{add-mcp-server-to-clients-BtWwRG7O.js.map → add-mcp-server-to-clients-CBtInAoH.js.map} +1 -1
- package/dist/{agent-interface-CK6pmvkF.js → agent-interface-lCIi_alS.js} +8 -7
- package/dist/agent-interface-lCIi_alS.js.map +1 -0
- package/dist/{agent-prompt-loader-CPCNMvGh.js → agent-prompt-loader-CSxm81TM.js} +5 -5
- package/dist/{agent-prompt-loader-CPCNMvGh.js.map → agent-prompt-loader-CSxm81TM.js.map} +1 -1
- package/dist/{agent-runner-jz4yiJfh.js → agent-runner-DVrkOFDA.js} +17 -16
- package/dist/{agent-runner-jz4yiJfh.js.map → agent-runner-DVrkOFDA.js.map} +1 -1
- package/dist/analytics-3H_AlflZ.js +2 -0
- package/dist/{analytics-DZNb3aRN.js → analytics-CeVMOExk.js} +2 -2
- package/dist/{analytics-DZNb3aRN.js.map → analytics-CeVMOExk.js.map} +1 -1
- package/dist/{api-DXGaiK94.js → api-2nAofyKY.js} +3 -3
- package/dist/{api-DXGaiK94.js.map → api-2nAofyKY.js.map} +1 -1
- package/dist/bin.js +43 -43
- package/dist/{bounded-fs-CErPpkN2.js → bounded-fs-DmcmFPRq.js} +3 -3
- package/dist/{bounded-fs-CErPpkN2.js.map → bounded-fs-DmcmFPRq.js.map} +1 -1
- package/dist/{ci-install-CzWzi_qu.js → ci-install-lwYLQ4Yw.js} +4 -4
- package/dist/{ci-install-CzWzi_qu.js.map → ci-install-lwYLQ4Yw.js.map} +1 -1
- package/dist/{debug-D4y9vVWt.js → debug-BvheXnxB.js} +1 -1
- package/dist/{debug-ECdmmlDM.js → debug-GxjVplPx.js} +14 -335
- package/dist/debug-GxjVplPx.js.map +1 -0
- package/dist/{env-api-key-g5w775Ve.js → env-api-key-BMXH7_4u.js} +2 -2
- package/dist/{env-api-key-g5w775Ve.js.map → env-api-key-BMXH7_4u.js.map} +1 -1
- package/dist/{environment-DDkdsWUr.js → environment-C3nfPBu-.js} +4 -4
- package/dist/{environment-DDkdsWUr.js.map → environment-C3nfPBu-.js.map} +1 -1
- package/dist/{file-watcher-BNdFPlI8.js → file-watcher-BGElIpJi.js} +2 -2
- package/dist/{file-watcher-BNdFPlI8.js.map → file-watcher-BGElIpJi.js.map} +1 -1
- package/dist/{headless-ui-DBZCl4CG.js → headless-ui-jkS2Dm_G.js} +2 -2
- package/dist/{headless-ui-DBZCl4CG.js.map → headless-ui-jkS2Dm_G.js.map} +1 -1
- package/dist/{interactive-BKxXUB0T.js → interactive-CrQWwMr2.js} +2 -2
- package/dist/{interactive-BKxXUB0T.js.map → interactive-CrQWwMr2.js.map} +1 -1
- package/dist/{mcp-CwM45_Kp.js → mcp-S3yX8Uz1.js} +2 -2
- package/dist/{mcp-CwM45_Kp.js.map → mcp-S3yX8Uz1.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-R8H7uhuO.js → mcp-prompt-streaming-BvvCZBYa.js} +4 -4
- package/dist/{mcp-prompt-streaming-R8H7uhuO.js.map → mcp-prompt-streaming-BvvCZBYa.js.map} +1 -1
- package/dist/{non-interactive-DxzBwNxJ.js → non-interactive-B-MIn3vt.js} +2 -2
- package/dist/{non-interactive-DxzBwNxJ.js.map → non-interactive-B-MIn3vt.js.map} +1 -1
- package/dist/{orchestrator-tools-CTpKnk5-.js → orchestrator-tools-CefodRf2.js} +3 -3
- package/dist/{orchestrator-tools-CTpKnk5-.js.map → orchestrator-tools-CefodRf2.js.map} +1 -1
- package/dist/{package-manager-Bi6gaPa4.js → package-manager-pB6S0gyu.js} +2 -2
- package/dist/{package-manager-Bi6gaPa4.js.map → package-manager-pB6S0gyu.js.map} +1 -1
- package/dist/{pi-DpWFwz5u.js → pi-Dbw8VakI.js} +11 -11
- package/dist/{pi-DpWFwz5u.js.map → pi-Dbw8VakI.js.map} +1 -1
- package/dist/{playground-BtTCVaSD.js → playground-C7DwGktN.js} +11 -63
- package/dist/playground-C7DwGktN.js.map +1 -0
- package/dist/{posthog-B99H8Zyk.js → posthog-B3RuLfxH.js} +2 -2
- package/dist/{posthog-B99H8Zyk.js.map → posthog-B3RuLfxH.js.map} +1 -1
- package/dist/{posthog-integration-CxvkKY1j.js → posthog-integration-su2LXi3-.js} +12 -12
- package/dist/{posthog-integration-CxvkKY1j.js.map → posthog-integration-su2LXi3-.js.map} +1 -1
- package/dist/{provisioning-BewSEuFX.js → provisioning-B4Lr3-aS.js} +3 -3
- package/dist/{provisioning-BewSEuFX.js.map → provisioning-B4Lr3-aS.js.map} +1 -1
- package/dist/{queue-tools-CbRM-OET.js → queue-tools-BveMVXQm.js} +20 -7
- package/dist/queue-tools-BveMVXQm.js.map +1 -0
- package/dist/{registry-Bt2Mc4vC.js → registry-D8MIZ9kJ.js} +5 -5
- package/dist/{registry-Bt2Mc4vC.js.map → registry-D8MIZ9kJ.js.map} +1 -1
- package/dist/{security-Cmxb64bz.js → security-B_Eo15wD.js} +5 -5
- package/dist/{security-Cmxb64bz.js.map → security-B_Eo15wD.js.map} +1 -1
- package/dist/{setup-utils-CCphwDIz.js → setup-utils-D3OK2RE_.js} +8 -8
- package/dist/{setup-utils-CCphwDIz.js.map → setup-utils-D3OK2RE_.js.map} +1 -1
- package/dist/{start-tui-BRwZzlWb.js → start-tui-B7u962uh.js} +20 -20
- package/dist/{start-tui-BRwZzlWb.js.map → start-tui-B7u962uh.js.map} +1 -1
- package/dist/{steps-CJrx1t72.js → steps-D4mHkYX4.js} +7 -7
- package/dist/{steps-CJrx1t72.js.map → steps-D4mHkYX4.js.map} +1 -1
- package/dist/{store-BhcCTIn4.js → store-Cn1VK6U0.js} +7 -22
- package/dist/store-Cn1VK6U0.js.map +1 -0
- package/dist/{subagent-BeMPBF5r.js → subagent-1UEmD7bE.js} +2 -2
- package/dist/{subagent-BeMPBF5r.js.map → subagent-1UEmD7bE.js.map} +1 -1
- package/dist/{task-DX3eZgBL.js → task-DNHNZMO0.js} +12 -12
- package/dist/{task-DX3eZgBL.js.map → task-DNHNZMO0.js.map} +1 -1
- package/dist/{task-stream-BW7BG70C.js → task-stream-BJrRzR9K.js} +4 -4
- package/dist/{task-stream-BW7BG70C.js.map → task-stream-BJrRzR9K.js.map} +1 -1
- package/dist/{tasks-Did1AR7v.js → tasks-DSEJ8GZC.js} +2 -2
- package/dist/{tasks-Did1AR7v.js.map → tasks-DSEJ8GZC.js.map} +1 -1
- package/dist/{telemetry-CPWHrsXK.js → telemetry-DWkTy6TJ.js} +3 -3
- package/dist/{telemetry-CPWHrsXK.js.map → telemetry-DWkTy6TJ.js.map} +1 -1
- package/dist/{terminal-CpVFsbGu.js → terminal-DNnNkvFu.js} +15 -58
- package/dist/terminal-DNnNkvFu.js.map +1 -0
- package/dist/{tools-DjiwCd53.js → tools-HHK4XbnC.js} +6 -6
- package/dist/{tools-DjiwCd53.js.map → tools-HHK4XbnC.js.map} +1 -1
- package/dist/{wizard-abort-RUnY1Dax.js → wizard-abort-BVmjqUAQ.js} +3 -3
- package/dist/{wizard-abort-RUnY1Dax.js.map → wizard-abort-BVmjqUAQ.js.map} +1 -1
- package/dist/{wizard-abort-DjJ-6y6S.js → wizard-abort-CG_xzEFN.js} +1 -1
- package/dist/yara-hooks-BOMbtTOr.js +2 -0
- package/dist/{yara-hooks-jBqAkgxw.js → yara-hooks-qRkdjYiV.js} +4 -4
- package/dist/{yara-hooks-jBqAkgxw.js.map → yara-hooks-qRkdjYiV.js.map} +1 -1
- package/package.json +1 -1
- package/dist/agent-interface-CK6pmvkF.js.map +0 -1
- package/dist/analytics-CfadnLFG.js +0 -2
- package/dist/debug-ECdmmlDM.js.map +0 -1
- package/dist/playground-BtTCVaSD.js.map +0 -1
- package/dist/queue-tools-CbRM-OET.js.map +0 -1
- package/dist/store-BhcCTIn4.js.map +0 -1
- package/dist/terminal-CpVFsbGu.js.map +0 -1
- package/dist/yara-hooks-CBMSmD9F.js +0 -2
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"queue-tools-CbRM-OET.js","names":["path"],"sources":["../src/utils/atomic-ledger.ts","../src/lib/agent/runner/switchboard/models.ts","../src/lib/agent/runner/sequence/orchestrator/queue.ts","../src/lib/agent/runner/sequence/orchestrator/queue-tools.ts"],"sourcesContent":["/**\n * Small shared primitives for on-disk ledgers: an atomic JSON writer and a\n * single-chain async mutex. Used by the audit tools and by the orchestrator\n * queue. Lifted here so both share one implementation.\n */\nimport * as fs from 'fs';\n\n/**\n * Atomically write JSON: write to a `.tmp` file then rename over the target. The\n * rename bumps the file's mtime in one step, which is what a file watcher polls.\n */\nexport function writeJsonAtomic(targetPath: string, data: unknown): void {\n const tmpPath = `${targetPath}.tmp`;\n fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), 'utf8');\n fs.renameSync(tmpPath, targetPath);\n}\n\n/**\n * A single async mutex. Serializes read-modify-write cycles so concurrent callers\n * (parallel task agents, audit tool calls) never interleave a mutation.\n */\nexport function makeMutex() {\n let chain: Promise<unknown> = Promise.resolve();\n return async function run<T>(fn: () => Promise<T> | T): Promise<T> {\n const next = chain.then(() => fn());\n chain = next.catch(() => undefined);\n return next;\n };\n}\n","// Local capabilities inform transport parameters; gateway policy independently admits models and efforts.\nimport {\n SONNET_5_MODEL,\n HAIKU_MODEL,\n GPT5_6_LUNA_MODEL,\n GPT5_6_SOL_MODEL,\n GPT5_6_TERRA_MODEL,\n HAIKU_TRIAGE_MODEL,\n Harness,\n} from '@lib/constants';\n\n/** Reasoning effort, mapped by each harness to its provider transport. */\nconst THINKING_LEVELS = [\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n] as const;\nexport type ThinkingLevel = (typeof THINKING_LEVELS)[number];\n\n/** Whether a value (e.g. remote prompt frontmatter) names a valid effort. */\nexport function isThinkingLevel(value: unknown): value is ThinkingLevel {\n return (THINKING_LEVELS as readonly unknown[]).includes(value);\n}\n\n/**\n * An effort *override* is always a positive level — `'off'` is a model trait\n * (`reasoning: false`), not something a flag may force onto a reasoning model.\n */\nexport type EffortLevel = Exclude<ThinkingLevel, 'off'>;\n\nexport interface ModelCapabilities {\n /** Model supports reasoning; safe to request reasoning effort. */\n reasoning: boolean;\n /** Effort to request when reasoning. Omit for the harness/provider default. */\n thinkingLevel?: ThinkingLevel;\n}\n\n/** Explicit per-model traits. Anything absent falls back to `defaultCaps`. */\nexport const MODEL_CAPABILITIES: Record<string, ModelCapabilities> = {\n [SONNET_5_MODEL]: { reasoning: true },\n [HAIKU_MODEL]: { reasoning: true },\n // The openai 5.6 line; all reasoning models, so they must opt in past the\n // openai-completions default (reasoning off). Luna stays low for cheap,\n // short-context mechanical work; terra runs medium as the sonnet-tier parallel\n // — enough reasoning depth for the judgment tasks without high's latency blowup.\n [GPT5_6_LUNA_MODEL]: { reasoning: true, thinkingLevel: 'low' },\n [GPT5_6_TERRA_MODEL]: { reasoning: true, thinkingLevel: 'medium' },\n [GPT5_6_SOL_MODEL]: { reasoning: true, thinkingLevel: 'low' },\n};\n\n// Local model choices; gateway admission also requires allowed models, efforts, and prompt compatibility.\nexport const VALID_MODELS: ReadonlySet<string> = new Set(\n Object.keys(MODEL_CAPABILITIES),\n);\n\n/** Whether the value is an allow-listed model. */\nexport function isValidModel(model: unknown): model is string {\n return typeof model === 'string' && VALID_MODELS.has(model);\n}\n\n/** First allow-listed candidate, else throw so a bad id fails loud. */\nexport function requireKnownModel(\n ...candidates: (string | undefined)[]\n): string {\n for (const c of candidates) if (isValidModel(c)) return c;\n throw new Error(\n `No valid model to dispatch on (tried ${JSON.stringify(candidates)}). ` +\n `Allowed: ${[...VALID_MODELS].join(', ')}.`,\n );\n}\n\n// Unknown Anthropic models default to reasoning; OpenAI models must opt in through the local table.\nfunction defaultCaps(modelId: string): ModelCapabilities {\n return { reasoning: !modelId.startsWith('openai/') };\n}\n\n// Triage requests must satisfy gateway safety policy and preserve Warlock’s classifier format.\nexport const TRIAGE_MODELS: Record<Harness, string> = {\n [Harness.anthropic]: HAIKU_TRIAGE_MODEL,\n [Harness.pi]: GPT5_6_LUNA_MODEL,\n};\n\nexport function triageModelFor(harness: Harness): string {\n return TRIAGE_MODELS[harness];\n}\n\n/**\n * Capabilities for a gateway model id, table override then transport default.\n * `effortOverride` is a switchboard-resolved effort (e.g. from a pi effort\n * flag); it applies only when the model reasons at all — a non-reasoning model\n * rejects reasoning effort, so the override is dropped.\n */\nexport function modelCapabilities(\n modelId: string,\n effortOverride?: EffortLevel,\n): ModelCapabilities {\n const caps = MODEL_CAPABILITIES[modelId] ?? defaultCaps(modelId);\n if (caps.reasoning && effortOverride) {\n return { ...caps, thinkingLevel: effortOverride };\n }\n return caps;\n}\n","/**\n * The orchestrator task queue.\n *\n * In memory, synchronous, single-owner: one Node process drives the run, so\n * there is no locking. The queue imposes no execution policy — `nextRunnable`\n * returns every pending task whose dependencies are satisfied, and how many of\n * those run at once is decided by the task graph, not the queue.\n *\n * Every transition rewrites `<installDir>/.posthog-wizard-cache/queue.json`, a\n * small file holding the whole queue, handoffs included. It is the run's log\n * and the report's source. The whole cache folder is run-scoped and wiped when\n * the run ends.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { randomUUID } from 'crypto';\nimport { writeJsonAtomic } from '@utils/atomic-ledger';\nimport { analytics } from '@utils/analytics';\n\nexport const TaskStatus = {\n Pending: 'pending',\n Running: 'running',\n Done: 'done',\n Skipped: 'not needed',\n Failed: 'failed',\n} as const;\n\nexport type TaskStatus = (typeof TaskStatus)[keyof typeof TaskStatus];\n\n/**\n * Why a task ended as skipped.\n *\n * A skip has several causes that look identical from outside: an agent finding\n * the step does not apply, a user declining it, and an offer nobody answered.\n * They mean opposite things — the first is a no-op, the last two are the user\n * telling us something — and telling them apart needs the reason recorded at\n * the moment of the skip, not inferred later from timings.\n *\n * Recorded on the task like `error` is on a failed one, so it reaches the\n * skipped-task event and the run's queue.json alike.\n */\nexport const SkipReason = {\n /** The user answered the step's notice with Skip. */\n UserDeclined: 'user-declined',\n /** The notice went unanswered until it timed out. */\n NoticeTimeout: 'notice-timeout',\n /** The notice could not be shown at all, so consent failed closed. */\n NoticeError: 'notice-error',\n /** The task agent reported `not needed`. See {@link NotNeededReason} for why. */\n AgentNotNeeded: 'agent-not-needed',\n} as const;\n\nexport type SkipReason = (typeof SkipReason)[keyof typeof SkipReason];\n\n/**\n * Why the agent itself reported `not needed`.\n *\n * {@link SkipReason.AgentNotNeeded} records who decided the skip, not what they\n * decided. `complete_task` offers `not needed` both for \"the step does not\n * apply\" and for \"you cannot do it\", so an agent that never got a credential\n * out of the user lands in the same bucket as one that found nothing to\n * connect — and the bucket is named after the first meaning. For the one step\n * that stops to ask for credentials, that is the difference that matters: a\n * step nobody supplied a password for is not a step that did not apply.\n *\n * A sub-dimension of the skip reason, never a replacement for it, so every\n * existing reason value keeps counting exactly what it counted before.\n */\nexport const NotNeededReason = {\n /** Nothing to do: the step genuinely does not apply to this project. */\n NotApplicable: 'not-applicable',\n /** The user was asked and declined, or left the prompts unanswered. */\n UserDeclined: 'user-declined',\n /** Something outside the run stopped it — a credential, plan, or endpoint. */\n Blocked: 'blocked',\n} as const;\n\nexport type NotNeededReason =\n (typeof NotNeededReason)[keyof typeof NotNeededReason];\n\n/**\n * Whether a value names a reason. The pi harness hands tool arguments over\n * unvalidated, so an invented value would otherwise reach the analytics\n * dimension as free text — the one thing this step must never emit.\n */\nexport function isNotNeededReason(value: unknown): value is NotNeededReason {\n return (Object.values(NotNeededReason) as unknown[]).includes(value);\n}\n\nexport interface QueuedTask {\n id: string;\n type: string;\n /** Human-readable label for the TUI, set by the enqueuing agent. */\n label?: string;\n status: TaskStatus;\n /**\n * Ids of tasks that must finish before this one runs. Ids are generated at\n * enqueue, so a task can only depend on tasks created before it — the graph is\n * a DAG by construction, cycles cannot form. Unknown ids are rejected by the\n * enqueue_task guard.\n *\n * One sanctioned exception: {@link QueueStore.addDependencies} adds edges to a\n * still-pending task, for a runner-seeded task that was queued before the\n * planner ran and so could not name its dependencies then. It is additive,\n * pending-only, and cycle-checked per edge, so the DAG property above still\n * holds. Nothing else mutates this field.\n */\n dependsOn: string[];\n inputs: Record<string, unknown>;\n model?: string;\n attempts: number;\n maxAttempts: number;\n /** The structured handoff the task reported on completion. */\n handoff?: TaskHandoff;\n /** 'orchestrator' for seeded tasks, or the id of the task that enqueued this one. */\n enqueuedBy: string;\n /** Wizard-seeded only: terminal failure unblocks dependents and never fails the run. */\n optional?: boolean;\n createdAt: string;\n startedAt?: string;\n finishedAt?: string;\n error?: { type: string; message: string };\n /** Set when `status` is `not needed`: why. The failed-task `error` of a skip. */\n skipReason?: SkipReason;\n /** Set only for an agent-reported skip, and only when the agent declared it. */\n notNeededReason?: NotNeededReason;\n}\n\nexport interface QueueFile {\n version: 1;\n runId: string;\n tasks: QueuedTask[];\n}\n\n/** The structured handoff a task leaves for the next agent. */\nexport interface TaskHandoff {\n goals: string;\n did: string;\n forNextAgent: string;\n filesTouched?: string[];\n /** How the agent knows it worked — what it ran or observed. */\n evidence?: string;\n /** What the agent assumed about the app and could not verify. */\n assumptions?: string;\n /** A one-line summary of any unresolved conflict, surfaced in the outro. */\n conflict?: string;\n /** A finished section for the run's report, written by the task that owns the subject. */\n reportSection?: string;\n}\n\nexport interface EnqueueInput {\n type: string;\n label?: string;\n inputs?: Record<string, unknown>;\n dependsOn?: string[];\n model?: string;\n maxAttempts?: number;\n enqueuedBy?: string;\n optional?: boolean;\n}\n\nexport const QUEUE_DIR_NAME = '.posthog-wizard-cache';\nconst DEFAULT_MAX_ATTEMPTS = 2;\n\nfunction nowIso(): string {\n return new Date().toISOString();\n}\n\n/** Dropped in the cache folder so an orphaned copy explains itself. */\nconst DELETE_ME_FILE = '.DELETE-ME.md';\nconst DELETE_ME_BODY = `# Safe to delete\n\nThis folder contains run artifacts from the PostHog Wizard. This should have\nbeen deleted if the Wizard has finished running. If this wasn't deleted for\nsome reason, you can safely delete the entire \\`${QUEUE_DIR_NAME}/\\` folder.\n`;\n\n/** Every queue transition, in the order it is reflected. */\nexport type TransitionEvent =\n | 'enqueue'\n | 'start'\n | 'complete'\n | 'skip'\n | 'fail'\n | 'requeue';\n\nexport interface QueueStoreOptions {\n /**\n * Called on every transition with the task's post-transition state. The\n * runner uses it for telemetry; the store itself stays analytics-free.\n * Listener errors are reported but cannot break a transition.\n */\n onTransition?: (event: TransitionEvent, task: QueuedTask) => void;\n}\n\nexport class QueueStore {\n private tasks: QueuedTask[] = [];\n private readonly onTransition?: (\n event: TransitionEvent,\n task: QueuedTask,\n ) => void;\n\n readonly runId: string;\n readonly queuePath: string;\n\n constructor(installDir: string, runId: string, opts?: QueueStoreOptions) {\n this.onTransition = opts?.onTransition;\n this.runId = runId;\n const dir = path.join(installDir, QUEUE_DIR_NAME);\n this.queuePath = path.join(dir, 'queue.json');\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, DELETE_ME_FILE), DELETE_ME_BODY);\n }\n\n // ── Reads ───────────────────────────────────────────────────────────\n\n list(): readonly QueuedTask[] {\n return this.tasks;\n }\n\n get(id: string): QueuedTask | undefined {\n return this.tasks.find((t) => t.id === id);\n }\n\n /**\n * Every pending task whose dependencies are all satisfied (`done` or\n * `skipped`). A skipped dependency does not block downstream work.\n */\n nextRunnable(): QueuedTask[] {\n // A TERMINALLY failed optional dep satisfies like skipped; retryable failure still blocks.\n const doneIds = new Set(\n this.tasks\n .filter(\n (t) =>\n t.status === TaskStatus.Done ||\n t.status === TaskStatus.Skipped ||\n (t.status === TaskStatus.Failed &&\n t.optional === true &&\n t.attempts >= t.maxAttempts),\n )\n .map((t) => t.id),\n );\n return this.tasks.filter(\n (t) =>\n t.status === TaskStatus.Pending &&\n t.dependsOn.every((d) => doneIds.has(d)),\n );\n }\n\n /**\n * True when no task is running and none can be started. Either everything\n * is terminal, or the only pending tasks are blocked by a failed dependency.\n */\n isDrained(): boolean {\n if (this.tasks.some((t) => t.status === TaskStatus.Running)) return false;\n return this.nextRunnable().length === 0;\n }\n\n summary(): Record<TaskStatus, number> & { total: number } {\n const counts: Record<TaskStatus, number> = {\n [TaskStatus.Pending]: 0,\n [TaskStatus.Running]: 0,\n [TaskStatus.Done]: 0,\n [TaskStatus.Skipped]: 0,\n [TaskStatus.Failed]: 0,\n };\n for (const t of this.tasks) counts[t.status] += 1;\n return { ...counts, total: this.tasks.length };\n }\n\n readHandoff(id: string): TaskHandoff | null {\n return this.get(id)?.handoff ?? null;\n }\n\n /** Handoffs of completed tasks of a given type, oldest first. */\n readHandoffsByType(type: string): TaskHandoff[] {\n return this.tasks\n .filter((t) => t.type === type && t.handoff)\n .map((t) => t.handoff as TaskHandoff);\n }\n\n // ── Transitions (each one reflected to queue.json) ──────────────────\n\n enqueue(input: EnqueueInput): QueuedTask {\n const task: QueuedTask = {\n id: randomUUID(),\n type: input.type,\n label: input.label,\n status: TaskStatus.Pending,\n dependsOn: input.dependsOn ?? [],\n inputs: input.inputs ?? {},\n model: input.model,\n attempts: 0,\n maxAttempts: input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,\n enqueuedBy: input.enqueuedBy ?? 'orchestrator',\n optional: input.optional,\n createdAt: nowIso(),\n };\n this.tasks.push(task);\n this.reflect();\n this.notify('enqueue', task);\n return task;\n }\n\n /**\n * Add dependency edges to a pending task — the one sanctioned exception to\n * `dependsOn` being immutable (see the field's doc).\n *\n * A runner-seeded task is enqueued before the planner runs, so the tasks it\n * should wait for do not exist yet and it cannot name them. This closes that\n * gap once, between planning and the drain.\n *\n * Three rules keep the graph a DAG: additive only (never removes an edge),\n * pending only (a task that already started keeps the graph it ran under), and\n * every edge cycle-checked — a dep that already reaches this task through its\n * own chain is refused. Refusals come back in the result rather than throwing:\n * one bad edge is a bug worth reporting, not a reason to end a run that is\n * otherwise fine.\n */\n addDependencies(\n id: string,\n depIds: readonly string[],\n ): { added: string[]; refused: string[] } {\n const task = this.require(id);\n if (task.status !== TaskStatus.Pending) {\n return { added: [], refused: [...depIds] };\n }\n\n const added: string[] = [];\n const refused: string[] = [];\n for (const depId of depIds) {\n // Already an edge, or a self-loop: nothing to do and nothing wrong.\n if (depId === id || task.dependsOn.includes(depId)) continue;\n // Checked against the graph as it stands, so two edges added in one call\n // cannot together form a cycle the first check missed.\n if (!this.get(depId) || this.reaches(depId, id)) {\n refused.push(depId);\n continue;\n }\n task.dependsOn.push(depId);\n added.push(depId);\n }\n if (added.length > 0) this.reflect();\n return { added, refused };\n }\n\n start(id: string): QueuedTask {\n const t = this.require(id);\n t.status = TaskStatus.Running;\n t.startedAt = nowIso();\n t.attempts += 1;\n this.reflect();\n this.notify('start', t);\n return t;\n }\n\n complete(id: string, handoff?: TaskHandoff): QueuedTask {\n return this.finish(id, TaskStatus.Done, handoff);\n }\n\n /**\n * Terminal: the task was not done, and that is not a failure.\n *\n * The reason is required, and sits before the optional handoff for that\n * reason. A skip carrying no reason is what let a five-minute auto-decline\n * hide inside the same event as an agent deciding a step did not apply.\n *\n * `notNeededReason` splits the agent-reported reason one level further; only\n * an agent supplies it, and only when it declared one.\n */\n skip(\n id: string,\n reason: SkipReason,\n handoff?: TaskHandoff,\n notNeededReason?: NotNeededReason,\n ): QueuedTask {\n const t = this.require(id);\n t.skipReason = reason;\n if (notNeededReason) t.notNeededReason = notNeededReason;\n return this.finish(id, TaskStatus.Skipped, handoff);\n }\n\n fail(\n id: string,\n error: { type: string; message: string },\n handoff?: TaskHandoff,\n ): QueuedTask {\n const t = this.require(id);\n t.error = error;\n return this.finish(id, TaskStatus.Failed, handoff);\n }\n\n /** Put a failed/running task back to pending for a retry within the run. */\n requeue(id: string): QueuedTask {\n const t = this.require(id);\n t.status = TaskStatus.Pending;\n t.startedAt = undefined;\n t.finishedAt = undefined;\n this.reflect();\n this.notify('requeue', t);\n return t;\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private finish(\n id: string,\n status: 'done' | 'not needed' | 'failed',\n handoff?: TaskHandoff,\n ): QueuedTask {\n const t = this.require(id);\n if (handoff) t.handoff = handoff;\n t.status = status;\n t.finishedAt = nowIso();\n this.reflect();\n this.notify(\n status === TaskStatus.Done\n ? 'complete'\n : status === TaskStatus.Skipped\n ? 'skip'\n : 'fail',\n t,\n );\n return t;\n }\n\n private reflect(): void {\n const file: QueueFile = {\n version: 1,\n runId: this.runId,\n tasks: this.tasks,\n };\n writeJsonAtomic(this.queuePath, file);\n }\n\n private notify(event: TransitionEvent, task: QueuedTask): void {\n try {\n this.onTransition?.(event, task);\n } catch (error) {\n // A listener must never break a transition, but its failure is a bug.\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'orchestrator_queue_listener', event },\n );\n }\n }\n\n /** Whether `fromId` reaches `targetId` by following dependsOn edges. */\n private reaches(fromId: string, targetId: string): boolean {\n const seen = new Set<string>();\n const stack = [fromId];\n while (stack.length > 0) {\n const current = stack.pop() as string;\n if (current === targetId) return true;\n if (seen.has(current)) continue;\n seen.add(current);\n stack.push(...(this.get(current)?.dependsOn ?? []));\n }\n return false;\n }\n\n private require(id: string): QueuedTask {\n const t = this.get(id);\n if (!t) throw new Error(`No task ${id} in the queue`);\n return t;\n }\n}\n","/**\n * Orchestrator MCP tools, registered into the existing `wizard-tools` server when\n * a queue is present. They let the orchestrator agent and task agents grow the\n * queue, report completion with a structured handoff, and read prior handoffs.\n *\n * The guard logic and the apply functions are plain, exported, and unit-tested.\n * `buildOrchestratorTools` wraps them in the SDK `tool()` shape.\n */\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport {\n isValidModel,\n VALID_MODELS,\n} from '@lib/agent/runner/switchboard/models';\nimport {\n isNotNeededReason,\n NotNeededReason,\n SkipReason,\n TaskStatus,\n type QueueStore,\n type QueuedTask,\n type TaskHandoff,\n} from './queue';\n\n/**\n * The `complete_task` `notNeededReason` description, shared by both harnesses'\n * schemas so the ask cannot drift between them.\n *\n * Named per value rather than left to prose: the distinction the flow needs is\n * \"the user did not hand over a credential\" against \"there was nothing here to\n * connect\", and an agent asked for that in free text writes it into the handoff\n * — where {@link applyComplete} deliberately cannot forward it, because handoff\n * prose on this step reaches live database passwords.\n */\nexport const NOT_NEEDED_REASON_ASK = `Required when status is 'not needed': which of these ended the task. '${NotNeededReason.NotApplicable}' — the step genuinely does not apply to this project. '${NotNeededReason.UserDeclined}' — you asked the user and they declined, cancelled, or never answered. '${NotNeededReason.Blocked}' — something outside your and the user's control stopped you (a credential the project does not have, a plan or permission it lacks, an endpoint you could not reach). Ignored for any other status.`;\n\n/**\n * The `enqueue_task` `model` description, shared by both harnesses' schemas.\n *\n * The field was declared as a bare optional string while\n * {@link checkEnqueueGuards} rejects anything outside {@link VALID_MODELS}, so\n * the allow-list existed only in the rejection message — an agent had to guess\n * a gateway model id, trip the `invalid-model` guard, and spend a turn reading\n * the list back before it could enqueue. Naming the list where the agent picks\n * the value costs nothing and makes the guess unnecessary.\n */\nexport const ENQUEUE_MODEL_DESCRIPTION = `Optional model override for this task. Omit it to use the task's default, which is almost always right. If you do set it, it must be one of: ${[\n ...VALID_MODELS,\n].join(', ')}.`;\n\n/** The per-task remark ask, shared by both harnesses' complete_task schemas. */\nexport const REMARK_ASK =\n 'What information or guidance would have been useful to have in the integration prompt or documentation for this task — specifically anything that would have prevented tool failures, erroneous edits, or other wasted turns.';\n\nexport interface OrchestratorToolsContext {\n store: QueueStore;\n /** Task types the registry knows about. enqueue_task rejects anything else. */\n validTypes: readonly string[];\n /**\n * Types marked `sink: true` — the ones that run last and must therefore\n * depend, transitively, on every other task in the queue. Enqueuing one with\n * an incomplete closure is rejected, so a task the runner seeded before the\n * planner ran can never be left un-awaited.\n */\n sinkTypes?: readonly string[];\n /**\n * Types the wizard queues itself, before the planner runs. They are deferred\n * to the end of the drain and may stop to ask the user for input, so the edge\n * to them is one-way: the sink depends on them, nothing else may. See\n * {@link seededDepViolations}.\n */\n runnerSeededTypes?: readonly string[];\n /**\n * The id of the task this tool server is bound to. Each task agent gets its\n * own wizard-tools server, so attribution holds when independent tasks run\n * in parallel. Absent for the seed, which is not a task.\n */\n currentTaskId?: string;\n}\n\nexport interface EnqueueArgs {\n type: string;\n label?: string;\n inputs?: Record<string, unknown>;\n dependsOn?: string[];\n model?: string;\n reason: string;\n}\n\nexport type GuardResult =\n | { ok: true }\n | { ok: false; guard: string; message: string };\n\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;\n const entries = Object.entries(value as Record<string, unknown>).sort(\n ([a], [b]) => a.localeCompare(b),\n );\n return `{${entries\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .join(',')}}`;\n}\n\nfunction dedupKey(type: string, inputs: Record<string, unknown>): string {\n return `${type}::${stableStringify(inputs)}`;\n}\n\n/**\n * A backstop on total queue size. Tasks can enqueue tasks, so a misbehaving\n * type could grow the queue without bound. Keeping the graph small is the job\n * of good agent and skill design, not this number — it only stops a runaway.\n * The real flow is ~9 tasks, so this sits well clear of it.\n */\nconst MAX_QUEUE_TASKS = 30;\n\n/** Every task id reachable from `roots` through dependsOn edges, roots included. */\nexport function dependencyClosure(\n store: QueueStore,\n roots: readonly string[],\n): Set<string> {\n const seen = new Set<string>();\n const visit = (id: string): void => {\n if (seen.has(id)) return;\n seen.add(id);\n for (const dep of store.get(id)?.dependsOn ?? []) visit(dep);\n };\n for (const id of roots) visit(id);\n return seen;\n}\n\n/**\n * Tasks a sink would fail to wait for. A sink runs last by definition, so any\n * queued task outside its dependency closure is work whose handoff the sink\n * would miss — the exact failure mode of a task seeded before the planner ran.\n * Empty for a non-sink type, and for a sink that covers everything.\n */\nexport function uncoveredBySink(\n ctx: OrchestratorToolsContext,\n args: Pick<EnqueueArgs, 'type' | 'dependsOn'>,\n): QueuedTask[] {\n if (!ctx.sinkTypes?.includes(args.type)) return [];\n const covered = dependencyClosure(ctx.store, args.dependsOn ?? []);\n return ctx.store.list().filter((t) => !covered.has(t.id));\n}\n\n/**\n * Runner-seeded tasks a non-sink task would end up waiting for.\n *\n * The edge to a runner-seeded task is one-way. Such a task is deferred to the\n * end of the drain and may stop to ask the user for input, so anything that\n * waits on it inherits that wait — a coding step gated on someone typing a\n * database password. The sink is the sole exception: it runs last by\n * definition and must depend on every task, this one included.\n *\n * The whole closure is checked, not just direct dependencies, so the rule\n * cannot be reached through an intermediate hop.\n *\n * Empty for a sink, and for any task that does not reach a seeded task.\n */\nexport function seededDepViolations(\n ctx: OrchestratorToolsContext,\n args: Pick<EnqueueArgs, 'type' | 'dependsOn'>,\n): QueuedTask[] {\n const seeded = ctx.runnerSeededTypes ?? [];\n if (seeded.length === 0) return [];\n if (ctx.sinkTypes?.includes(args.type)) return [];\n const reached = dependencyClosure(ctx.store, args.dependsOn ?? []);\n return ctx.store\n .list()\n .filter((t) => reached.has(t.id) && seeded.includes(t.type));\n}\n\n/**\n * Validate an enqueue. Structural checks only — a real type, real dependencies,\n * a sink that waits for everything, no non-sink task waiting on a runner-seeded\n * one, not a literal duplicate, and not past the runaway backstop. How much\n * runs, and in what shape, is the task graph's business, not a knob's.\n */\nexport function checkEnqueueGuards(\n ctx: OrchestratorToolsContext,\n args: EnqueueArgs,\n): GuardResult {\n const tasks = ctx.store.list();\n\n if (tasks.length >= MAX_QUEUE_TASKS) {\n return {\n ok: false,\n guard: 'queue-full',\n message: `The queue already holds ${tasks.length} tasks (cap ${MAX_QUEUE_TASKS}). Refine the existing tasks rather than adding more.`,\n };\n }\n\n if (!ctx.validTypes.includes(args.type)) {\n return {\n ok: false,\n guard: 'unknown-type',\n message: `Unknown task type \"${\n args.type\n }\". Valid types: ${ctx.validTypes.join(', ')}.`,\n };\n }\n\n // Reject an agent pinning a task to a non-allow-listed model.\n if (args.model !== undefined && !isValidModel(args.model)) {\n return {\n ok: false,\n guard: 'invalid-model',\n message: `Model \"${\n args.model\n }\" is not allowed. Omit model to use the task default, or pick one of: ${[\n ...VALID_MODELS,\n ].join(', ')}.`,\n };\n }\n\n for (const dep of args.dependsOn ?? []) {\n if (!ctx.store.get(dep)) {\n return {\n ok: false,\n guard: 'unknown-dep',\n message: `Dependency \"${dep}\" is not a known task id.`,\n };\n }\n }\n\n const seededDeps = seededDepViolations(ctx, args);\n if (seededDeps.length > 0) {\n return {\n ok: false,\n guard: 'seeded-dep',\n message: `A \"${args.type}\" task must not wait for ${seededDeps\n .map((t) => `${t.type} (${t.id})`)\n .join(\n ', ',\n )} — directly or through its dependencies. The wizard placed those, runs them at the end, and they stop to ask the user for input, so anything waiting on one waits on a person. Remove them from dependsOn and enqueue it again; only the final reporting task may depend on them.`,\n };\n }\n\n const uncovered = uncoveredBySink(ctx, args);\n if (uncovered.length > 0) {\n const seededUncovered = uncovered.filter((t) =>\n (ctx.runnerSeededTypes ?? []).includes(t.type),\n );\n // \"Or to a task already in dependsOn\" is the right advice for an ordinary\n // task and exactly wrong for a runner-seeded one — routing it through an\n // intermediate satisfies the closure while breaking the one-way rule above.\n // So a seeded task is named with the only placement that is legal for it.\n const how =\n seededUncovered.length === uncovered.length\n ? \"Add them to this task's own dependsOn — for a task the wizard placed, that is the only legal spot — and enqueue it again.\"\n : 'Add them to dependsOn, or to a task already in dependsOn, and enqueue it again.';\n return {\n ok: false,\n guard: 'sink-closure',\n message: `A \"${\n args.type\n }\" task runs last, so it must depend on every other task — directly or through its dependencies. These are not covered: ${uncovered\n .map((t) => `${t.type} (${t.id})`)\n .join(', ')}. ${how}`,\n };\n }\n\n const key = dedupKey(args.type, args.inputs ?? {});\n if (\n tasks.some(\n (t) =>\n t.status !== TaskStatus.Failed && dedupKey(t.type, t.inputs) === key,\n )\n ) {\n return {\n ok: false,\n guard: 'dedup',\n message: `A \"${args.type}\" task with these inputs already exists.`,\n };\n }\n\n return { ok: true };\n}\n\nexport type EnqueueResult =\n | { ok: true; task: QueuedTask }\n | { ok: false; guard: string; message: string };\n\nexport function applyEnqueue(\n ctx: OrchestratorToolsContext,\n args: EnqueueArgs,\n): EnqueueResult {\n const guard = checkEnqueueGuards(ctx, args);\n if (!guard.ok) return guard;\n\n const task = ctx.store.enqueue({\n type: args.type,\n label: args.label,\n inputs: args.inputs ?? {},\n dependsOn: args.dependsOn ?? [],\n model: args.model,\n enqueuedBy: ctx.currentTaskId ?? 'orchestrator',\n });\n return { ok: true, task };\n}\n\nexport type CompleteResult = { ok: true } | { ok: false; message: string };\n\nexport type CompleteArgs = {\n status: 'done' | 'failed' | 'not needed';\n handoff: TaskHandoff;\n remark?: string;\n notNeededReason?: NotNeededReason;\n};\n\nexport function applyComplete(\n ctx: OrchestratorToolsContext,\n args: CompleteArgs,\n): CompleteResult {\n const id = ctx.currentTaskId;\n if (!id) {\n return {\n ok: false,\n message: 'complete_task can only be called by a running task agent.',\n };\n }\n if (args.remark) {\n analytics.wizardCapture('orchestrator remark', {\n task_type: ctx.store.get(id)?.type,\n remark: args.remark,\n });\n }\n if (args.status === TaskStatus.Failed) {\n ctx.store.fail(\n id,\n { type: 'self-reported', message: args.handoff.forNextAgent },\n args.handoff,\n );\n } else if (args.status === TaskStatus.Skipped) {\n // The agent's own words stay in the handoff and out of telemetry. This flow\n // reaches live database and API credentials, and the repo has no redaction\n // pass for handoff prose, so the event carries the task type, the reason,\n // and the closed set of `notNeededReason` values — enough to separate an\n // agent no-op from a user decline from a blocked step, with no free text.\n ctx.store.skip(\n id,\n SkipReason.AgentNotNeeded,\n args.handoff,\n isNotNeededReason(args.notNeededReason)\n ? args.notNeededReason\n : undefined,\n );\n } else {\n ctx.store.complete(id, args.handoff);\n }\n return { ok: true };\n}\n\nexport function applyReadHandoffs(\n ctx: OrchestratorToolsContext,\n args: { type?: string; taskId?: string },\n): TaskHandoff[] {\n if (args.taskId) {\n const h = ctx.store.readHandoff(args.taskId);\n return h ? [h] : [];\n }\n if (args.type) {\n return ctx.store.readHandoffsByType(args.type);\n }\n // No filter: every handoff of a dependency of the current task.\n const currentId = ctx.currentTaskId;\n const current = currentId ? ctx.store.get(currentId) : undefined;\n if (!current) return [];\n return current.dependsOn\n .map((depId) => ctx.store.readHandoff(depId))\n .filter((h): h is TaskHandoff => h !== null);\n}\n\n// Caps each LLM-authored free-text field; queue.json rewrites whole per transition.\nconst HANDOFF_TEXT_MAX = 8_000;\n\n/**\n * The one description of every handoff field, shared by both harnesses'\n * `complete_task` schemas — the zod shape below and the typebox mirror in\n * `harness/pi/orchestrator-tools.ts`. A field an agent cannot see is a field it\n * cannot fill, so the two drifting silently drops whatever the missing field\n * carried; `__tests__/handoff-schema-parity.test.ts` holds them level.\n */\nexport const HANDOFF_FIELDS = {\n goals: 'What this task was asked to achieve.',\n did: 'What you actually did — for each file you edited: the change, the intention behind it, and the analytics it should feed (the insight, funnel, or dashboard tile it becomes part of).',\n forNextAgent: 'What the next agent should know.',\n filesTouched: 'Paths of every file you edited.',\n evidence:\n 'How you know it worked — what you ran or observed, not what you expect.',\n assumptions: 'What you assumed about the app and could not verify.',\n conflict:\n 'A one-line summary of any conflict you could not cleanly resolve (e.g. a dependency or build conflict). Put full detail in your work; this line is surfaced to the user.',\n reportSection:\n 'A finished markdown section about your work for the run report, written only when your task instructions ask for one. The reporting task includes it as its own section instead of rewriting it.',\n} as const satisfies Record<keyof Required<TaskHandoff>, string>;\n\nconst HANDOFF_SHAPE = {\n goals: z.string().max(HANDOFF_TEXT_MAX).describe(HANDOFF_FIELDS.goals),\n did: z.string().max(HANDOFF_TEXT_MAX).describe(HANDOFF_FIELDS.did),\n forNextAgent: z\n .string()\n .max(HANDOFF_TEXT_MAX)\n .describe(HANDOFF_FIELDS.forNextAgent),\n filesTouched: z\n .array(z.string().max(1_000))\n .max(200)\n .optional()\n .describe(HANDOFF_FIELDS.filesTouched),\n evidence: z\n .string()\n .max(HANDOFF_TEXT_MAX)\n .optional()\n .describe(HANDOFF_FIELDS.evidence),\n assumptions: z\n .string()\n .max(HANDOFF_TEXT_MAX)\n .optional()\n .describe(HANDOFF_FIELDS.assumptions),\n conflict: z\n .string()\n .max(HANDOFF_TEXT_MAX)\n .optional()\n .describe(HANDOFF_FIELDS.conflict),\n reportSection: z\n .string()\n .max(HANDOFF_TEXT_MAX)\n .optional()\n .describe(HANDOFF_FIELDS.reportSection),\n};\n\n/** Exported so the parity test can compare both harnesses' field sets. */\nexport const HANDOFF_SHAPE_KEYS: readonly string[] = Object.keys(HANDOFF_SHAPE);\n\n/**\n * `complete_task`'s own arguments, held level with the pi mirror by the same\n * parity test that guards the handoff — a top-level field can go missing on the\n * harness that runs just as easily as a nested one.\n */\nconst COMPLETE_SHAPE = {\n status: z.enum(['done', 'failed', 'not needed']),\n handoff: z.object(HANDOFF_SHAPE),\n remark: z.string().optional().describe(REMARK_ASK),\n notNeededReason: z\n .enum([\n NotNeededReason.NotApplicable,\n NotNeededReason.UserDeclined,\n NotNeededReason.Blocked,\n ])\n .optional()\n .describe(NOT_NEEDED_REASON_ASK),\n};\n\n/** Exported so the parity test can compare both harnesses' field sets. */\nexport const COMPLETE_SHAPE_KEYS: readonly string[] =\n Object.keys(COMPLETE_SHAPE);\n\ntype SdkTool = (\n name: string,\n description: string,\n // The SDK accepts a plain object of zod fields as the schema.\n schema: Record<string, z.ZodTypeAny>,\n handler: (args: never) => unknown,\n) => unknown;\n\nfunction textResult(text: string, isError = false) {\n return { isError, content: [{ type: 'text' as const, text }] };\n}\n\n/**\n * Build the orchestrator tools in the SDK `tool()` shape. Called from\n * createWizardToolsServer only when a queue context is present.\n */\nexport function buildOrchestratorTools(\n tool: SdkTool,\n ctx: OrchestratorToolsContext,\n): unknown[] {\n const enqueueTask = tool(\n 'enqueue_task',\n 'Add a task to the orchestrator queue. Use it to seed work and to enqueue follow-up work you discover. Keep tasks small and discrete.',\n {\n type: z\n .string()\n .describe(`The task type. One of: ${ctx.validTypes.join(', ')}.`),\n label: z\n .string()\n .optional()\n .describe(\n 'A short label for the UI — the action in a few words (e.g. \"Add the PostHog SDK\", \"Initialize PostHog\"). Leave out file names, class names, and other specifics.',\n ),\n inputs: z.record(z.unknown()).optional(),\n dependsOn: z\n .array(z.string())\n .optional()\n .describe('Task ids that must be done before this task runs.'),\n model: z.string().optional().describe(ENQUEUE_MODEL_DESCRIPTION),\n reason: z.string().describe('One line on why this task is needed.'),\n },\n ((args: EnqueueArgs) => {\n const res = applyEnqueue(ctx, args);\n if (!res.ok) {\n analytics.wizardCapture('orchestrator guard tripped', {\n guard: res.guard,\n type: args.type,\n });\n return textResult(res.message, true);\n }\n return textResult(JSON.stringify({ id: res.task.id }));\n }) as (args: never) => unknown,\n );\n\n const completeTask = tool(\n 'complete_task',\n \"Report the outcome of your task. Always call this exactly once when you finish, with a structured handoff for the next agent. Use status 'not needed' when the task does not apply to this project and you cannot do it (say why in the handoff) — not 'done'.\",\n COMPLETE_SHAPE,\n ((args: CompleteArgs) => {\n const res = applyComplete(ctx, args);\n if (!res.ok) return textResult(res.message, true);\n return textResult('ok');\n }) as (args: never) => unknown,\n );\n\n const readHandoffs = tool(\n 'read_handoffs',\n 'Read structured handoffs from earlier tasks. With no argument, returns the handoffs of your dependencies.',\n {\n type: z.string().optional(),\n taskId: z.string().optional(),\n },\n ((args: { type?: string; taskId?: string }) => {\n const handoffs = applyReadHandoffs(ctx, args);\n return textResult(JSON.stringify(handoffs, null, 2));\n }) as (args: never) => unknown,\n );\n\n return [enqueueTask, completeTask, readHandoffs];\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store-BhcCTIn4.js","names":[],"sources":["../src/lib/programs/program-step.ts","../src/lib/programs/ai-opt-in-gate.ts","../src/ui/tui/screen-sequences.ts","../src/ui/tui/router.ts","../src/ui/tui/store.ts"],"sourcesContent":["import type {\n WizardSession,\n DiscoveredFeature,\n TaskNotice,\n} from '@lib/wizard-session';\nimport type { WizardReadinessResult } from '@lib/health-checks/readiness';\nimport type { ProgramRun } from '@lib/agent/agent-runner';\nimport type { Integration } from '@lib/constants';\nimport type { FrameworkConfig } from '@lib/framework-config';\nimport type { ContentBlock } from '@ui/tui/primitives/index';\nimport type { WizardStore } from '@ui/tui/store';\nimport type { Tip } from '@ui/tui/components/TipsCard';\n// Type-only — erased at compile time, so no runtime cycle with the\n// registry that imports `ProgramConfig` back from this module.\nimport type { ProgramId } from './program-registry.js';\n\n/**\n * A program step is the primary unit of the wizard's execution model.\n *\n * It can own:\n * - a screen in the TUI (optional — some steps are headless)\n * - agent work via a program reference (optional — some steps are UI-only)\n * - completion and visibility predicates\n *\n * The PostHog integration program is one ordered list of steps.\n * Other programs (e.g. revenue analytics) register a different step list.\n */\n/**\n * Context passed to onInit callbacks — fires when the TUI starts\n * rendering, before bin.ts has assigned the real session.\n */\nexport interface StoreInitContext {\n readonly session: WizardSession;\n readonly setReadinessResult: (result: WizardReadinessResult | null) => void;\n readonly setFrameworkContext: (key: string, value: unknown) => void;\n readonly emitChange: () => void;\n}\n\n/**\n * Context passed to onReady callbacks — fires after bin.ts has assigned\n * the real session, so reading `session.installDir` returns the target\n * project. Use for async pre-program work like prerequisite detection.\n */\nexport interface ProgramReadyContext {\n readonly session: WizardSession;\n readonly setFrameworkContext: (key: string, value: unknown) => void;\n\n // Detection-specific methods — used by core-integration's detect step\n readonly setFrameworkConfig: (\n integration: Integration,\n config: FrameworkConfig,\n ) => void;\n readonly setDetectedFramework: (label: string) => void;\n readonly setSkillId: (skillId: string | null) => void;\n readonly setUnsupportedVersion: (info: {\n current: string;\n minimum: string;\n docsUrl: string;\n }) => void;\n readonly addDiscoveredFeature: (feature: DiscoveredFeature) => void;\n readonly setDetectionComplete: () => void;\n}\n\nexport interface ProgramStep {\n /** Unique identifier for this step */\n id: string;\n\n /** Human-readable label for progress display */\n label: string;\n\n /**\n * TUI screen this step owns, if any.\n * Matches the ScreenId enum values (e.g. 'intro', 'run', 'outro').\n */\n screenId?: string;\n\n /**\n * For a run step (`screenId: 'run'`): runs this step's own agent. A program\n * exports a self-contained run step and another imports it into its step list\n * — e.g. posthog-integration exports a run step that runs its agent, and\n * self-driving imports it before its own run step. Omit to run the host\n * program's own agent (`config.run`).\n */\n run?: (session: WizardSession) => Promise<void>;\n\n /**\n * For a run step: prepare a derived session before its agent runs — e.g.\n * gather framework context for the chosen project. The session it receives is\n * the run's own, so writes don't leak into later runs.\n */\n onRunPrep?: (session: WizardSession) => Promise<void>;\n\n /**\n * For a run step: the working directory its agent runs in, resolved from the\n * session (e.g. self-driving's integration runs in the picked monorepo\n * sub-app, not the repo root). The runner scopes a derived session to this\n * dir for that run only. Defaults to `session.installDir`.\n */\n targetDir?: (session: WizardSession) => string;\n\n /**\n * Whether this step should be visible in the current program.\n * If omitted, the step is always visible.\n */\n show?: (session: WizardSession) => boolean;\n\n /**\n * Exit condition for the screen. Router advances when true.\n * Defaults to `gate` if unset.\n */\n isComplete?: (session: WizardSession) => boolean;\n\n /**\n * Define a gate if your screen needs to await user interactions.\n * bin.ts can `await store.getGate(stepId)` to pause until the\n * predicate becomes true.\n */\n gate?: (session: WizardSession) => boolean;\n\n /**\n * Called once when the TUI starts rendering, with the default\n * session. Use for session-independent fire-and-forget work that\n * should start as early as possible (e.g. health check kicked off\n * while the user is still reading the intro screen). Never fires for\n * a store that isn't rendering screens (tests, playground).\n */\n onInit?: (ctx: StoreInitContext) => void;\n\n /**\n * Called once after bin.ts has assigned the real session to the store,\n * before any gate is awaited. Awaited in sequence with other steps'\n * onReady callbacks. Use for session-dependent pre-program work like\n * scanning the installDir for prerequisites. May be sync or async.\n */\n onReady?: (ctx: ProgramReadyContext) => void | Promise<void>;\n\n /**\n * Report this step's analytics under a different program than its host, for\n * steps shared across programs (the MCP tutorial is all of `mcp-tutorial`\n * and the last step of `mcp-add`). Attribution only — scopes, bindings, and\n * sequences still follow the host. Matched by `screenId`, so headless steps\n * are unaffected.\n */\n reportsAsProgramId?: ProgramId;\n}\n\n/**\n * Declares a program's place in the wizard CLI surface.\n *\n * Mirrors the `cli:` block in context-mill skill configs so wizard-native\n * programs and skill-backed programs share one vocabulary. Field names\n * match `ProgramConfig.command` / `parentCommand` above, so contributors\n * only learn one set of words.\n *\n * - `role: 'command'` — appears as a normal wizard command.\n * - `role: 'skill'` — reachable only via `wizard skill <id>`.\n * - `role: 'internal'` — hidden everywhere, only reachable via the\n * `--skill=<id>` dev escape hatch.\n *\n * Mapping table — declaration on the left, registered command on the right:\n *\n * { role: 'command', → wizard revenue-analytics\n * command: 'revenue-analytics' }\n *\n * { role: 'command', → wizard audit feature-flags\n * parentCommand: 'audit',\n * command: 'feature-flags' }\n *\n * { role: 'skill' } → wizard skill <id>\n *\n * `cli` only configures the command shape — the verbs the user types.\n * Flags and positional args (e.g. `--since=30d`) are configured on\n * `cliOptions`, not here.\n *\n * Naming rule: commands use the full PostHog product name with hyphens\n * (`revenue-analytics`, `feature-flags`, `session-replay`), not\n * abbreviations like `revenue` or `flags`.\n */\nexport interface ProgramCliSurface {\n /** Where the program appears in the wizard CLI surface. */\n role: 'command' | 'skill' | 'internal';\n /**\n * The user-typed word that registers this program (e.g. `'feature-flags'`\n * in `wizard audit feature-flags`, or `'revenue-analytics'` in\n * `wizard revenue-analytics`). Required when `role` is `'command'`.\n */\n command?: string;\n /**\n * The command this program nests under (e.g. `'audit'` for\n * `wizard audit feature-flags`). Omit for flat / standalone commands.\n */\n parentCommand?: string;\n}\n\n/**\n * Uniform configuration for a wizard program.\n *\n * Each program directory exports one of these. The system uses it\n * for CLI registration, sequence/step wiring, and skill bootstrap.\n */\nexport interface ProgramConfig {\n /** CLI command name (e.g. 'revenue-analytics'). Omit for the default program. */\n command?: string;\n /**\n * Parent CLI command to nest this program under. When set, the program is\n * registered as `<parentCommand> <command>` instead of as a top-level\n * command. The parent must itself be a registered subcommand program. Omit\n * for top-level programs.\n */\n parentCommand?: string;\n /** CLI description shown in --help */\n description: string;\n /** Unique program id — matches the Program enum value */\n id: string;\n /**\n * Content-mill flow the orchestrator loads its agent prompts + step-skills\n * from (`agents/<flow>/` and `skills/<flow>/`). Defaults to `id`; set it when\n * the content-mill flow name diverges from the program id.\n */\n agentFlow?: string;\n /**\n * Whether this program's agent run requires third-party AI services.\n *\n * When true (the default), the wizard checks\n * `apiUser.organization.is_ai_data_processing_approved` after auth and\n * renders `AiOptInRequiredScreen` if the org has not opted in. Matches\n * Max's strict reading: only literal `true` proceeds.\n *\n * Opt out (set to `false`) for programs that don't run the agent —\n * doctor, mcp install/remove/tutorial, source-map upload. The safe\n * default is `true` so future programs gate by declaration.\n */\n requiresAi?: boolean;\n /**\n * Context-mill skill ID this program installs and runs. When present,\n * bin.ts seeds `session.skillId` with this value before the TUI renders\n * so intro screens can resolve skill metadata without waiting for the\n * agent run.\n */\n skillId?: string;\n /** The ordered step list */\n steps: ProgramStep[];\n /** Agent run config. Static object or async function for dynamic config. */\n run?: ProgramRun | ((session: WizardSession) => Promise<ProgramRun>);\n /**\n * CI-mode pre-run strategy. When set, runWizardCI awaits this after building\n * the ci:true session and before the agent runs, instead of walking step\n * onReady hooks. Use for headless prerequisite work (e.g. framework\n * detection) that the TUI performs via step onReady callbacks.\n */\n ciPreRun?: (session: WizardSession) => Promise<void>;\n /**\n * Tasks the orchestrator queues itself, before the planner runs, from what\n * the wizard detected. Their types are marked `runnerSeeded: true` in the\n * agent prompt, so the planner never sees them: whether such a task runs is\n * decided here, in code, not by a model that could invent it or forget it.\n * Return an empty list to queue none.\n */\n seedTasks?: (session: WizardSession) => Array<{\n type: string;\n label?: string;\n inputs?: Record<string, unknown>;\n /**\n * Shown before the run starts, letting the user decline the task. The\n * program owns the words — the runner and the modal only carry them. A\n * task without one is queued silently.\n */\n notice?: TaskNotice;\n }>;\n /** Prerequisites: other program ids that must have run first */\n requires?: string[];\n /**\n * Path (relative to installDir) of the report file the program writes.\n * Mirrors `run.reportFile` but lifted to the top level so UI screens can\n * read it synchronously without resolving a deferred `run` function.\n */\n reportFile?: string;\n /**\n * Agent-authored event-plan artifact to mirror into the wizard session.\n * Relative to `session.installDir`. Programs that do not produce an event\n * plan leave this unset, so generic runner machinery does not inspect a\n * stale or unrelated `.posthog-events.json` file.\n */\n eventPlanFile?: string;\n /**\n * LearnCard deck rendered in the shared `RunScreen` while the agent\n * runs. Lives at `<program>/content/index.tsx` by convention.\n * Programs that ship a custom RunScreen variant (audit) or skip the\n * run step (posthog-doctor) leave this unset.\n */\n getContentBlocks?: (store?: WizardStore) => ContentBlock[];\n /**\n * Tips shown in the run screen's right pane (the `Tips` sidebar) once\n * the LearnCard finishes. Lets a program supply its own explainer copy\n * (e.g. self-driving explaining what signal sources and scouts are)\n * instead of the generic onboarding deck. Unset → `RunScreen` falls back\n * to `DEFAULT_TIPS`, so every other program is unaffected. Lives at\n * `<program>/content/tips.ts` by convention.\n */\n getTips?: (store?: WizardStore) => Tip[];\n /**\n * Subcommand-specific CLI options. Spread into yargs `.options(...)` when the\n * program's subcommand is registered. Program-specific knowledge stays in\n * the program config, not in bin.ts. Typed as `unknown` to avoid pulling a\n * yargs dependency into this module.\n */\n cliOptions?: Record<string, unknown>;\n /**\n * Translate parsed CLI argv into extra options the runner consumes. Runs\n * after yargs validation, before runWizard/runWizardCI. Use this when a flag\n * needs to derive another field (e.g. `--product=statsig` → `skillId:\n * 'migrate-statsig'`).\n */\n mapCliOptions?: (argv: Record<string, unknown>) => Record<string, unknown>;\n /**\n * Extra tool names added on top of BASE_ALLOWED_TOOLS for this program's\n * agent run. Use for tools that only this program needs.\n */\n allowedTools?: readonly string[];\n /**\n * Tool names removed from BASE_ALLOWED_TOOLS for this program's agent\n * run. Use to forbid a base tool — e.g. `['Agent']` to block subagent\n * dispatch in a program whose steps are explicitly single-agent.\n */\n disallowedTools?: readonly string[];\n /**\n * Declares this program's place in the wizard CLI surface. See\n * `ProgramCliSurface` for semantics.\n */\n cli?: ProgramCliSurface;\n}\n\n/**\n * Project program steps into the narrower Screen shape the router consumes.\n *\n * Two things happen here:\n * 1. Headless steps (no `screenId`) are filtered out. The router walks\n * visible screens; gate-only steps like `detect` are store concerns.\n * 2. The step is narrowed to just { id, show, isComplete } — the\n * router has no business touching gate, onInit, or label.\n *\n * This intentional separation keeps the router focused on one question:\n * \"Which screen should be rendered right now?\"\n */\nexport function createProgramSequence(steps: ProgramStep[]): Array<{\n id: string;\n show?: (session: WizardSession) => boolean;\n isComplete?: (session: WizardSession) => boolean;\n}> {\n const entries = steps\n .filter((step) => step.screenId != null)\n .map((step) => ({\n id: step.screenId!,\n show: step.show,\n // `isComplete` defaults to `gate` — for most steps they're the same\n // predicate (e.g. intro: setupConfirmed unblocks bin.ts AND finishes\n // the screen). Only override when the two conditions diverge.\n isComplete: step.isComplete ?? step.gate,\n }));\n\n // Every program ends with the exit screen.\n entries.push({ id: 'exit', show: undefined, isComplete: undefined });\n\n return entries;\n}\n","/**\n * AI opt-in gate — step injection for programs whose agent run sends\n * source to Anthropic Claude.\n *\n * Injected after the `auth` step for every program that doesn't declare\n * `requiresAi: false`. The injected step carries three predicates:\n *\n * show — renders AiOptInRequiredScreen when the org hasn't\n * approved third-party AI\n * isComplete — router advances once approval lands\n * gate — `await store.getGate('ai-opt-in')` parks the agent\n * runner until approval lands. THIS is the enforcement:\n * the screen alone is cosmetic; the gate is what stops\n * source from leaving the machine.\n *\n * Used by BOTH screen-sequences.ts (screen injection) and the store's\n * _initFromProgram (gate registration) so the two layers can't drift.\n *\n * The predicates mirror Max's strict reading of\n * `organization.is_ai_data_processing_approved`: only literal `true`\n * proceeds; `null` / `undefined` / `false` all block. CI and signup\n * sessions skip the gate:\n * - `--ci` auto-consents to AI usage per the README, and the\n * interactive kill screen would be unworkable headless.\n * - signup (account provisioning) auto-consents too: the provisioning\n * access token deliberately omits the `organization:read` scope\n * (`WIZARD_PROVISIONING_SCOPES` in constants.ts), so the org's\n * approval can never be read back — `apiUser` stays null and the gate\n * could never clear. Creating an account through the wizard to run the\n * AI agent is itself the consent, mirroring how `shouldDisableAsk`\n * already treats `ci || signup` as one non-interactive mode.\n */\n\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { ProgramConfig, ProgramStep } from './program-step.js';\n\n/** Step id — also the ScreenId.AiOptIn enum value in screen-sequences. */\nexport const AI_OPT_IN_STEP_ID = 'ai-opt-in';\n\nfunction aiApproved(session: WizardSession): boolean {\n return !!session.apiUser?.organization?.is_ai_data_processing_approved;\n}\n\n/**\n * Returns the program's steps with the AI opt-in gate injected after\n * `auth`. Programs with `requiresAi: false` or no auth step pass\n * through unchanged — without auth, `apiUser` would never be populated\n * for evaluation anyway.\n */\nexport function withAiOptInGate(config: ProgramConfig): ProgramStep[] {\n if (config.requiresAi === false) return config.steps;\n\n const authIdx = config.steps.findIndex((s) => s.id === 'auth');\n if (authIdx === -1) return config.steps;\n\n const gateStep: ProgramStep = {\n id: AI_OPT_IN_STEP_ID,\n label: 'AI opt-in check',\n screenId: AI_OPT_IN_STEP_ID,\n // Only fire once apiUser has actually been populated — between\n // setCredentials and setApiUser there's a brief emitChange window\n // where apiUser is null, and we don't want to flash the gate then.\n show: (session) =>\n !session.ci &&\n !session.signup &&\n session.apiUser != null &&\n !aiApproved(session),\n isComplete: (session) =>\n session.ci || session.signup || aiApproved(session),\n gate: (session) => session.ci || session.signup || aiApproved(session),\n };\n\n return [\n ...config.steps.slice(0, authIdx + 1),\n gateStep,\n ...config.steps.slice(authIdx + 1),\n ];\n}\n","/**\n * Screen taxonomy + per-program screen sequences.\n *\n * Owns the ScreenId enum and projects each registered program's steps\n * into the router-shaped screen sequence (filtering headless steps and\n * appending the exit screen). Pure leaf module — no store, no React.\n */\n\nimport type { WizardSession } from '@lib/wizard-session';\nimport {\n PROGRAM_REGISTRY,\n type ProgramId,\n} from '@lib/programs/program-registry';\nimport { createProgramSequence } from '@lib/programs/program-step';\nimport { withAiOptInGate } from '@lib/programs/ai-opt-in-gate';\n\n/** Screens that participate in linear programs. */\nexport enum ScreenId {\n Intro = 'intro',\n RevenueIntro = 'revenue-intro',\n WarehouseIntro = 'warehouse-intro',\n SourceMapsIntro = 'source-maps-intro',\n SourceMapsDetect = 'source-maps-detect',\n SourceMapsOutro = 'source-maps-outro',\n MigrationIntro = 'migration-intro',\n AgentSkillIntro = 'agent-skill-intro',\n AiObservabilityIntro = 'ai-observability-intro',\n MetricsIntro = 'metrics-intro',\n SelfDrivingIntro = 'self-driving-intro',\n SelfDrivingIntegrationCheck = 'self-driving-integration-check',\n SelfDrivingIntegrationDetect = 'self-driving-integration-detect',\n SelfDrivingHandoff = 'self-driving-handoff',\n SelfDrivingGithub = 'self-driving-github',\n AuditIntro = 'audit-intro',\n AuditRun = 'audit-run',\n AuditOutro = 'audit-outro',\n HealthCheck = 'health-check',\n DoctorIntro = 'doctor-intro',\n DoctorReport = 'doctor-report',\n Setup = 'setup',\n Auth = 'auth',\n Run = 'run',\n Mcp = 'mcp',\n McpSuggestedPrompts = 'mcp-suggested-prompts',\n SlackConnect = 'slack-connect',\n KeepSkills = 'keep-skills',\n Outro = 'outro',\n Exit = 'exit',\n McpAdd = 'mcp-add',\n McpRemove = 'mcp-remove',\n AiOptIn = 'ai-opt-in',\n}\n\nexport interface Screen {\n /** ScreenId to show */\n id: ScreenId;\n /** If provided, screen is skipped when this returns false. Omit = always show. */\n show?: (session: WizardSession) => boolean;\n /** If provided, screen is considered complete when this returns true. */\n isComplete?: (session: WizardSession) => boolean;\n}\n\n/** An ordered list of screens — a program's screen journey. */\nexport type Sequence = Screen[];\n\n/** All program screen sequences keyed by program id. */\nexport const PROGRAM_SEQUENCES: Record<ProgramId, Sequence> =\n Object.fromEntries(\n PROGRAM_REGISTRY.map((c) => [\n c.id,\n createProgramSequence(withAiOptInGate(c)) as Sequence,\n ]),\n ) as Record<ProgramId, Sequence>;\n","/**\n * WizardRouter — declarative program pipelines + overlay stack.\n *\n * Two layers:\n * Program cursor — linear sequence of screens, advanced with next()\n * Overlay stack — interrupts (outage, auth-expired, etc.) that push/pop\n *\n * The visible screen is: top of overlay stack if non-empty, otherwise the program cursor.\n *\n * Adding a program screen = append to a sequence array.\n * Adding an overlay = call pushOverlay() from anywhere.\n * No switch statements, no hardcoded transitions in business logic.\n */\n\nimport { RunPhase, type WizardSession } from '@lib/wizard-session';\nimport { Program, type ProgramId } from '@lib/programs/program-registry';\nimport {\n PROGRAM_SEQUENCES,\n ScreenId,\n type Screen,\n type Sequence,\n} from './screen-sequences.js';\n\n// Re-export so existing imports from './router.js' keep working\nexport { ScreenId, Program };\nexport type { Screen, Sequence, ProgramId };\n\n// ── ScreenId name taxonomy ──────────────────────────────────────────────\n\n/** Screens that interrupt programs as overlays */\nexport enum Overlay {\n SettingsOverride = 'settings-override',\n ManagedSettings = 'managed-settings',\n PortConflict = 'port-conflict',\n ManualAuthCode = 'manual-auth-code',\n AuthError = 'auth-error',\n SessionTimeout = 'session-timeout',\n WizardAsk = 'wizard-ask',\n TaskNotice = 'task-notice',\n}\n\n/** Union of all screen names */\nexport type ScreenName = ScreenId | Overlay;\n\n// ── Router ────────────────────────────────────────────────────────────\n\nexport class WizardRouter {\n private sequence: Sequence;\n private programId: ProgramId;\n private overlays: Overlay[] = [];\n\n constructor(programId: ProgramId = Program.PostHogIntegration) {\n this.programId = programId;\n this.sequence = PROGRAM_SEQUENCES[programId];\n }\n\n /**\n * Resolve which screen should be active based on session state.\n * Walks the program sequence, skipping hidden entries and completed entries,\n * returns the first incomplete screen.\n */\n resolve(session: WizardSession): ScreenName {\n if (this.overlays.length > 0) {\n return this.overlays[this.overlays.length - 1];\n }\n\n for (const entry of this.sequence) {\n if (entry.show && !entry.show(session)) continue;\n if (entry.isComplete && entry.isComplete(session)) continue;\n // A failed login aborts the run: wizardAbort renders the error outro\n // and then waits for its dismissal. But the auth step only completes\n // on credentials — which an aborted login never set — so the walk\n // would park here forever: auth spinner up, outro unreachable, and\n // that wait deadlocked. Route to the outro so the error can be read\n // and dismissed. Auth only: the run steps already complete on\n // RunPhase.Error, so later aborts reach their program's own outro.\n if (\n entry.id === ScreenId.Auth &&\n session.runPhase === RunPhase.Error &&\n session.outroData\n ) {\n return ScreenId.Outro;\n }\n return entry.id;\n }\n\n // All entries complete — show the last screen (outro)\n return this.sequence[this.sequence.length - 1].id;\n }\n\n /** The screen that should be rendered right now. */\n get activeScreen(): ScreenName {\n // Overlays take priority — resolve() handles this too,\n // but activeScreen is called before session is available in some paths\n if (this.overlays.length > 0) {\n return this.overlays[this.overlays.length - 1];\n }\n return this.sequence[0].id;\n }\n\n /** The id of the active program. */\n get activeProgram(): ProgramId {\n return this.programId;\n }\n\n /** Whether an overlay is currently active. */\n get hasOverlay(): boolean {\n return this.overlays.length > 0;\n }\n\n /**\n * Push an overlay that interrupts the current program.\n * The program resumes when the overlay is dismissed via popOverlay().\n */\n pushOverlay(overlay: Overlay): void {\n this.overlays.push(overlay);\n }\n\n /**\n * Dismiss the topmost overlay. The program screen underneath resumes.\n */\n popOverlay(): void {\n this.overlays.pop();\n }\n\n /**\n * Direction hint for screen transitions.\n */\n private _lastDirection: 'push' | 'pop' | null = null;\n\n get lastNavDirection(): 'push' | 'pop' | null {\n return this._lastDirection;\n }\n\n /** @internal — called by store wrapper to track direction */\n _setDirection(dir: 'push' | 'pop' | null): void {\n this._lastDirection = dir;\n }\n}\n","/**\n * WizardStore — Nanostore-backed reactive store for the TUI.\n * React components subscribe via useSyncExternalStore.\n *\n * The active screen is derived from session state — WizardRouter walks\n * the flow and shows the first step whose `isComplete` is still false.\n *\n * Define a step `gate` if your screen needs to await user interactions.\n * bin.ts calls `await store.getGate(stepId)` to pause until the gate\n * predicate becomes true.\n *\n * All session mutations that affect screen resolution go through\n * explicit setters so emitChange() is always called.\n */\n\nimport { atom, map } from 'nanostores';\nimport { logToFile } from '@utils/debug';\nimport {\n TaskStatus,\n isTaskStatus,\n type AuthErrorDetail,\n type TokenUsageDelta,\n} from '@ui/wizard-ui';\nimport {\n type WizardSession,\n type OutroData,\n type DiscoveredFeature,\n type PendingQuestion,\n type AskAnswers,\n type CloudRegion,\n AdditionalFeature,\n McpOutcome,\n RunPhase,\n ScanConsent,\n buildSession,\n type TaskNotice,\n} from '@lib/wizard-session';\nimport type { SettingsConflict } from '@lib/agent/claude-settings';\nimport {\n WizardReadiness,\n getBlockingServiceKeys,\n type WizardReadinessResult,\n} from '@lib/health-checks/readiness';\nimport { ServiceHealthStatus } from '@lib/health-checks/types';\nimport {\n WizardRouter,\n type ScreenName,\n ScreenId,\n Overlay,\n Program,\n type ProgramId,\n} from './router.js';\nimport { analytics, sessionProperties } from '@utils/analytics';\nimport type {\n StoreInitContext,\n ProgramReadyContext,\n} from '@lib/programs/program-step';\nimport { getProgramConfig } from '@lib/programs/program-registry';\nimport { withAiOptInGate } from '@lib/programs/ai-opt-in-gate';\nimport { reportWarehouseSourcesDetected } from '@lib/programs/posthog-integration/detect';\nimport { EXPANDED_COUNT } from '@ui/tui/constants';\nimport { IS_DEV } from '@lib/constants';\nimport { computeTokenCostUsd } from '@lib/agent/token-pricing';\n\nexport { TaskStatus, ScreenId, Overlay, Program, RunPhase, McpOutcome };\nexport type { ScreenName, OutroData, WizardSession, ProgramId };\n\nexport interface TaskItem {\n label: string;\n activeForm?: string;\n status: TaskStatus;\n /** Legacy compat */\n done: boolean;\n}\n\nexport interface PlannedEvent {\n name: string;\n description: string;\n}\n\n/**\n * Running token/cost estimate for the hidden Ctrl+T HUD. Accumulated live\n * from each assistant turn's usage (see `agent-interface.ts`), then\n * reconciled to the SDK's authoritative `total_cost_usd` once the run\n * completes — `costIsFinal` flips so the HUD can show the number as exact\n * rather than a running estimate.\n */\nexport interface TokenUsageSnapshot {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n costUsd: number;\n costIsFinal: boolean;\n}\n\nconst EMPTY_TOKEN_USAGE: TokenUsageSnapshot = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheCreationTokens: 0,\n costUsd: 0,\n costIsFinal: false,\n};\n\n/** Total tokens across all counters in a `TokenUsageSnapshot` — used by\n * both `TokenCostHud` and `exit-line.ts` to detect \"no agent turns yet\". */\nexport function totalTokenCount(usage: TokenUsageSnapshot): number {\n return (\n usage.inputTokens +\n usage.outputTokens +\n usage.cacheReadTokens +\n usage.cacheCreationTokens\n );\n}\n\ninterface GateEntry {\n predicate: (session: WizardSession) => boolean;\n promise: Promise<void>;\n resolve: () => void;\n resolved: boolean;\n}\n\n/**\n * FIFO cap on retained status lines. The status bar is the only consumer and\n * renders at most EXPANDED_COUNT lines, so there is no reason to retain more —\n * the cap is tied to the window it feeds.\n */\nconst MAX_STATUS_MESSAGES = EXPANDED_COUNT;\n\n/**\n * Fired once per blocked readiness result, so we can quantify how often\n * the wizard refuses to start and — crucially — split that between\n * confirmed PostHog outages and probe-level reachability failures that\n * are most likely the user's network. Helps us decide whether the\n * health-check UX is over-firing.\n */\nfunction captureHealthCheckBlocked(result: WizardReadinessResult): void {\n try {\n const health = result.health;\n const blockingKeys = getBlockingServiceKeys(health);\n const blockingStatuses = blockingKeys.map((k) => health[k]?.status);\n\n const allNoConnection =\n blockingStatuses.length > 0 &&\n blockingStatuses.every((s) => s === ServiceHealthStatus.NoConnection);\n const onlySkillsOrigin =\n blockingKeys.length === 1 && blockingKeys[0] === 'skillsOrigin';\n\n const decision = onlySkillsOrigin\n ? 'skills-origin-down'\n : allNoConnection\n ? 'no-connection'\n : 'confirmed-outage';\n\n const posthogStatus = health.posthogOverall?.status;\n const retriesUsed = Math.max(\n 0,\n ...(['mcp', 'skillsOrigin'] as const).map((k) => {\n const ind = health[k]?.rawIndicator ?? '';\n const m = ind.match(/attempts=(\\d+)/);\n return m ? Number(m[1]) - 1 : 0;\n }),\n );\n\n analytics.wizardCapture('health check blocked', {\n decision,\n blocking_keys: blockingKeys,\n posthog_status_reachable:\n posthogStatus !== ServiceHealthStatus.NoConnection,\n posthog_status_reports_incident:\n posthogStatus === ServiceHealthStatus.Down ||\n posthogStatus === ServiceHealthStatus.Degraded,\n retries_used: retriesUsed,\n });\n } catch (err) {\n logToFile(\n `[health-checks] failed to capture analytics: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n}\n\nexport class WizardStore {\n // ── Internal nanostore atoms ─────────────────────────────────────\n private $session = map<WizardSession>(buildSession({}));\n private $statusMessages = atom<string[]>([]);\n private $statusExpanded = atom(false);\n private $tasks = atom<TaskItem[]>([]);\n private $eventPlan = atom<PlannedEvent[]>([]);\n private $handoffText = atom<string | null>(null);\n private $learnCardBlockIdx = atom(0);\n private $learnCardComplete = atom(false);\n private $version = atom(0);\n private $currentStage = atom<{ stage: string; startedAt: number } | null>(\n null,\n );\n private $tokenUsage = atom<TokenUsageSnapshot>(EMPTY_TOKEN_USAGE);\n // Defaults on for local/dev/test runs (tsx, `pnpm try`, vitest) so\n // contributors see it without needing to know the shortcut; defaults off\n // for the published build, where it stays genuinely hidden. Still\n // Ctrl+T-toggleable either way.\n private $tokenHudVisible = atom(IS_DEV);\n\n private _onTasksChanged: (() => void) | null = null;\n /** Last screen seen — used to detect screen transitions for analytics. */\n private _lastScreen: ScreenName | null = null;\n\n /** Hooks run when transitioning onto a screen. */\n private _enterScreenHooks = new Map<ScreenName, (() => void)[]>();\n\n /** Gate promises derived from program step definitions. */\n private _gates = new Map<string, GateEntry>();\n\n version = '';\n\n /** Navigation router — resolves active screen from session state. */\n readonly router: WizardRouter;\n\n /** Blocks agent execution until the settings-override overlay is dismissed. */\n private _resolveSettingsOverride: (() => void) | null = null;\n private _backupAndFixSettings: (() => boolean) | null = null;\n\n /** Blocks the run until an optional step's notice is answered. */\n private _resolveTaskNotice: ((keep: boolean) => void) | null = null;\n /** Blocks OAuth flow until the port-conflict overlay is dismissed. */\n private _resolvePortConflict: (() => void) | null = null;\n\n /** Resolves the OAuth flow with a manually-entered authorization code. */\n private _resolveManualAuthCode: ((code: string) => void) | null = null;\n\n /** Resolves the in-flight wizard_ask request. */\n private _resolvePendingQuestion: ((answers: AskAnswers) => void) | null =\n null;\n\n constructor(program: ProgramId = Program.PostHogIntegration) {\n this.router = new WizardRouter(program);\n this._initFromProgram(program);\n }\n\n /**\n * Scan program steps for gate predicates and create gate promises.\n *\n * Steps are wrapped with withAiOptInGate so the injected ai-opt-in\n * step's gate registers here — the agent runner awaits it (via\n * WizardUI.waitForAiOptIn) before any source leaves the machine.\n * Same wrapper screen-sequences.ts uses, so the gate and its screen\n * can't drift apart.\n */\n private _initFromProgram(program: ProgramId): void {\n const steps = withAiOptInGate(getProgramConfig(program));\n\n // Create gate promises from steps that define them\n for (const step of steps) {\n if (step.gate) {\n let resolve!: () => void;\n const promise = new Promise<void>((r) => {\n resolve = r;\n });\n this._gates.set(step.id, {\n predicate: step.gate,\n promise,\n resolve,\n resolved: false,\n });\n }\n }\n }\n\n /**\n * Run the program steps' onInit callbacks. startTUI calls this once\n * the screens are actually rendering — constructing a store alone\n * (tests, playground) must not fire init work like the health-check\n * pre-flight, whose probes belong only to flows that show its screen.\n */\n runInitHooks(): void {\n const steps = getProgramConfig(this.router.activeProgram).steps;\n const getSession = (): WizardSession => this.session;\n const ctx: StoreInitContext = {\n get session() {\n return getSession();\n },\n setReadinessResult: (r) => this.setReadinessResult(r),\n setFrameworkContext: (k, v) => this.setFrameworkContext(k, v),\n emitChange: () => this.emitChange(),\n };\n for (const step of steps) {\n step.onInit?.(ctx);\n }\n }\n\n /**\n * Run all `onReady` hooks declared by the current flow's steps, in\n * order. Must be called after `store.session = session` so hooks see\n * the real installDir. bin.ts calls this generically — it doesn't\n * need to know which program has which pre-flow work.\n */\n async runReadyHooks(): Promise<void> {\n const steps = getProgramConfig(this.router.activeProgram).steps;\n const ctx: ProgramReadyContext = {\n session: this.session,\n setFrameworkContext: (k, v) => this.setFrameworkContext(k, v),\n setFrameworkConfig: (i, c) => this.setFrameworkConfig(i, c),\n setDetectedFramework: (l) => this.setDetectedFramework(l),\n setSkillId: (id) => this.setSkillId(id),\n setUnsupportedVersion: (info) => this.setUnsupportedVersion(info),\n addDiscoveredFeature: (f) => this.addDiscoveredFeature(f),\n setDetectionComplete: () => this.setDetectionComplete(),\n };\n for (const step of steps) {\n if (step.onReady) {\n await step.onReady(ctx);\n }\n }\n }\n\n // ── Gate API ────────────────────────────────────────────────────\n\n /**\n * Get a gate promise by step ID — the primary blocking checkpoint API\n * for bin.ts. `await store.getGate('...')` parks the caller until the\n * corresponding program step's gate predicate flips to true (if the\n * predicate stays false, the caller stays parked indefinitely — the\n * TUI keeps rendering so the user can resolve whatever is blocking).\n *\n * If the program doesn't define a step with this ID, or the step\n * has no `gate` predicate, this returns an already-resolved promise\n * so bin.ts flows straight through. This lets programs opt in to\n * gates on a per-step basis without bin.ts needing to know which\n * gates exist in which flow.\n */\n getGate(stepId: string): Promise<void> {\n return this._gates.get(stepId)?.promise ?? Promise.resolve();\n }\n\n /**\n * Resolve once `predicate(session)` is true. Unlike a gate, this is created\n * at the await point and evaluated live against the current session, so it\n * never latches on a startup value — the orchestrator uses it to wait for a\n * decision (a project picked, a handoff acknowledged) without the \"true while\n * undecided\" trap that latched gate predicates have.\n */\n waitUntil(predicate: (session: WizardSession) => boolean): Promise<void> {\n if (predicate(this.session)) return Promise.resolve();\n return new Promise((resolve) => {\n const unsub = this.subscribe(() => {\n if (predicate(this.session)) {\n unsub();\n resolve();\n }\n });\n });\n }\n\n /**\n * Re-evaluate every gate predicate against the current session and\n * resolve any whose predicate now returns true. Called after every\n * emitChange(), so gates unblock as soon as the session mutation\n * that satisfies them lands. Gates only resolve once — a predicate\n * that goes true → false → true will NOT re-block a caller that\n * already awaited through.\n */\n private _checkGates(): void {\n for (const [, gate] of this._gates) {\n if (!gate.resolved && gate.predicate(this.session)) {\n gate.resolved = true;\n gate.resolve();\n }\n }\n }\n\n // ── State accessors (read from atoms) ────────────────────────────\n\n get session(): WizardSession {\n return this.$session.get();\n }\n\n set session(value: WizardSession) {\n this.$session.set(value);\n this.emitChange();\n }\n\n get statusMessages(): string[] {\n return this.$statusMessages.get();\n }\n\n get tasks(): TaskItem[] {\n return this.$tasks.get();\n }\n\n get eventPlan(): PlannedEvent[] {\n return this.$eventPlan.get();\n }\n\n get handoffText(): string | null {\n return this.$handoffText.get();\n }\n\n get currentStage(): { stage: string; startedAt: number } | null {\n return this.$currentStage.get();\n }\n\n /** No-op when the stage hasn't changed, so `startedAt` survives across\n * re-renders and tab switches and measures real stage time. */\n setCurrentStage(stage: string): void {\n const cur = this.$currentStage.get();\n if (cur?.stage === stage) return;\n this.$currentStage.set({ stage, startedAt: Date.now() });\n this.emitChange();\n }\n\n get statusExpanded(): boolean {\n return this.$statusExpanded.get();\n }\n\n toggleStatusExpanded(): void {\n this.$statusExpanded.set(!this.$statusExpanded.get());\n this.emitChange();\n }\n\n setStatusExpanded(expanded: boolean): void {\n if (this.$statusExpanded.get() !== expanded) {\n this.$statusExpanded.set(expanded);\n this.emitChange();\n }\n }\n\n // ── Session setters ─────────────────────────────────────────────\n // Every setter that affects screen resolution calls emitChange().\n // Business logic calls these instead of mutating session directly.\n\n /** Sets setupConfirmed, and is the point consent becomes final. */\n completeSetup(): void {\n this.$session.setKey('setupConfirmed', true);\n // Reports first: analytics merges tags into an event as it is sent, so\n // `setup confirmed` only carries the warehouse tags if they are already\n // set. On main they were, because reporting happened back in detect.\n this._markWarehouseSourcesReportedIfNeeded();\n analytics.wizardCapture('setup confirmed', sessionProperties(this.session));\n this.emitChange();\n }\n\n /**\n * Sharing is on: either the user turned it back on in the panel, or they\n * pressed Continue without ever touching it. Both are reversible until\n * completeSetup() resolves the intro gate and reports.\n */\n grantSharing(): void {\n this.$session.setKey('scanConsent', ScanConsent.Granted);\n this.emitChange();\n }\n\n /**\n * Sharing is off. Suppresses reporting only — local detection still ran and\n * the results stay in the session, so the outro suggestion and the warehouse\n * task are unaffected; see `scanConsent` on `WizardSession`.\n *\n * Deliberately does not report. The panel's toggle can come back here, so\n * marking the run reported would strand a user who turns sharing off and\n * then on again. completeSetup() owns the single report.\n */\n declineSharing(): void {\n this.$session.setKey('scanConsent', ScanConsent.Declined);\n this.emitChange();\n }\n\n /**\n * reportWarehouseSourcesDetected() is the single place scan results turn\n * into telemetry; this just supplies its idempotency flag via the normal\n * setter path (never mutate session directly). A no-op once\n * `warehouseSourcesReported` is set, or for any program that never\n * populated a warehouse-scan result in the first place.\n */\n private _markWarehouseSourcesReportedIfNeeded(): void {\n if (reportWarehouseSourcesDetected(this.session)) {\n this.$session.setKey('warehouseSourcesReported', true);\n }\n }\n\n setRunPhase(phase: RunPhase): void {\n this.$session.setKey('runPhase', phase);\n analytics.setTag('run_phase', phase);\n this.emitChange();\n }\n\n setCredentials(credentials: WizardSession['credentials']): void {\n this.$session.setKey('credentials', credentials);\n if (credentials?.projectId) {\n analytics.setTag('project_id', credentials.projectId);\n }\n analytics.wizardCapture('auth complete', {\n project_id: credentials?.projectId,\n });\n this.emitChange();\n }\n\n /** Post-refresh credential swap. No `auth complete` — see WizardUI. */\n setAccessToken(credentials: WizardSession['credentials']): void {\n this.$session.setKey('credentials', credentials);\n this.emitChange();\n }\n\n setRoleAtOrganization(role: string | null): void {\n this.$session.setKey('roleAtOrganization', role);\n this.emitChange();\n }\n\n setApiUser(user: WizardSession['apiUser']): void {\n this.$session.setKey('apiUser', user);\n this.emitChange();\n }\n\n setFrameworkConfig(\n integration: WizardSession['integration'],\n config: WizardSession['frameworkConfig'],\n ): void {\n this.$session.setKey('integration', integration);\n this.$session.setKey('frameworkConfig', config);\n this.$session.setKey('unsupportedVersion', null);\n if (integration) analytics.setTag('integration', integration);\n this.emitChange();\n }\n\n setDetectionComplete(): void {\n this.$session.setKey('detectionComplete', true);\n this.emitChange();\n }\n\n setDetectedFramework(label: string): void {\n this.$session.setKey('detectedFrameworkLabel', label);\n analytics.setTag('detected_framework', label);\n this.emitChange();\n }\n\n setSkillId(skillId: string | null): void {\n this.$session.setKey('skillId', skillId);\n this.emitChange();\n }\n\n setUnsupportedVersion(info: {\n current: string;\n minimum: string;\n docsUrl: string;\n }): void {\n this.$session.setKey('unsupportedVersion', info);\n this.emitChange();\n }\n\n setLoginUrl(url: string | null): void {\n this.$session.setKey('loginUrl', url);\n this.emitChange();\n }\n\n setAuthorizeUrl(url: string | null): void {\n this.$session.setKey('authorizeUrl', url);\n this.emitChange();\n }\n\n setReadinessResult(result: WizardReadinessResult | null): void {\n this.$session.setKey('readinessResult', result);\n if (result && result.decision === WizardReadiness.No) {\n captureHealthCheckBlocked(result);\n }\n this.emitChange();\n }\n\n /** User dismissed the blocking outage screen. Gate resolves via _checkGates(). */\n dismissOutage(): void {\n logToFile('[health-checks] user dismissed outage screen, continuing');\n this.$session.setKey('outageDismissed', true);\n this.emitChange();\n }\n\n /**\n * Push the settings-override overlay and return a promise that blocks\n * until the user dismisses it via backupAndFixSettingsOverride().\n */\n showSettingsOverride(\n conflicts: SettingsConflict[],\n backupAndFix: () => boolean,\n ): Promise<void> {\n const allKeys = conflicts.flatMap((c) => c.keys);\n this.$session.setKey('settingsOverrideKeys', allKeys);\n this.$session.setKey('settingsConflicts', conflicts);\n this._backupAndFixSettings = backupAndFix;\n\n const hasReadOnly = conflicts.some((c) => !c.writable);\n if (hasReadOnly) {\n this.pushOverlay(Overlay.ManagedSettings);\n } else {\n this.pushOverlay(Overlay.SettingsOverride);\n }\n\n return new Promise((resolve) => {\n this._resolveSettingsOverride = resolve;\n });\n }\n\n /**\n * Push the port-conflict overlay and return a promise that blocks\n * until the user frees the ports and retries, or exits.\n */\n showPortConflict(processInfo: {\n command: string;\n pid: string;\n port: number;\n user: string;\n }): Promise<void> {\n this.$session.setKey('portConflictProcess', processInfo);\n this.pushOverlay(Overlay.PortConflict);\n return new Promise((resolve) => {\n this._resolvePortConflict = resolve;\n });\n }\n\n /** Dismiss the port-conflict overlay and retry the OAuth port loop. */\n resolvePortConflict(): void {\n this.$session.setKey('portConflictProcess', null);\n this.popOverlay();\n this._resolvePortConflict?.();\n this._resolvePortConflict = null;\n }\n\n /**\n * Show an optional step's notice and return whether to keep that step.\n * Asked before the step runs, so nobody is surprised by a prompt mid-run.\n */\n showTaskNotice(notice: TaskNotice): Promise<boolean> {\n this.$session.setKey('taskNotice', notice);\n this.pushOverlay(Overlay.TaskNotice);\n return new Promise((resolve) => {\n this._resolveTaskNotice = resolve;\n });\n }\n\n /** Dismiss the notice, keeping (`true`) or skipping (`false`) the step. */\n resolveTaskNotice(keep: boolean): void {\n this.$session.setKey('taskNotice', null);\n this.popOverlay();\n this._resolveTaskNotice?.(keep);\n this._resolveTaskNotice = null;\n }\n\n /**\n * Return a promise that resolves when the user submits a manually-entered\n * OAuth code via the paste modal. The OAuth flow races this against the\n * local callback server — see `performOAuthFlow`.\n */\n waitForManualAuthCode(): Promise<string> {\n return new Promise<string>((resolve) => {\n this._resolveManualAuthCode = resolve;\n });\n }\n\n /** Open the manual OAuth code-entry overlay over the auth screen. */\n showManualAuthCode(): void {\n this.pushOverlay(Overlay.ManualAuthCode);\n }\n\n /** Dismiss the manual OAuth code overlay without submitting. */\n dismissManualAuthCode(): void {\n this.popOverlay();\n }\n\n /**\n * Submit a manually-entered authorization code: dismiss the overlay and\n * resolve the in-flight OAuth flow so it can exchange the code for a token.\n */\n submitManualAuthCode(code: string): void {\n this.popOverlay();\n this._resolveManualAuthCode?.(code);\n this._resolveManualAuthCode = null;\n }\n\n /**\n * Open the WizardAsk overlay with a set of questions and return a promise\n * that resolves once the user submits answers (or the request is cancelled).\n *\n * Only one request is in flight at a time — calling this while a request\n * is already pending throws.\n */\n requestQuestion(question: PendingQuestion): Promise<AskAnswers> {\n if (this._resolvePendingQuestion) {\n throw new Error(\n 'requestQuestion called while another wizard_ask request is pending',\n );\n }\n this.$session.setKey('pendingQuestion', question);\n this.pushOverlay(Overlay.WizardAsk);\n analytics.wizardCapture('wizard_ask shown', {\n source: question.source,\n question_count: question.questions.length,\n kinds: question.questions.map((q) => q.kind),\n });\n return new Promise<AskAnswers>((resolve) => {\n this._resolvePendingQuestion = resolve;\n });\n }\n\n /**\n * Resolve the in-flight wizard_ask request with the user's answers and\n * dismiss the overlay. Answers flow back to the agent as the tool result.\n */\n resolvePendingQuestion(answers: AskAnswers): void {\n const resolve = this._resolvePendingQuestion;\n this._resolvePendingQuestion = null;\n this.$session.setKey('pendingQuestion', null);\n this.popOverlay();\n resolve?.(answers);\n }\n\n /**\n * Cancel the in-flight wizard_ask request — the bridge sends a sentinel\n * answer (\"__cancelled__\") so the skill can decide how to handle it.\n */\n cancelPendingQuestion(): void {\n const pending = this.session.pendingQuestion;\n if (!pending) return;\n const cancelled: AskAnswers = {};\n for (const q of pending.questions) {\n cancelled[q.id] = '__cancelled__';\n }\n this.resolvePendingQuestion(cancelled);\n }\n\n /**\n * Back up .claude/settings.json. Dismisses the overlay on success.\n */\n backupAndFixSettingsOverride(): boolean {\n const ok = this._backupAndFixSettings?.() ?? false;\n if (ok) {\n this.$session.setKey('settingsOverrideKeys', null);\n this.$session.setKey('settingsConflicts', null);\n this.popOverlay();\n this._resolveSettingsOverride?.();\n this._resolveSettingsOverride = null;\n this._backupAndFixSettings = null;\n }\n return ok;\n }\n\n /** Push the auth-error overlay (no dismiss — user must exit). */\n showAuthError(detail?: AuthErrorDetail): void {\n this.$session.setKey('authErrorDetail', detail ?? null);\n this.pushOverlay(Overlay.AuthError);\n }\n\n /** Push the session-timeout overlay (no dismiss — user must exit). */\n showSessionTimeout(): void {\n this.pushOverlay(Overlay.SessionTimeout);\n }\n\n addDiscoveredFeature(feature: DiscoveredFeature): void {\n if (!this.session.discoveredFeatures.includes(feature)) {\n this.session.discoveredFeatures.push(feature);\n this.emitChange();\n }\n }\n\n /**\n * Enable an additional feature: enqueue it for the stop hook\n * and set any feature-specific session flags.\n */\n enableFeature(feature: AdditionalFeature): void {\n if (!this.session.additionalFeatureQueue.includes(feature)) {\n this.session.additionalFeatureQueue.push(feature);\n // Distinct key from `sessionProperties()`'s array-valued\n // `additional_features` — see the note in posthog-integration/detect.ts.\n analytics.setTag(\n 'additional_feature_kinds',\n this.session.additionalFeatureQueue.join(','),\n );\n }\n // Feature-specific flags\n if (feature === AdditionalFeature.LLM) {\n this.session.llmOptIn = true;\n }\n analytics.wizardCapture('feature enabled', { feature });\n this.emitChange();\n }\n\n setMcpComplete(\n outcome: McpOutcome = McpOutcome.Skipped,\n installedClients: string[] = [],\n featuresSelected?: 'all' | string[],\n loginCommands: string[] = [],\n ): void {\n this.$session.setKey('mcpComplete', true);\n this.$session.setKey('mcpOutcome', outcome);\n this.$session.setKey('mcpInstalledClients', installedClients);\n this.$session.setKey('mcpLoginCommands', loginCommands);\n const featuresPayload =\n outcome === McpOutcome.Installed && featuresSelected !== undefined\n ? { mcp_features_selected: featuresSelected }\n : {};\n analytics.wizardCapture('mcp complete', {\n mcp_outcome: outcome,\n mcp_installed_clients: installedClients,\n ...featuresPayload,\n ...sessionProperties(this.session),\n });\n this.emitChange();\n }\n\n setSkillsComplete(kept: boolean): void {\n this.$session.setKey('skillsComplete', true);\n analytics.wizardCapture('skills complete', {\n skills_kept: kept,\n ...sessionProperties(this.session),\n });\n this.emitChange();\n }\n\n setMcpSuggestedPromptsDismissed(): void {\n this.$session.setKey('mcpSuggestedPromptsDismissed', true);\n this.emitChange();\n }\n\n setSlackStepDismissed(): void {\n this.$session.setKey('slackStepDismissed', true);\n this.emitChange();\n }\n\n setSlackConnected(connected: boolean): void {\n this.$session.setKey('slackConnected', connected);\n this.emitChange();\n }\n\n setGithubConnected(connected: boolean): void {\n this.$session.setKey('githubConnected', connected);\n this.emitChange();\n }\n\n /**\n * Self-driving GitHub gate declined. Carries the outro the user lands on,\n * since declining ends the flow before the agent runs and there is no abort\n * case to render one.\n */\n declineGithub(outroData: OutroData): void {\n this.$session.setKey('githubDeclined', true);\n this.$session.setKey('outroData', outroData);\n this.emitChange();\n }\n\n /**\n * Self-driving integration-check answer. `true` → integrate the SDK as part\n * of this run; `false` → PostHog is already set up, go straight to\n * Self-driving. Resolves `session.integrate` from null.\n */\n setIntegrate(\n integrate: boolean,\n extra?: { via?: string; path?: string },\n ): void {\n this.$session.setKey('integrate', integrate);\n analytics.wizardCapture('self-driving integration check', {\n self_driving_integrate: integrate,\n ...(extra?.via ? { self_driving_integrate_via: extra.via } : {}),\n ...(extra?.path ? { self_driving_integrate_path: extra.path } : {}),\n ...sessionProperties(this.session),\n });\n this.emitChange();\n }\n\n /**\n * Self-driving \"no PostHog account\" branch of the integration check. The\n * project has no SDK, so we always integrate (`integrate = true`); and since\n * the user has no account, we flip `signup` and record the `email` / `region`\n * collected on the screen so `authenticate` → `getOrAskForProjectData` takes\n * the provisioning path (create account + email a login link) instead of\n * OAuth. The \"yes, I have an account\" branch uses `setIntegrate(true)` and\n * leaves `signup` false so auth runs the normal OAuth login.\n */\n chooseProvisionAccount(email: string, region: CloudRegion): void {\n this.$session.setKey('signup', true);\n this.$session.setKey('email', email);\n this.$session.setKey('region', region);\n this.$session.setKey('integrate', true);\n analytics.wizardCapture('self-driving integration check', {\n self_driving_integrate: true,\n self_driving_has_account: false,\n provision_region: region,\n ...sessionProperties(this.session),\n });\n this.emitChange();\n }\n\n /**\n * Self-driving handoff confirmed — the user acknowledged the post-integration\n * screen, so the Self-driving run can begin. Gate resolves via _checkGates().\n */\n confirmSelfDrivingHandoff(): void {\n this.$session.setKey('selfDrivingHandoffConfirmed', true);\n this.emitChange();\n }\n\n /**\n * Mark a composed run step complete (e.g. self-driving's `integrate-run`).\n * Records the step id so its `isComplete` predicate holds, clears the task\n * list, and resets run phase to Idle so the next run step starts fresh.\n */\n completeRunStep(stepId: string): void {\n const done = this.session.completedRuns;\n if (!done.includes(stepId)) {\n this.$session.setKey('completedRuns', [...done, stepId]);\n }\n this.$tasks.set([]);\n this.setRunPhase(RunPhase.Idle);\n }\n\n setOutroDismissed(): void {\n this.$session.setKey('outroDismissed', true);\n this.emitChange();\n }\n\n setOutroData(data: OutroData): void {\n this.$session.setKey('outroData', data);\n this.emitChange();\n }\n\n setDashboardUrl(url: string): void {\n logToFile(`store.setDashboardUrl: ${url}`);\n this.$session.setKey('dashboardUrl', url);\n this.emitChange();\n }\n\n setNotebookUrl(url: string): void {\n logToFile(`store.setNotebookUrl: ${url}`);\n this.$session.setKey('notebookUrl', url);\n this.emitChange();\n }\n\n setFrameworkContext(key: string, value: unknown): void {\n const ctx = { ...this.$session.get().frameworkContext, [key]: value };\n this.$session.setKey('frameworkContext', ctx);\n this.emitChange();\n }\n\n // ── Derived state ───────────────────────────────────────────────\n\n /**\n * The screen that should be rendered right now.\n * Derived from session state via the router.\n */\n get currentScreen(): ScreenName {\n return this.router.resolve(this.session);\n }\n\n /** Direction hint for screen transitions. */\n get lastNavDirection(): 'push' | 'pop' | null {\n return this.router.lastNavDirection;\n }\n\n // ── Change notification ─────────────────────────────────────────\n\n getVersion(): number {\n return this.$version.get();\n }\n\n /**\n * Notify React that state has changed.\n * The router re-resolves the active screen on next render.\n * Gate predicates are checked and resolved if ready.\n */\n emitChange(): void {\n this.router._setDirection('push');\n this.$version.set(this.$version.get() + 1);\n this._checkGates();\n this._detectTransition();\n }\n\n // ── Overlay navigation ──────────────────────────────────────────\n\n pushOverlay(overlay: Overlay): void {\n this.router._setDirection('push');\n this.router.pushOverlay(overlay);\n this.$version.set(this.$version.get() + 1);\n this._detectTransition();\n }\n\n popOverlay(): void {\n this.router._setDirection('pop');\n this.router.popOverlay();\n this.$version.set(this.$version.get() + 1);\n this._detectTransition();\n }\n\n // ── ScreenId transition analytics ─────────────────────────────────\n\n /**\n * Register a callback to run when transitioning onto the given screen.\n * Fires after every transition that lands on this screen.\n */\n onEnterScreen(screen: ScreenName, fn: () => void): void {\n const list = this._enterScreenHooks.get(screen) ?? [];\n list.push(fn);\n this._enterScreenHooks.set(screen, list);\n }\n\n /**\n * The program `screen` reports under — its step's `reportsAsProgramId` if it\n * claims one, else the running program (also the fallback for overlays and\n * screens with no owning step).\n */\n private _programIdForScreen(screen: ScreenName): ProgramId {\n const program = this.router.activeProgram;\n const step = getProgramConfig(program).steps.find(\n (s) => s.screenId === screen,\n );\n return step?.reportsAsProgramId ?? program;\n }\n\n /** The program the visible screen reports under; screens stamp this on their\n * own events rather than relying on the run-level `program_id` tag. */\n get analyticsProgramId(): ProgramId {\n return this._programIdForScreen(this.router.resolve(this.session));\n }\n\n /**\n * Detect screen transitions, run enter-screen hooks, and fire analytics.\n * Called at the end of emitChange/pushOverlay/popOverlay.\n */\n private _detectTransition(): void {\n const next = this.router.resolve(this.session);\n const prev = this._lastScreen;\n if (next !== prev) {\n // Every event carries the active TUI screen, filling the\n // \"URL / Screen\" column in PostHog.\n analytics.setTag('$screen_name', next);\n }\n if (prev !== null && next !== prev) {\n const hooks = this._enterScreenHooks.get(next);\n if (hooks) {\n for (const fn of hooks) fn();\n }\n analytics.wizardCapture(`screen ${next}`, {\n from_screen: prev,\n program_id: this._programIdForScreen(next),\n ...sessionProperties(this.session),\n });\n }\n this._lastScreen = next;\n }\n\n // ── Agent observation state ─────────────────────────────────────\n\n pushStatus(message: string): void {\n const msgs = this.$statusMessages.get();\n // Skip consecutive duplicate messages (no allocation on the hot path)\n if (msgs.length > 0 && msgs[msgs.length - 1] === message) return;\n // Nanostore detects change by reference equality, so a new array is\n // required. At the cap, allocate exactly once at the final size (dropping\n // the oldest entry) rather than push-then-truncate.\n const next =\n msgs.length >= MAX_STATUS_MESSAGES\n ? [...msgs.slice(msgs.length - MAX_STATUS_MESSAGES + 1), message]\n : [...msgs, message];\n this.$statusMessages.set(next);\n this.emitChange();\n }\n\n get tokenUsage(): TokenUsageSnapshot {\n return this.$tokenUsage.get();\n }\n\n get tokenHudVisible(): boolean {\n return this.$tokenHudVisible.get();\n }\n\n /** Hidden Ctrl+T shortcut — see ScreenContainer. Not registered as a\n * keyboard hint, so it never shows in the hints bar. */\n toggleTokenHud(): void {\n this.$tokenHudVisible.set(!this.$tokenHudVisible.get());\n this.emitChange();\n }\n\n /**\n * Accumulate one assistant turn's token usage into the running estimate.\n * Approximate by design (no dedup for SDK-retried/replayed turns, unlike\n * the benchmark middleware's TurnCounterPlugin) — it's a live indicator\n * for a hidden debug HUD, not a billing record, and `setFinalTokenCostUsd`\n * corrects the total once the run's authoritative cost is known.\n */\n addTokenUsage(delta: TokenUsageDelta): void {\n const cur = this.$tokenUsage.get();\n if (cur.costIsFinal) return;\n const deltaCostUsd = computeTokenCostUsd(delta);\n this.$tokenUsage.set({\n inputTokens: cur.inputTokens + delta.inputTokens,\n outputTokens: cur.outputTokens + delta.outputTokens,\n cacheReadTokens: cur.cacheReadTokens + delta.cacheReadTokens,\n cacheCreationTokens: cur.cacheCreationTokens + delta.cacheCreationTokens,\n costUsd: cur.costUsd + deltaCostUsd,\n costIsFinal: false,\n });\n this.emitChange();\n }\n\n /** Reconcile the running cost estimate to the SDK's authoritative total\n * once the agent run completes — same trick the benchmark's\n * CostTrackerPlugin.onFinalize uses to correct any per-turn drift. */\n setFinalTokenCostUsd(costUsd: number): void {\n const cur = this.$tokenUsage.get();\n this.$tokenUsage.set({ ...cur, costUsd, costIsFinal: true });\n this.emitChange();\n }\n\n setTasks(tasks: TaskItem[]): void {\n this.$tasks.set(tasks);\n this.emitChange();\n }\n\n updateTask(index: number, done: boolean): void {\n const tasks = this.$tasks.get();\n if (tasks[index]) {\n const updated = [...tasks];\n updated[index] = {\n ...updated[index],\n done,\n status: done ? TaskStatus.Completed : TaskStatus.Pending,\n };\n this.$tasks.set(updated);\n this.emitChange();\n }\n }\n\n setEventPlan(events: PlannedEvent[]): void {\n this.$eventPlan.set(events);\n this.emitChange();\n }\n\n /** No-op on identical text: an emit here means a network push downstream. */\n setHandoffText(text: string): void {\n if (this.$handoffText.get() === text) return;\n logToFile(`store.setHandoffText: ${text.length} chars`);\n this.$handoffText.set(text);\n this.emitChange();\n }\n\n get learnCardBlockIdx(): number {\n return this.$learnCardBlockIdx.get();\n }\n\n setLearnCardBlockIdx(idx: number): void {\n this.$learnCardBlockIdx.set(idx);\n }\n\n get learnCardComplete(): boolean {\n return this.$learnCardComplete.get();\n }\n\n setLearnCardComplete(): void {\n this.$learnCardComplete.set(true);\n this.emitChange();\n }\n\n syncTodos(\n todos: Array<{ content: string; status: string; activeForm?: string }>,\n ): void {\n const incoming = todos.map((t) => {\n const status = isTaskStatus(t.status) ? t.status : TaskStatus.Pending;\n return {\n label: t.content,\n activeForm: t.activeForm,\n status,\n done: status === TaskStatus.Completed,\n };\n });\n\n const incomingLabels = new Set(incoming.map((t) => t.label));\n\n const retained = this.$tasks\n .get()\n .filter((t) => t.done && !incomingLabels.has(t.label));\n\n this.$tasks.set([...retained, ...incoming]);\n this.emitChange();\n this._onTasksChanged?.();\n }\n\n /** Register a listener for task state changes (e.g. task stream push). */\n set onTasksChanged(fn: () => void) {\n this._onTasksChanged = fn;\n }\n\n // ── React integration ───────────────────────────────────────────\n\n subscribe(callback: () => void): () => void {\n return this.$version.listen(() => callback());\n }\n\n getSnapshot(): number {\n return this.$version.get();\n }\n}\n"],"mappings":"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"}
|