{"version":3,"file":"chat-progress.d.ts","sourceRoot":"","sources":["../../../src/workflows/chat-progress.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,eAAO,MAAM,4BAA4B,uCAAwC,CAAC;AAClF,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AACrF,MAAM,MAAM,gCAAgC,GAAG,OAAO,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;AAEzF,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,8BAA8B;IAC9C,IAAI,EAAE,gCAAgC,CAAC;IACvC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,gCAAgC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAiBD,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAa3F;AAUD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE9E;AAUD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,gCAAgC,GAAG;IACrF,UAAU,CAAC,EAAE,8BAA8B,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,CAyBA;AAED,MAAM,WAAW,uBAAuB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,6BAA6B,CAC5C,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,GAC9C,uBAAuB,EAAE,CA6B3B","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { Details } from \"../shared/types.ts\";\n\nexport const WORKFLOW_CHAT_PROGRESS_MODES = [\"auto\", \"off\", \"live-card\"] as const;\nexport type WorkflowChatProgressMode = (typeof WORKFLOW_CHAT_PROGRESS_MODES)[number];\nexport type ResolvedWorkflowChatProgressMode = Exclude<WorkflowChatProgressMode, \"auto\">;\n\nexport interface GitRepositoryIdentity {\n\troot: string;\n\tcommonDir: string;\n}\n\nexport interface WorkflowChatProgressProjection {\n\tmode: ResolvedWorkflowChatProgressMode;\n\trepoRelation: \"same\" | \"other\";\n\trepoLabel?: string;\n}\n\ninterface ResolveWorkflowChatProgressInput {\n\trequested: unknown;\n\tparentCwd: string;\n\tworkflowCwd: string;\n\tbackground: boolean;\n}\n\nfunction git(cwd: string, args: string[]): string | undefined {\n\tconst result = spawnSync(\"git\", [\"-C\", cwd, ...args], { encoding: \"utf-8\" });\n\tif (result.status !== 0) return undefined;\n\tconst output = result.stdout.trim();\n\treturn output || undefined;\n}\n\nfunction realPath(value: string): string {\n\ttry {\n\t\treturn fs.realpathSync.native(value);\n\t} catch {\n\t\treturn path.resolve(value);\n\t}\n}\n\nexport function resolveGitRepositoryIdentity(cwd: string): GitRepositoryIdentity | undefined {\n\tif (git(cwd, [\"rev-parse\", \"--is-inside-work-tree\"]) !== \"true\") return undefined;\n\tconst root = git(cwd, [\"rev-parse\", \"--show-toplevel\"]);\n\tconst commonDir = git(cwd, [\"rev-parse\", \"--git-common-dir\"]);\n\tif (!root || !commonDir) return undefined;\n\tconst commonDirPath = path.isAbsolute(commonDir)\n\t\t? commonDir\n\t\t: ([path.resolve(cwd, commonDir), path.resolve(root, commonDir)].find((candidate) => fs.existsSync(candidate)) ??\n\t\t\tpath.resolve(root, commonDir));\n\treturn {\n\t\troot: realPath(root),\n\t\tcommonDir: realPath(commonDirPath),\n\t};\n}\n\nfunction isSameGitRepositoryIdentity(\n\tleft: GitRepositoryIdentity | undefined,\n\tright: GitRepositoryIdentity | undefined,\n): boolean {\n\tif (!left || !right) return false;\n\treturn left.commonDir === right.commonDir || left.root === right.root;\n}\n\nexport function isSameGitRepository(leftCwd: string, rightCwd: string): boolean {\n\treturn isSameGitRepositoryIdentity(resolveGitRepositoryIdentity(leftCwd), resolveGitRepositoryIdentity(rightCwd));\n}\n\nfunction normalizeRequestedMode(value: unknown): { mode?: WorkflowChatProgressMode; error?: string } {\n\tif (value === undefined) return { mode: \"auto\" };\n\tif (typeof value !== \"string\" || !WORKFLOW_CHAT_PROGRESS_MODES.includes(value as WorkflowChatProgressMode)) {\n\t\treturn { error: `chatProgress must be one of: ${WORKFLOW_CHAT_PROGRESS_MODES.join(\", \")}.` };\n\t}\n\treturn { mode: value as WorkflowChatProgressMode };\n}\n\nexport function resolveWorkflowChatProgress(input: ResolveWorkflowChatProgressInput): {\n\tprojection?: WorkflowChatProgressProjection;\n\terror?: string;\n} {\n\tconst requested = normalizeRequestedMode(input.requested);\n\tif (requested.error) return { error: requested.error };\n\tconst parentIdentity = resolveGitRepositoryIdentity(input.parentCwd);\n\tconst workflowIdentity = resolveGitRepositoryIdentity(input.workflowCwd);\n\tconst sameRepo = !!(\n\t\tparentIdentity &&\n\t\tworkflowIdentity &&\n\t\t(parentIdentity.commonDir === workflowIdentity.commonDir || parentIdentity.root === workflowIdentity.root)\n\t);\n\tconst repoLabel = workflowIdentity ? path.basename(workflowIdentity.root) : undefined;\n\tconst repoRelation = sameRepo ? \"same\" : \"other\";\n\n\tconst requestedMode = requested.mode ?? \"auto\";\n\tlet mode: ResolvedWorkflowChatProgressMode;\n\tif (requestedMode === \"auto\") mode = sameRepo && !input.background ? \"live-card\" : \"off\";\n\telse mode = requestedMode;\n\n\tif (mode === \"live-card\" && !sameRepo)\n\t\treturn {\n\t\t\terror: \"chatProgress: 'live-card' is only available for workflowScript runs in the same Git repository.\",\n\t\t};\n\tif (mode === \"live-card\" && input.background)\n\t\treturn { error: \"chatProgress: 'live-card' requires a watched foreground workflow; pass async:false.\" };\n\treturn { projection: { mode, repoRelation, ...(repoLabel ? { repoLabel } : {}) } };\n}\n\nexport interface WorkflowChatProgressRow {\n\tkey: string;\n\tstate: \"running\" | \"complete\" | \"failed\";\n\tlabel?: string;\n\tphase?: string;\n\trunId?: string;\n\tdurationMs?: number;\n\terror?: string;\n}\n\nfunction cleanLabel(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nexport function buildWorkflowChatProgressRows(\n\ttrace: NonNullable<Details[\"workflow\"]>[\"trace\"],\n): WorkflowChatProgressRow[] {\n\tconst rows = new Map<string, WorkflowChatProgressRow>();\n\tfor (const entry of trace) {\n\t\tif (entry.operation !== \"run\") continue;\n\t\tconst existing = rows.get(entry.key);\n\t\tif (entry.state === \"reused\") {\n\t\t\tif (existing) {\n\t\t\t\tconst label = cleanLabel(entry.label);\n\t\t\t\tconst phase = cleanLabel(entry.phase);\n\t\t\t\tif (label) existing.label = label;\n\t\t\t\tif (phase) existing.phase = phase;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst next: WorkflowChatProgressRow = existing ?? { key: entry.key, state: \"running\" };\n\t\tnext.state = entry.state === \"completed\" ? \"complete\" : entry.state === \"failed\" ? \"failed\" : \"running\";\n\t\tconst label = cleanLabel(entry.label);\n\t\tconst phase = cleanLabel(entry.phase);\n\t\tif (label) next.label = label;\n\t\tif (phase) next.phase = phase;\n\t\tif (entry.runId === undefined) delete next.runId;\n\t\telse next.runId = entry.runId;\n\t\tif (entry.durationMs === undefined) delete next.durationMs;\n\t\telse next.durationMs = entry.durationMs;\n\t\tif (entry.error === undefined) delete next.error;\n\t\telse next.error = entry.error;\n\t\trows.set(entry.key, next);\n\t}\n\treturn [...rows.values()];\n}\n"]}