@agimon-ai/doompi-workflow 0.0.1-alpha.13 → 0.0.1-alpha.14
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 +6 -0
- package/dist/adapters/pi/workflow/piExtension.cjs +8 -8
- package/dist/adapters/pi/workflow/piExtension.cjs.map +1 -1
- package/dist/adapters/pi/workflow/piExtension.mjs +8 -8
- package/dist/adapters/pi/workflow/piExtension.mjs.map +1 -1
- package/dist/adapters/pi/workflow/piToolBridge.cjs +1 -1
- package/dist/adapters/pi/workflow/piToolBridge.cjs.map +1 -1
- package/dist/adapters/pi/workflow/piToolBridge.mjs +1 -1
- package/dist/adapters/pi/workflow/piToolBridge.mjs.map +1 -1
- package/dist/adapters/pi/workflow/piTools.cjs +3 -2
- package/dist/adapters/pi/workflow/piTools.cjs.map +1 -1
- package/dist/adapters/pi/workflow/piTools.mjs +3 -2
- package/dist/adapters/pi/workflow/piTools.mjs.map +1 -1
- package/dist/services/workflow/workflowNarration.cjs +2 -0
- package/dist/services/workflow/workflowNarration.cjs.map +1 -0
- package/dist/services/workflow/workflowNarration.mjs +2 -0
- package/dist/services/workflow/workflowNarration.mjs.map +1 -0
- package/dist/tui/workflow/workflowInspector.cjs +1 -1
- package/dist/tui/workflow/workflowInspector.cjs.map +1 -1
- package/dist/tui/workflow/workflowInspector.mjs +1 -1
- package/dist/tui/workflow/workflowInspector.mjs.map +1 -1
- package/dist/tui/workflow/workflowProgressOverlay.cjs +2 -0
- package/dist/tui/workflow/workflowProgressOverlay.cjs.map +1 -0
- package/dist/tui/workflow/workflowProgressOverlay.mjs +2 -0
- package/dist/tui/workflow/workflowProgressOverlay.mjs.map +1 -0
- package/dist/tui/workflow/workflowStatusRow.cjs +1 -1
- package/dist/tui/workflow/workflowStatusRow.cjs.map +1 -1
- package/dist/tui/workflow/workflowStatusRow.mjs +1 -1
- package/dist/tui/workflow/workflowStatusRow.mjs.map +1 -1
- package/dist/tui/workflow/workflowStepMessage.cjs +1 -1
- package/dist/tui/workflow/workflowStepMessage.cjs.map +1 -1
- package/dist/tui/workflow/workflowStepMessage.mjs +1 -1
- package/dist/tui/workflow/workflowStepMessage.mjs.map +1 -1
- package/dist/tui/workflow/workflowToolRender.cjs +7 -0
- package/dist/tui/workflow/workflowToolRender.cjs.map +1 -0
- package/dist/tui/workflow/workflowToolRender.mjs +7 -0
- package/dist/tui/workflow/workflowToolRender.mjs.map +1 -0
- package/package.json +6 -6
- package/dist/tui/workflow/workflowFooterStatus.cjs +0 -2
- package/dist/tui/workflow/workflowFooterStatus.cjs.map +0 -1
- package/dist/tui/workflow/workflowFooterStatus.mjs +0 -2
- package/dist/tui/workflow/workflowFooterStatus.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStatusRow.cjs","names":[],"sources":["../../../src/tui/workflow/workflowStatusRow.ts"],"sourcesContent":["/**\n * Session reporting for a run's progress log.\n *\n * DESIGN PATTERNS:\n * - Pure derivation. Takes a run's raw progress events and returns report lines,\n * so the behaviour is unit-testable without a registry or a live run.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n *
|
|
1
|
+
{"version":3,"file":"workflowStatusRow.cjs","names":[],"sources":["../../../src/tui/workflow/workflowStatusRow.ts"],"sourcesContent":["/**\n * Session reporting for a run's progress log.\n *\n * DESIGN PATTERNS:\n * - Pure derivation. Takes a run's raw progress events and returns report lines,\n * so the behaviour is unit-testable without a registry or a live run.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n * Live progress now renders through `workflowProgressOverlay.ts`. This module\n * remains the compatibility derivation used by terminal summaries and legacy\n * workflow-step cards already persisted in older sessions.\n */\n\nimport type { WorkflowProgressEvent } from '@agimon-ai/workflow-mcp';\n\n/** Step names come from workflow YAML and can be sentences. */\nconst MAX_STEP_LABEL = 48;\n\n/** One session line, plus the key that stops it being reported twice. */\nexport interface StepReport {\n duration?: string;\n job: string;\n key: string;\n status: 'FAILED' | 'FINISHED' | 'STARTED';\n step: string;\n}\n\nfunction truncateStep(step: string): string {\n return step.length <= MAX_STEP_LABEL ? step : `${step.slice(0, MAX_STEP_LABEL - 1)}…`;\n}\n\n/**\n * Human-scale duration.\n *\n * Rounded hard on purpose: this is read at a glance next to a step name, where\n * milliseconds are noise and \"4m12s\" answers the only question being asked.\n */\nexport function humanizeDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return '?';\n if (ms < 1000) return `${ms}ms`;\n const seconds = Math.round(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m${String(seconds % 60).padStart(2, '0')}s`;\n return `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, '0')}m`;\n}\n\n/**\n * Turn a run's raw progress log into session report lines.\n *\n * Derives durations by pairing each step's start with its end rather than\n * recording a duration in the log, so nothing about the log format has to\n * change and old runs read back the same way.\n *\n * Keyed by the event's own timestamp so a caller polling the same append-only\n * file repeatedly reports each transition exactly once, however often it polls.\n */\nexport function stepReportLines(runKey: string, events: WorkflowProgressEvent[]): StepReport[] {\n const startedAt = new Map<string, string>();\n const reports: StepReport[] = [];\n\n for (const event of events) {\n if (event.type !== 'step' || !event.step) continue;\n const id = `${runKey}/${event.job}`;\n const stepKey = `${id}/${event.step}`;\n const label = truncateStep(event.step);\n\n if (event.status === 'running') {\n startedAt.set(stepKey, event.at);\n reports.push({ job: event.job, key: `started:${stepKey}:${event.at}`, status: 'STARTED', step: label });\n continue;\n }\n\n const began = startedAt.get(stepKey);\n const duration = began ? humanizeDuration(Date.parse(event.at) - Date.parse(began)) : '?';\n // A failed step did finish, but calling it FINISHED buries the one outcome\n // the reader needs to see. Same shape, honest verb.\n const status = event.status === 'failed' ? 'FAILED' : 'FINISHED';\n reports.push({ duration, job: event.job, key: `${status}:${stepKey}:${event.at}`, status, step: label });\n }\n\n return reports;\n}\n"],"mappings":"AAwCA,SAAgB,EAAiB,EAAoB,CACnD,GAAI,CAAC,OAAO,SAAS,CAAE,GAAK,EAAK,EAAG,MAAO,IAC3C,GAAI,EAAK,IAAM,MAAO,GAAG,EAAG,IAC5B,IAAM,EAAU,KAAK,MAAM,EAAK,GAAI,EACpC,GAAI,EAAU,GAAI,MAAO,GAAG,EAAQ,GACpC,IAAM,EAAU,KAAK,MAAM,EAAU,EAAE,EAEvC,OADI,EAAU,GAAW,GAAG,EAAQ,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GACtE,GAAG,KAAK,MAAM,EAAU,EAAE,EAAE,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,EAC9E"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){
|
|
1
|
+
function e(e){if(!Number.isFinite(e)||e<0)return`?`;if(e<1e3)return`${e}ms`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);return n<60?`${n}m${String(t%60).padStart(2,`0`)}s`:`${Math.floor(n/60)}h${String(n%60).padStart(2,`0`)}m`}export{e as humanizeDuration};
|
|
2
2
|
//# sourceMappingURL=workflowStatusRow.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStatusRow.mjs","names":[],"sources":["../../../src/tui/workflow/workflowStatusRow.ts"],"sourcesContent":["/**\n * Session reporting for a run's progress log.\n *\n * DESIGN PATTERNS:\n * - Pure derivation. Takes a run's raw progress events and returns report lines,\n * so the behaviour is unit-testable without a registry or a live run.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n *
|
|
1
|
+
{"version":3,"file":"workflowStatusRow.mjs","names":[],"sources":["../../../src/tui/workflow/workflowStatusRow.ts"],"sourcesContent":["/**\n * Session reporting for a run's progress log.\n *\n * DESIGN PATTERNS:\n * - Pure derivation. Takes a run's raw progress events and returns report lines,\n * so the behaviour is unit-testable without a registry or a live run.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n * Live progress now renders through `workflowProgressOverlay.ts`. This module\n * remains the compatibility derivation used by terminal summaries and legacy\n * workflow-step cards already persisted in older sessions.\n */\n\nimport type { WorkflowProgressEvent } from '@agimon-ai/workflow-mcp';\n\n/** Step names come from workflow YAML and can be sentences. */\nconst MAX_STEP_LABEL = 48;\n\n/** One session line, plus the key that stops it being reported twice. */\nexport interface StepReport {\n duration?: string;\n job: string;\n key: string;\n status: 'FAILED' | 'FINISHED' | 'STARTED';\n step: string;\n}\n\nfunction truncateStep(step: string): string {\n return step.length <= MAX_STEP_LABEL ? step : `${step.slice(0, MAX_STEP_LABEL - 1)}…`;\n}\n\n/**\n * Human-scale duration.\n *\n * Rounded hard on purpose: this is read at a glance next to a step name, where\n * milliseconds are noise and \"4m12s\" answers the only question being asked.\n */\nexport function humanizeDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return '?';\n if (ms < 1000) return `${ms}ms`;\n const seconds = Math.round(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m${String(seconds % 60).padStart(2, '0')}s`;\n return `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, '0')}m`;\n}\n\n/**\n * Turn a run's raw progress log into session report lines.\n *\n * Derives durations by pairing each step's start with its end rather than\n * recording a duration in the log, so nothing about the log format has to\n * change and old runs read back the same way.\n *\n * Keyed by the event's own timestamp so a caller polling the same append-only\n * file repeatedly reports each transition exactly once, however often it polls.\n */\nexport function stepReportLines(runKey: string, events: WorkflowProgressEvent[]): StepReport[] {\n const startedAt = new Map<string, string>();\n const reports: StepReport[] = [];\n\n for (const event of events) {\n if (event.type !== 'step' || !event.step) continue;\n const id = `${runKey}/${event.job}`;\n const stepKey = `${id}/${event.step}`;\n const label = truncateStep(event.step);\n\n if (event.status === 'running') {\n startedAt.set(stepKey, event.at);\n reports.push({ job: event.job, key: `started:${stepKey}:${event.at}`, status: 'STARTED', step: label });\n continue;\n }\n\n const began = startedAt.get(stepKey);\n const duration = began ? humanizeDuration(Date.parse(event.at) - Date.parse(began)) : '?';\n // A failed step did finish, but calling it FINISHED buries the one outcome\n // the reader needs to see. Same shape, honest verb.\n const status = event.status === 'failed' ? 'FAILED' : 'FINISHED';\n reports.push({ duration, job: event.job, key: `${status}:${stepKey}:${event.at}`, status, step: label });\n }\n\n return reports;\n}\n"],"mappings":"AAwCA,SAAgB,EAAiB,EAAoB,CACnD,GAAI,CAAC,OAAO,SAAS,CAAE,GAAK,EAAK,EAAG,MAAO,IAC3C,GAAI,EAAK,IAAM,MAAO,GAAG,EAAG,IAC5B,IAAM,EAAU,KAAK,MAAM,EAAK,GAAI,EACpC,GAAI,EAAU,GAAI,MAAO,GAAG,EAAQ,GACpC,IAAM,EAAU,KAAK,MAAM,EAAU,EAAE,EAEvC,OADI,EAAU,GAAW,GAAG,EAAQ,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GACtE,GAAG,KAAK,MAAM,EAAU,EAAE,EAAE,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,EAC9E"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require("@earendil-works/pi-tui");function t(e
|
|
1
|
+
let e=require("@earendil-works/pi-tui");function t(e){if(!e||typeof e!=`object`||Array.isArray(e))return!1;let t=e;return typeof t.displayName==`string`&&typeof t.job==`string`&&typeof t.step==`string`&&(t.status===`STARTED`||t.status===`FINISHED`||t.status===`FAILED`)&&(t.duration===void 0||typeof t.duration==`string`)}var n=class{details;theme;constructor(e,t){this.details=e,this.theme=t}render(t){let n=`${this.theme.fg(`accent`,`[${this.details.displayName}]:`)} ${this.details.job}`,r=this.details.duration?`${this.details.status}::${this.details.duration}::${this.details.step}`:`${this.details.status}::${this.details.step}`,i=this.details.status===`FAILED`?`error`:this.details.status===`FINISHED`?`success`:`accent`;return[(0,e.truncateToWidth)(n,t),(0,e.truncateToWidth)(this.theme.fg(i,r),t)]}invalidate(){}};function r(t,r,i,a){let o=new e.Box(i,1,e=>a.bg(`customMessageBg`,e));return o.addChild(t?new n(t,a):new e.Text(r,0,0)),o}exports.isWorkflowStepMessageDetails=t,exports.renderWorkflowStepMessage=r;
|
|
2
2
|
//# sourceMappingURL=workflowStepMessage.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStepMessage.cjs","names":["truncateToWidth","Box","Text"],"sources":["../../../src/tui/workflow/workflowStepMessage.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { Box, type Component, Text, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { StepReport } from './workflowStatusRow';\n\nexport interface WorkflowStepMessageDetails {\n displayName: string;\n duration?: string;\n job: string;\n status: StepReport['status'];\n step: string;\n}\n\nexport function workflowStepMessageDetails(displayName: string, report: StepReport): WorkflowStepMessageDetails {\n return {\n displayName,\n job: report.job,\n status: report.status,\n step: report.step,\n ...(report.duration ? { duration: report.duration } : {}),\n };\n}\n\nexport function formatWorkflowStepMessage(details: WorkflowStepMessageDetails): string {\n const transition = details.duration\n ? `${details.status}::${details.duration}::${details.step}`\n : `${details.status}::${details.step}`;\n return `[${details.displayName}]: ${details.job}\\n${transition}`;\n}\n\nexport function isWorkflowStepMessageDetails(value: unknown): value is WorkflowStepMessageDetails {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<WorkflowStepMessageDetails>;\n return (\n typeof candidate.displayName === 'string' &&\n typeof candidate.job === 'string' &&\n typeof candidate.step === 'string' &&\n (candidate.status === 'STARTED' || candidate.status === 'FINISHED' || candidate.status === 'FAILED') &&\n (candidate.duration === undefined || typeof candidate.duration === 'string')\n );\n}\n\nclass WorkflowStepMessageLines implements Component {\n constructor(\n private readonly details: WorkflowStepMessageDetails,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n const header = `${this.theme.fg('accent', `[${this.details.displayName}]:`)} ${this.details.job}`;\n const transition = this.details.duration\n ? `${this.details.status}::${this.details.duration}::${this.details.step}`\n : `${this.details.status}::${this.details.step}`;\n const colour =\n this.details.status === 'FAILED' ? 'error' : this.details.status === 'FINISHED' ? 'success' : 'accent';\n return [truncateToWidth(header, width), truncateToWidth(this.theme.fg(colour, transition), width)];\n }\n\n invalidate(): void {}\n}\n\nexport function renderWorkflowStepMessage(\n details: WorkflowStepMessageDetails | undefined,\n fallback: string,\n outputPad: number,\n theme: Theme,\n): Component {\n const box = new Box(outputPad, 1, (text) => theme.bg('customMessageBg', text));\n box.addChild(details ? new WorkflowStepMessageLines(details, theme) : new Text(fallback, 0, 0));\n return box;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflowStepMessage.cjs","names":["truncateToWidth","Box","Text"],"sources":["../../../src/tui/workflow/workflowStepMessage.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { Box, type Component, Text, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { StepReport } from './workflowStatusRow';\n\nexport interface WorkflowStepMessageDetails {\n displayName: string;\n duration?: string;\n job: string;\n status: StepReport['status'];\n step: string;\n}\n\nexport function workflowStepMessageDetails(displayName: string, report: StepReport): WorkflowStepMessageDetails {\n return {\n displayName,\n job: report.job,\n status: report.status,\n step: report.step,\n ...(report.duration ? { duration: report.duration } : {}),\n };\n}\n\nexport function formatWorkflowStepMessage(details: WorkflowStepMessageDetails): string {\n const transition = details.duration\n ? `${details.status}::${details.duration}::${details.step}`\n : `${details.status}::${details.step}`;\n return `[${details.displayName}]: ${details.job}\\n${transition}`;\n}\n\nexport function isWorkflowStepMessageDetails(value: unknown): value is WorkflowStepMessageDetails {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<WorkflowStepMessageDetails>;\n return (\n typeof candidate.displayName === 'string' &&\n typeof candidate.job === 'string' &&\n typeof candidate.step === 'string' &&\n (candidate.status === 'STARTED' || candidate.status === 'FINISHED' || candidate.status === 'FAILED') &&\n (candidate.duration === undefined || typeof candidate.duration === 'string')\n );\n}\n\nclass WorkflowStepMessageLines implements Component {\n constructor(\n private readonly details: WorkflowStepMessageDetails,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n const header = `${this.theme.fg('accent', `[${this.details.displayName}]:`)} ${this.details.job}`;\n const transition = this.details.duration\n ? `${this.details.status}::${this.details.duration}::${this.details.step}`\n : `${this.details.status}::${this.details.step}`;\n const colour =\n this.details.status === 'FAILED' ? 'error' : this.details.status === 'FINISHED' ? 'success' : 'accent';\n return [truncateToWidth(header, width), truncateToWidth(this.theme.fg(colour, transition), width)];\n }\n\n invalidate(): void {}\n}\n\nexport function renderWorkflowStepMessage(\n details: WorkflowStepMessageDetails | undefined,\n fallback: string,\n outputPad: number,\n theme: Theme,\n): Component {\n const box = new Box(outputPad, 1, (text) => theme.bg('customMessageBg', text));\n box.addChild(details ? new WorkflowStepMessageLines(details, theme) : new Text(fallback, 0, 0));\n return box;\n}\n"],"mappings":"wCA6BA,SAAgB,EAA6B,EAAqD,CAChG,GAAI,CAAC,GAAS,OAAO,GAAU,UAAY,MAAM,QAAQ,CAAK,EAAG,MAAO,GACxE,IAAM,EAAY,EAClB,OACE,OAAO,EAAU,aAAgB,UACjC,OAAO,EAAU,KAAQ,UACzB,OAAO,EAAU,MAAS,WACzB,EAAU,SAAW,WAAa,EAAU,SAAW,YAAc,EAAU,SAAW,YAC1F,EAAU,WAAa,IAAA,IAAa,OAAO,EAAU,UAAa,SAEvE,CAEA,IAAM,EAAN,KAAoD,CAE/B,QACA,MAFnB,YACE,EACA,EACA,CAFiB,KAAA,QAAA,EACA,KAAA,MAAA,CAChB,CAEH,OAAO,EAAyB,CAC9B,IAAM,EAAS,GAAG,KAAK,MAAM,GAAG,SAAU,IAAI,KAAK,QAAQ,YAAY,GAAG,EAAE,GAAG,KAAK,QAAQ,MACtF,EAAa,KAAK,QAAQ,SAC5B,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,SAAS,IAAI,KAAK,QAAQ,OAClE,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,OACtC,EACJ,KAAK,QAAQ,SAAW,SAAW,QAAU,KAAK,QAAQ,SAAW,WAAa,UAAY,SAChG,MAAO,EAAA,EAACA,EAAAA,gBAAAA,CAAgB,EAAQ,CAAK,GAAA,EAAGA,EAAAA,gBAAAA,CAAgB,KAAK,MAAM,GAAG,EAAQ,CAAU,EAAG,CAAK,CAAC,CACnG,CAEA,YAAmB,CAAC,CACtB,EAEA,SAAgB,EACd,EACA,EACA,EACA,EACW,CACX,IAAM,EAAM,IAAIC,EAAAA,IAAI,EAAW,EAAI,GAAS,EAAM,GAAG,kBAAmB,CAAI,CAAC,EAE7E,OADA,EAAI,SAAS,EAAU,IAAI,EAAyB,EAAS,CAAK,EAAI,IAAIC,EAAAA,KAAK,EAAU,EAAG,CAAC,CAAC,EACvF,CACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Box as e,Text as t,truncateToWidth as n}from"@earendil-works/pi-tui";function r(e
|
|
1
|
+
import{Box as e,Text as t,truncateToWidth as n}from"@earendil-works/pi-tui";function r(e){if(!e||typeof e!=`object`||Array.isArray(e))return!1;let t=e;return typeof t.displayName==`string`&&typeof t.job==`string`&&typeof t.step==`string`&&(t.status===`STARTED`||t.status===`FINISHED`||t.status===`FAILED`)&&(t.duration===void 0||typeof t.duration==`string`)}var i=class{details;theme;constructor(e,t){this.details=e,this.theme=t}render(e){let t=`${this.theme.fg(`accent`,`[${this.details.displayName}]:`)} ${this.details.job}`,r=this.details.duration?`${this.details.status}::${this.details.duration}::${this.details.step}`:`${this.details.status}::${this.details.step}`,i=this.details.status===`FAILED`?`error`:this.details.status===`FINISHED`?`success`:`accent`;return[n(t,e),n(this.theme.fg(i,r),e)]}invalidate(){}};function a(n,r,a,o){let s=new e(a,1,e=>o.bg(`customMessageBg`,e));return s.addChild(n?new i(n,o):new t(r,0,0)),s}export{r as isWorkflowStepMessageDetails,a as renderWorkflowStepMessage};
|
|
2
2
|
//# sourceMappingURL=workflowStepMessage.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStepMessage.mjs","names":[],"sources":["../../../src/tui/workflow/workflowStepMessage.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { Box, type Component, Text, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { StepReport } from './workflowStatusRow';\n\nexport interface WorkflowStepMessageDetails {\n displayName: string;\n duration?: string;\n job: string;\n status: StepReport['status'];\n step: string;\n}\n\nexport function workflowStepMessageDetails(displayName: string, report: StepReport): WorkflowStepMessageDetails {\n return {\n displayName,\n job: report.job,\n status: report.status,\n step: report.step,\n ...(report.duration ? { duration: report.duration } : {}),\n };\n}\n\nexport function formatWorkflowStepMessage(details: WorkflowStepMessageDetails): string {\n const transition = details.duration\n ? `${details.status}::${details.duration}::${details.step}`\n : `${details.status}::${details.step}`;\n return `[${details.displayName}]: ${details.job}\\n${transition}`;\n}\n\nexport function isWorkflowStepMessageDetails(value: unknown): value is WorkflowStepMessageDetails {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<WorkflowStepMessageDetails>;\n return (\n typeof candidate.displayName === 'string' &&\n typeof candidate.job === 'string' &&\n typeof candidate.step === 'string' &&\n (candidate.status === 'STARTED' || candidate.status === 'FINISHED' || candidate.status === 'FAILED') &&\n (candidate.duration === undefined || typeof candidate.duration === 'string')\n );\n}\n\nclass WorkflowStepMessageLines implements Component {\n constructor(\n private readonly details: WorkflowStepMessageDetails,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n const header = `${this.theme.fg('accent', `[${this.details.displayName}]:`)} ${this.details.job}`;\n const transition = this.details.duration\n ? `${this.details.status}::${this.details.duration}::${this.details.step}`\n : `${this.details.status}::${this.details.step}`;\n const colour =\n this.details.status === 'FAILED' ? 'error' : this.details.status === 'FINISHED' ? 'success' : 'accent';\n return [truncateToWidth(header, width), truncateToWidth(this.theme.fg(colour, transition), width)];\n }\n\n invalidate(): void {}\n}\n\nexport function renderWorkflowStepMessage(\n details: WorkflowStepMessageDetails | undefined,\n fallback: string,\n outputPad: number,\n theme: Theme,\n): Component {\n const box = new Box(outputPad, 1, (text) => theme.bg('customMessageBg', text));\n box.addChild(details ? new WorkflowStepMessageLines(details, theme) : new Text(fallback, 0, 0));\n return box;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflowStepMessage.mjs","names":[],"sources":["../../../src/tui/workflow/workflowStepMessage.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { Box, type Component, Text, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { StepReport } from './workflowStatusRow';\n\nexport interface WorkflowStepMessageDetails {\n displayName: string;\n duration?: string;\n job: string;\n status: StepReport['status'];\n step: string;\n}\n\nexport function workflowStepMessageDetails(displayName: string, report: StepReport): WorkflowStepMessageDetails {\n return {\n displayName,\n job: report.job,\n status: report.status,\n step: report.step,\n ...(report.duration ? { duration: report.duration } : {}),\n };\n}\n\nexport function formatWorkflowStepMessage(details: WorkflowStepMessageDetails): string {\n const transition = details.duration\n ? `${details.status}::${details.duration}::${details.step}`\n : `${details.status}::${details.step}`;\n return `[${details.displayName}]: ${details.job}\\n${transition}`;\n}\n\nexport function isWorkflowStepMessageDetails(value: unknown): value is WorkflowStepMessageDetails {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<WorkflowStepMessageDetails>;\n return (\n typeof candidate.displayName === 'string' &&\n typeof candidate.job === 'string' &&\n typeof candidate.step === 'string' &&\n (candidate.status === 'STARTED' || candidate.status === 'FINISHED' || candidate.status === 'FAILED') &&\n (candidate.duration === undefined || typeof candidate.duration === 'string')\n );\n}\n\nclass WorkflowStepMessageLines implements Component {\n constructor(\n private readonly details: WorkflowStepMessageDetails,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n const header = `${this.theme.fg('accent', `[${this.details.displayName}]:`)} ${this.details.job}`;\n const transition = this.details.duration\n ? `${this.details.status}::${this.details.duration}::${this.details.step}`\n : `${this.details.status}::${this.details.step}`;\n const colour =\n this.details.status === 'FAILED' ? 'error' : this.details.status === 'FINISHED' ? 'success' : 'accent';\n return [truncateToWidth(header, width), truncateToWidth(this.theme.fg(colour, transition), width)];\n }\n\n invalidate(): void {}\n}\n\nexport function renderWorkflowStepMessage(\n details: WorkflowStepMessageDetails | undefined,\n fallback: string,\n outputPad: number,\n theme: Theme,\n): Component {\n const box = new Box(outputPad, 1, (text) => theme.bg('customMessageBg', text));\n box.addChild(details ? new WorkflowStepMessageLines(details, theme) : new Text(fallback, 0, 0));\n return box;\n}\n"],"mappings":"4EA6BA,SAAgB,EAA6B,EAAqD,CAChG,GAAI,CAAC,GAAS,OAAO,GAAU,UAAY,MAAM,QAAQ,CAAK,EAAG,MAAO,GACxE,IAAM,EAAY,EAClB,OACE,OAAO,EAAU,aAAgB,UACjC,OAAO,EAAU,KAAQ,UACzB,OAAO,EAAU,MAAS,WACzB,EAAU,SAAW,WAAa,EAAU,SAAW,YAAc,EAAU,SAAW,YAC1F,EAAU,WAAa,IAAA,IAAa,OAAO,EAAU,UAAa,SAEvE,CAEA,IAAM,EAAN,KAAoD,CAE/B,QACA,MAFnB,YACE,EACA,EACA,CAFiB,KAAA,QAAA,EACA,KAAA,MAAA,CAChB,CAEH,OAAO,EAAyB,CAC9B,IAAM,EAAS,GAAG,KAAK,MAAM,GAAG,SAAU,IAAI,KAAK,QAAQ,YAAY,GAAG,EAAE,GAAG,KAAK,QAAQ,MACtF,EAAa,KAAK,QAAQ,SAC5B,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,SAAS,IAAI,KAAK,QAAQ,OAClE,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,OACtC,EACJ,KAAK,QAAQ,SAAW,SAAW,QAAU,KAAK,QAAQ,SAAW,WAAa,UAAY,SAChG,MAAO,CAAC,EAAgB,EAAQ,CAAK,EAAG,EAAgB,KAAK,MAAM,GAAG,EAAQ,CAAU,EAAG,CAAK,CAAC,CACnG,CAEA,YAAmB,CAAC,CACtB,EAEA,SAAgB,EACd,EACA,EACA,EACA,EACW,CACX,IAAM,EAAM,IAAI,EAAI,EAAW,EAAI,GAAS,EAAM,GAAG,kBAAmB,CAAI,CAAC,EAE7E,OADA,EAAI,SAAS,EAAU,IAAI,EAAyB,EAAS,CAAK,EAAI,IAAI,EAAK,EAAU,EAAG,CAAC,CAAC,EACvF,CACT"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
let e=require("@agimon-ai/doompi-ui/toolChrome");function t(e){if(!(!e||typeof e!=`object`||Array.isArray(e)))return e}function n(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=e<=31||e===127?` `:n}return t.replace(/\s+/gu,` `).trim()}function r(e,t){let r=e?.[t];if(typeof r==`string`)return n(r)||void 0}function i(e,t){let n=e?.[t];return typeof n==`number`&&Number.isFinite(n)?n:void 0}function a(e,t){let n=e?.[t];return typeof n==`boolean`?n:void 0}function o(e,t){let n=e?.[t];return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]}function s(e){if(e)try{return t(JSON.parse(e))}catch{return}}function c(e){return Array.isArray(e.content)?e.content.flatMap(e=>{let n=t(e);return n?.type===`text`&&typeof n.text==`string`?[n.text]:[]}):[]}function l(e){return c(e).flatMap(e=>e.split(`
|
|
2
|
+
`)).map(e=>e.trimEnd()).filter(e=>e.length>0)}function u(e){return s(c(e)[0])}function d(e,t,n=`${t}s`){return e===1?t:n}function f(e,t,n){return`${n.fg(`dim`,`${e} `)}${n.fg(`text`,t)}`}function p(e){let t=r(e,`runKey`);if(!t)return;let n=r(e,`workspace`);return n?`${n}/${t}`:t}function m(e,t){return e===`list_workflows`?`list`:e===`launch_workflow`?`launch`:r(t,`action`)??`run`}function h(e,t){return e===`list_workflows`?r(t,`directory`):e===`launch_workflow`?r(t,`workflowPath`):p(t)}function g(t,n,r){return new e.DoomToolResult(t,n,{wrap:r})}function _(e,t){return t.fg(`dim`,`… ${e} more · ctrl+o`)}function v(e){return e.replace(`T`,` `).replace(/\.\d{3}Z$/,`Z`)}function y(e){if(!e)return`workflow`;let t=e.split(`/`).at(-1)??e;return t.replace(/\.workflow\.ya?ml$/u,``)||t}function b(e,n,s){let c=[];if(e===`list_workflows`){let e=i(n,`page`),t=i(n,`pageSize`);e!==void 0&&c.push(`page ${e}`),t!==void 0&&c.push(`${t}/page`),r(n,`filter`)&&c.push(`filtered`);let a=o(n,`tags`);a.length>0&&c.push(`${a.length} ${d(a.length,`tag`)}`)}else if(e===`launch_workflow`){let e=r(n,`job`),i=r(n,`runner`),o=r(n,`workspace`),s=Object.keys(t(n.inputs)??{}).length,l=Object.keys(t(n.env)??{}).length;o&&c.push(`workspace ${o}`),e&&c.push(`job ${e}`),i&&c.push(`runner ${i}`),a(n,`dryRun`)&&c.push(`dry run`),r(n,`prompt`)&&c.push(`with prompt`),s>0&&c.push(`${s} ${d(s,`input`)}`),l>0&&c.push(`${l} env`)}else r(n,`expectedRunId`)&&c.push(`generation checked`),r(n,`reason`)&&c.push(`with reason`),a(n,`dryRun`)&&c.push(`dry run`),r(n,`job`)&&c.push(`job override`),r(n,`runner`)&&c.push(`runner override`);return c.map(e=>s.fg(`dim`,e))}function x(t,n,r){let i=m(t,n),a=h(t,n),o=b(t,n,r),s=[r.bold(i),a?r.fg(`muted`,a):void 0,...o].filter(e=>!!e).join(r.fg(`dim`,` · `));return new e.DoomToolCall((0,e.renderToolHeading)(`workflow`,s,r))}function S(e){if(!Array.isArray(e?.workflows))return;let n=e.workflows.flatMap(e=>{let n=t(e),i=r(n,`path`);return!n||!i?[]:[{description:r(n,`description`)??``,name:r(n,`name`)??``,path:i,tags:o(n,`tags`)}]}),a=Array.isArray(e.tags)?e.tags.flatMap(e=>{let n=t(e),a=r(n,`tag`),o=i(n,`count`);return a&&o!==void 0?[{count:o,tag:a}]:[]}):[];return{directory:r(e,`directory`),page:i(e,`page`)??1,pageSize:i(e,`pageSize`)??n.length,tags:a,total:i(e,`total`)??n.length,totalPages:i(e,`totalPages`)??+(n.length>0),workflows:n}}function C(e,t,n){let r=e.name||y(e.path),i=[`${n.fg(`accent`,`›`)} ${n.bold(r)} ${n.fg(`muted`,`· ${e.path}`)}`];return t?(e.description&&i.push(` ${n.fg(`text`,e.description)}`),e.tags.length>0&&i.push(` ${f(`tags`,e.tags.join(`, `),n)}`),i):i}function w(e,t,n){let r=S(u(e));if(!r)return;let i=`page ${r.page}/${r.totalPages}`,a=`${n.fg(r.total>0?`success`:`muted`,r.total>0?`✓`:`○`)} ${n.bold(`${r.total} ${d(r.total,`workflow`)}`)} ${n.fg(`dim`,`· ${i}`)}`,o=t.expanded?r.workflows:r.workflows.slice(0,6),s=[a,...o.flatMap(e=>C(e,t.expanded,n))],c=r.workflows.length-o.length;if(c>0&&s.push(_(c,n)),t.expanded){if(r.tags.length>0){let e=r.tags.map(({count:e,tag:t})=>`${t} (${e})`).join(`, `);s.push(f(`available tags`,e,n))}r.directory&&s.push(f(`directory`,r.directory,n)),s.push(f(`page size`,String(r.pageSize),n))}return g(s,n,t.expanded)}function T(e){let t=r(e,`effectiveStage`)??r(e,`stage`)??`unknown`,n=r(e,`effectiveOutcome`)??r(e,`outcome`),i=r(e,`executionState`);return n===`interrupted`?{color:`warning`,glyph:`!`,label:`interrupted`}:t===`error`||n===`failed`?{color:`error`,glyph:`✗`,label:`failed`}:t===`completed`&&n===`skipped`?{color:`muted`,glyph:`○`,label:`skipped`}:t===`completed`?{color:`success`,glyph:`✓`,label:`completed`}:i===`paused`?{color:`warning`,glyph:`Ⅱ`,label:`paused`}:i===`pause_requested`?{color:`warning`,glyph:`◐`,label:`pause requested`}:i===`resume_requested`?{color:`warning`,glyph:`◐`,label:`resume requested`}:t===`running`?{color:`accent`,glyph:`◐`,label:`running`}:{color:`muted`,glyph:`○`,label:t}}function E(e){let t=r(e,`runKey`)??`unknown run`,n=r(e,`workspace`);return n?`${n}/${t}`:t}function D(e,n){let i=t(e.executionCursor),a=r(i,`job`)??r(e,`job`),o=r(i,`stepName`),s=r(i,`phase`);if(!a&&!o&&!s)return;let c=a??s??`workflow`;return` ${n.fg(`muted`,`[${c}]`)} ${n.fg(`text`,o??s??`running`)}`}function O(e){let n=t(e.launcher),i=r(n,`type`);if(!i)return;let a=r(n,`sessionName`)??r(n,`workspaceId`);return a?`${i} ${a}`:i}function k(e,t,n){let o=r(e,`runKey`),s=r(e,`stage`);if(!o||!s)return;let c=T(e),l=r(e,`displayName`)??o,u=[`${n.fg(c.color,c.glyph)} ${n.bold(l)} ${n.fg(`dim`,`· ${E(e)} ·`)} ${n.fg(c.color,c.label)}`],d=D(e,n);d&&u.push(d);let p=r(e,`errorMessage`)??r(e,`failureReason`);p&&u.push(`${n.fg(`error`,`error`)} ${n.fg(`text`,p)}`);let m=r(e,`staleReason`);m&&u.push(`${n.fg(`warning`,`stale`)} ${n.fg(`text`,m)}`);let h=[r(e,`runner`)?`runner ${r(e,`runner`)}`:void 0,a(e,`dryRun`)?`dry run`:void 0,i(e,`exitCode`)===void 0?void 0:`exit ${i(e,`exitCode`)}`,r(e,`startedAt`)?`started ${v(r(e,`startedAt`)??``)}`:void 0].filter(e=>!!e);if(h.length>0&&u.push(n.fg(`dim`,h.join(` · `))),!t)return u;let g=[[`workflow`,r(e,`workflowPath`)],[`workflow id`,r(e,`workflowId`)],[`run id`,r(e,`runId`)],[`launcher`,O(e)],[`job`,r(e,`job`)],[`outcome`,r(e,`effectiveOutcome`)??r(e,`outcome`)],[`finished`,r(e,`finishedAt`)?v(r(e,`finishedAt`)??``):void 0],[`pid`,i(e,`pid`)?.toString()],[`repair`,r(e,`activeRepairId`)?`#${r(e,`activeRepairId`)}`:void 0],[`worktree`,r(e,`worktreeBranch`)??r(e,`worktreePath`)]];for(let[e,t]of g)t&&u.push(f(e,t,n));return u}function A(e,t,n){let r=u(e);if(!r)return;let i=k(r,t.expanded,n);return i?g(i,n,t.expanded):void 0}function j(e){return r(t(e?.request),`requestedAt`)??r(e,`requestedAt`)}function M(e,t,n,i,a){let o=u(n),s=p(t)??r(o,`runKey`)??`workflow`,c=r(o,`workspace`),l=c&&!s.includes(`/`)?`${c}/${s}`:s,d=[`${a.fg(`warning`,`◐`)} ${a.bold(`${e} requested`)} ${a.fg(`dim`,`· ${l}`)}`],m=j(o);return m&&d.push(f(`requested`,v(m),a)),i.expanded&&d.push(a.fg(`dim`,`verify the transition with workflow_run status`)),g(d,a,i.expanded)}function N(e,t){return e.match(t)?.[1]?.trim()}function P(e,t){let n=N(e,/^Started (.+) in workspace .+\.$/mu),i=N(e,/^Started .+ in workspace (.+)\.$/mu)??r(t,`workspace`),a=N(e,/^Run key:\s*(.+)$/mu),o=r(t,`workflowPath`),s=n??y(o),c=a?i?`${i}/${a}`:a:i??o??`workflow`;return/Workflow completed successfully/iu.test(e)?{name:s,scope:c,state:{color:`success`,glyph:`✓`,label:`completed`}}:/Workflow skipped/iu.test(e)?{name:s,scope:c,state:{color:`muted`,glyph:`○`,label:`skipped`}}:/no run was registered/iu.test(e)?{name:s,scope:c,state:{color:`warning`,glyph:`◐`,label:`starting · run key pending`}}:{name:s,scope:c,state:{color:`accent`,glyph:`◐`,label:a?`started`:`launch accepted`}}}function F(e,t,n,i){let o=P(c(t).join(`
|
|
3
|
+
`),e),s=[`${i.fg(o.state.color,o.state.glyph)} ${i.bold(o.name)} ${i.fg(`dim`,`· ${o.scope} ·`)} ${i.fg(o.state.color,o.state.label)}`],u=[r(e,`job`)?`job ${r(e,`job`)}`:void 0,r(e,`runner`)?`runner ${r(e,`runner`)}`:void 0,a(e,`dryRun`)?`dry run`:void 0].filter(e=>!!e);if(u.length>0&&s.push(i.fg(`dim`,u.join(` · `))),n.expanded){let e=l(t).filter(e=>!e.startsWith(`Next steps for the agent:`));e.length>0&&s.push(...e)}return g(s,i,n.expanded)}function I(e){let t=[],n;for(let r of e.split(`
|
|
4
|
+
`)){let e=r.match(/^--- (.+) ---$/u);if(e?.[1]){n={body:[],name:e[1]},t.push(n);continue}n?.body.push(r)}return t}function L(e){return s(e.find(e=>e.name===`run.json`)?.body.join(`
|
|
5
|
+
`))}function R(e,t,n,r){let i=I(c(t).join(`
|
|
6
|
+
`)),a=L(i),o=a?k(a,n.expanded,r):void 0,s=i.filter(e=>e.name!==`run.json`),l=p(e)??`workflow`,u=o??[`${r.fg(`success`,`✓`)} ${r.bold(`recovery evidence`)} ${r.fg(`dim`,`· ${l}`)}`],d=s.map(e=>e.name);if(u.push(d.length>0?f(`evidence`,d.join(`, `),r):r.fg(`muted`,`no durable evidence files recorded`)),n.expanded)for(let e of s)u.push(r.bold(e.name),...e.body.filter(e=>e.length>0));return g(u,r,n.expanded)}function z(e,t,n,r,i){let o=p(t)??`workflow`,s={follow:`following output`,open:`launcher opened`,recover:a(t,`dryRun`)?`recovery dry run finished`:`recovery accepted`,tail:`launcher output checked`},c=e===`recover`?{color:`accent`,glyph:`◐`,label:s[e]??e}:{color:`success`,glyph:`✓`,label:s[e]??e},u=[`${i.fg(c.color,c.glyph)} ${i.bold(c.label)} ${i.fg(`dim`,`· ${o}`)}`],d=l(n);if(e===`tail`){let e=d.find(e=>e.startsWith(`Stage:`)),t=d.find(e=>e.startsWith(`Workflow:`));t&&u.push(t),e&&u.push(e)}return r.expanded&&d.length>0&&u.push(...d),g(u,i,r.expanded)}function B(e,t,n,r,i){let a=m(e,t),o=h(e,t),s=`${i.fg(`error`,`✗`)} ${i.bold(`${a} failed`)}${o?i.fg(`dim`,` · ${o}`):``}`,c=l(n),u=r.expanded?c:c.slice(0,8),d=[s,...u.length>0?u:[i.fg(`error`,`Unknown workflow error`)]],f=c.length-u.length;return f>0&&d.push(_(f,i)),g(d,i,r.expanded)}function V(e,t,n,r,i){let a=l(n),o=a.slice(-4),s=m(e,t),c=h(e,t),u=`${i.fg(`accent`,`◐`)} ${i.bold(s)}${c?i.fg(`dim`,` · ${c}`):``}`,d=a.length-o.length,f=[u];return d>0&&f.push(i.fg(`dim`,`… ${d} earlier`)),f.push(...o),g(f,i,r.expanded)}function H(e,t,n){let r=l(e);if(r.length===0)return g([n.fg(`muted`,`No workflow output.`)],n,t.expanded);let i=t.expanded?r:r.slice(0,8),a=[...i],o=r.length-i.length;return o>0&&a.push(_(o,n)),g(a,n,t.expanded)}function U(e,t,n,i,a){if(i.isPartial)return V(e,t,n,i,a);if(i.isError===!0||n.isError===!0)return B(e,t,n,i,a);if(e===`list_workflows`)return w(n,i,a)??H(n,i,a);if(e===`launch_workflow`)return F(t,n,i,a);let o=r(t,`action`);return o===`status`?A(n,i,a)??H(n,i,a):o===`pause`||o===`resume`||o===`stop`?M(o,t,n,i,a):o===`recovery-evidence`?R(t,n,i,a):o===`follow`||o===`open`||o===`recover`||o===`tail`?z(o,t,n,i,a):H(n,i,a)}exports.renderWorkflowToolCall=x,exports.renderWorkflowToolResult=U;
|
|
7
|
+
//# sourceMappingURL=workflowToolRender.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowToolRender.cjs","names":["DoomToolResult","DoomToolCall","renderToolHeading"],"sources":["../../../src/tui/workflow/workflowToolRender.ts"],"sourcesContent":["import { DoomToolCall, DoomToolResult, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\nimport type { Component } from '@earendil-works/pi-tui';\n\nexport type WorkflowToolName = 'launch_workflow' | 'list_workflows' | 'workflow_run';\nexport type WorkflowPiToolName = WorkflowToolName;\n\ninterface WorkflowToolResultLike {\n content?: unknown;\n isError?: boolean;\n}\n\ninterface WorkflowToolRenderOptions {\n expanded: boolean;\n isError?: boolean;\n isPartial?: boolean;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\ntype WorkflowState = {\n color: ThemeColor;\n glyph: string;\n label: string;\n};\n\ninterface WorkflowCatalogItem {\n description: string;\n name: string;\n path: string;\n tags: string[];\n}\n\ninterface WorkflowCatalog {\n directory?: string;\n page: number;\n pageSize: number;\n tags: Array<{ count: number; tag: string }>;\n total: number;\n totalPages: number;\n workflows: WorkflowCatalogItem[];\n}\n\ninterface EvidenceSection {\n body: string[];\n name: string;\n}\n\nconst COLLAPSED_CATALOG_ROWS = 6;\nconst COLLAPSED_DETAIL_LINES = 8;\nconst PARTIAL_DETAIL_LINES = 4;\n\nfunction asRecord(value: unknown): JsonRecord | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as JsonRecord;\n}\n\nfunction inlineText(value: string): string {\n let normalized = '';\n for (const character of value) {\n const code = character.codePointAt(0) ?? 0;\n normalized += code <= 31 || code === 127 ? ' ' : character;\n }\n return normalized.replace(/\\s+/gu, ' ').trim();\n}\n\nfunction readString(record: JsonRecord | undefined, key: string): string | undefined {\n const value = record?.[key];\n if (typeof value !== 'string') return undefined;\n return inlineText(value) || undefined;\n}\n\nfunction readNumber(record: JsonRecord | undefined, key: string): number | undefined {\n const value = record?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction readBoolean(record: JsonRecord | undefined, key: string): boolean | undefined {\n const value = record?.[key];\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction readStringArray(record: JsonRecord | undefined, key: string): string[] {\n const value = record?.[key];\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === 'string');\n}\n\nfunction parseJsonRecord(text: string | undefined): JsonRecord | undefined {\n if (!text) return undefined;\n try {\n return asRecord(JSON.parse(text));\n } catch {\n return undefined;\n }\n}\n\nfunction textBlocks(result: WorkflowToolResultLike): string[] {\n if (!Array.isArray(result.content)) return [];\n return result.content.flatMap((item) => {\n const record = asRecord(item);\n return record?.type === 'text' && typeof record.text === 'string' ? [record.text] : [];\n });\n}\n\nfunction detailLines(result: WorkflowToolResultLike): string[] {\n return textBlocks(result)\n .flatMap((block) => block.split('\\n'))\n .map((line) => line.trimEnd())\n .filter((line) => line.length > 0);\n}\n\nfunction firstPayload(result: WorkflowToolResultLike): JsonRecord | undefined {\n return parseJsonRecord(textBlocks(result)[0]);\n}\n\nfunction plural(count: number, singular: string, pluralForm = `${singular}s`): string {\n return count === 1 ? singular : pluralForm;\n}\n\nfunction labelValue(label: string, value: string, theme: Theme): string {\n return `${theme.fg('dim', `${label} `)}${theme.fg('text', value)}`;\n}\n\nfunction targetScope(args: Record<string, unknown>): string | undefined {\n const runKey = readString(args, 'runKey');\n if (!runKey) return undefined;\n const workspace = readString(args, 'workspace');\n return workspace ? `${workspace}/${runKey}` : runKey;\n}\n\nfunction toolAction(toolName: WorkflowPiToolName, args: Record<string, unknown>): string {\n if (toolName === 'list_workflows') return 'list';\n if (toolName === 'launch_workflow') return 'launch';\n return readString(args, 'action') ?? 'run';\n}\n\nfunction toolTarget(toolName: WorkflowPiToolName, args: Record<string, unknown>): string | undefined {\n if (toolName === 'list_workflows') return readString(args, 'directory');\n if (toolName === 'launch_workflow') return readString(args, 'workflowPath');\n return targetScope(args);\n}\n\nfunction renderResult(lines: readonly string[], theme: Theme, expanded: boolean): Component {\n return new DoomToolResult(lines, theme, { wrap: expanded });\n}\n\nfunction hiddenDetailLine(hidden: number, theme: Theme): string {\n return theme.fg('dim', `… ${hidden} more · ctrl+o`);\n}\n\nfunction statusTimestamp(value: string): string {\n return value.replace('T', ' ').replace(/\\.\\d{3}Z$/, 'Z');\n}\n\nfunction workflowNameFromPath(path: string | undefined): string {\n if (!path) return 'workflow';\n const filename = path.split('/').at(-1) ?? path;\n return filename.replace(/\\.workflow\\.ya?ml$/u, '') || filename;\n}\n\nfunction callMetadata(toolName: WorkflowPiToolName, args: Record<string, unknown>, theme: Theme): string[] {\n const values: string[] = [];\n\n if (toolName === 'list_workflows') {\n const page = readNumber(args, 'page');\n const pageSize = readNumber(args, 'pageSize');\n if (page !== undefined) values.push(`page ${page}`);\n if (pageSize !== undefined) values.push(`${pageSize}/page`);\n if (readString(args, 'filter')) values.push('filtered');\n const tags = readStringArray(args, 'tags');\n if (tags.length > 0) values.push(`${tags.length} ${plural(tags.length, 'tag')}`);\n } else if (toolName === 'launch_workflow') {\n const job = readString(args, 'job');\n const runner = readString(args, 'runner');\n const workspace = readString(args, 'workspace');\n const inputCount = Object.keys(asRecord(args.inputs) ?? {}).length;\n const environmentCount = Object.keys(asRecord(args.env) ?? {}).length;\n if (workspace) values.push(`workspace ${workspace}`);\n if (job) values.push(`job ${job}`);\n if (runner) values.push(`runner ${runner}`);\n if (readBoolean(args, 'dryRun')) values.push('dry run');\n if (readString(args, 'prompt')) values.push('with prompt');\n if (inputCount > 0) values.push(`${inputCount} ${plural(inputCount, 'input')}`);\n if (environmentCount > 0) values.push(`${environmentCount} env`);\n } else {\n const expectedRunId = readString(args, 'expectedRunId');\n if (expectedRunId) values.push('generation checked');\n if (readString(args, 'reason')) values.push('with reason');\n if (readBoolean(args, 'dryRun')) values.push('dry run');\n if (readString(args, 'job')) values.push('job override');\n if (readString(args, 'runner')) values.push('runner override');\n }\n\n return values.map((value) => theme.fg('dim', value));\n}\n\nexport function renderWorkflowToolCall(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n theme: Theme,\n): Component {\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const metadata = callMetadata(toolName, args, theme);\n const purpose = [theme.bold(action), target ? theme.fg('muted', target) : undefined, ...metadata]\n .filter((part): part is string => !!part)\n .join(theme.fg('dim', ' · '));\n return new DoomToolCall(renderToolHeading('workflow', purpose, theme));\n}\n\nfunction parseCatalog(payload: JsonRecord | undefined): WorkflowCatalog | undefined {\n if (!Array.isArray(payload?.workflows)) return undefined;\n\n const workflows = payload.workflows.flatMap((value) => {\n const record = asRecord(value);\n const path = readString(record, 'path');\n if (!record || !path) return [];\n return [\n {\n description: readString(record, 'description') ?? '',\n name: readString(record, 'name') ?? '',\n path,\n tags: readStringArray(record, 'tags'),\n },\n ];\n });\n const tags = Array.isArray(payload.tags)\n ? payload.tags.flatMap((value) => {\n const record = asRecord(value);\n const tag = readString(record, 'tag');\n const count = readNumber(record, 'count');\n return tag && count !== undefined ? [{ count, tag }] : [];\n })\n : [];\n\n return {\n directory: readString(payload, 'directory'),\n page: readNumber(payload, 'page') ?? 1,\n pageSize: readNumber(payload, 'pageSize') ?? workflows.length,\n tags,\n total: readNumber(payload, 'total') ?? workflows.length,\n totalPages: readNumber(payload, 'totalPages') ?? (workflows.length > 0 ? 1 : 0),\n workflows,\n };\n}\n\nfunction catalogItemLines(item: WorkflowCatalogItem, expanded: boolean, theme: Theme): string[] {\n const name = item.name || workflowNameFromPath(item.path);\n const lines = [`${theme.fg('accent', '›')} ${theme.bold(name)} ${theme.fg('muted', `· ${item.path}`)}`];\n if (!expanded) return lines;\n if (item.description) lines.push(` ${theme.fg('text', item.description)}`);\n if (item.tags.length > 0) lines.push(` ${labelValue('tags', item.tags.join(', '), theme)}`);\n return lines;\n}\n\nfunction renderCatalogResult(\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component | undefined {\n const catalog = parseCatalog(firstPayload(result));\n if (!catalog) return undefined;\n\n const pageLabel = `page ${catalog.page}/${catalog.totalPages}`;\n const summary = `${theme.fg(catalog.total > 0 ? 'success' : 'muted', catalog.total > 0 ? '✓' : '○')} ${theme.bold(\n `${catalog.total} ${plural(catalog.total, 'workflow')}`,\n )} ${theme.fg('dim', `· ${pageLabel}`)}`;\n const shown = options.expanded ? catalog.workflows : catalog.workflows.slice(0, COLLAPSED_CATALOG_ROWS);\n const lines = [summary, ...shown.flatMap((item) => catalogItemLines(item, options.expanded, theme))];\n const hidden = catalog.workflows.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n\n if (options.expanded) {\n if (catalog.tags.length > 0) {\n const tags = catalog.tags.map(({ count, tag }) => `${tag} (${count})`).join(', ');\n lines.push(labelValue('available tags', tags, theme));\n }\n if (catalog.directory) lines.push(labelValue('directory', catalog.directory, theme));\n lines.push(labelValue('page size', String(catalog.pageSize), theme));\n }\n\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction readWorkflowState(record: JsonRecord): WorkflowState {\n const stage = readString(record, 'effectiveStage') ?? readString(record, 'stage') ?? 'unknown';\n const outcome = readString(record, 'effectiveOutcome') ?? readString(record, 'outcome');\n const executionState = readString(record, 'executionState');\n\n if (outcome === 'interrupted') return { color: 'warning', glyph: '!', label: 'interrupted' };\n if (stage === 'error' || outcome === 'failed') return { color: 'error', glyph: '✗', label: 'failed' };\n if (stage === 'completed' && outcome === 'skipped') return { color: 'muted', glyph: '○', label: 'skipped' };\n if (stage === 'completed') return { color: 'success', glyph: '✓', label: 'completed' };\n if (executionState === 'paused') return { color: 'warning', glyph: 'Ⅱ', label: 'paused' };\n if (executionState === 'pause_requested') return { color: 'warning', glyph: '◐', label: 'pause requested' };\n if (executionState === 'resume_requested') return { color: 'warning', glyph: '◐', label: 'resume requested' };\n if (stage === 'running') return { color: 'accent', glyph: '◐', label: 'running' };\n return { color: 'muted', glyph: '○', label: stage };\n}\n\nfunction recordScope(record: JsonRecord): string {\n const runKey = readString(record, 'runKey') ?? 'unknown run';\n const workspace = readString(record, 'workspace');\n return workspace ? `${workspace}/${runKey}` : runKey;\n}\n\nfunction currentActivity(record: JsonRecord, theme: Theme): string | undefined {\n const cursor = asRecord(record.executionCursor);\n const job = readString(cursor, 'job') ?? readString(record, 'job');\n const step = readString(cursor, 'stepName');\n const phase = readString(cursor, 'phase');\n if (!job && !step && !phase) return undefined;\n const context = job ?? phase ?? 'workflow';\n return ` ${theme.fg('muted', `[${context}]`)} ${theme.fg('text', step ?? phase ?? 'running')}`;\n}\n\nfunction launcherLabel(record: JsonRecord): string | undefined {\n const launcher = asRecord(record.launcher);\n const type = readString(launcher, 'type');\n if (!type) return undefined;\n const identity = readString(launcher, 'sessionName') ?? readString(launcher, 'workspaceId');\n return identity ? `${type} ${identity}` : type;\n}\n\nfunction workflowRecordLines(record: JsonRecord, expanded: boolean, theme: Theme): string[] | undefined {\n const runKey = readString(record, 'runKey');\n const stage = readString(record, 'stage');\n if (!runKey || !stage) return undefined;\n\n const state = readWorkflowState(record);\n const displayName = readString(record, 'displayName') ?? runKey;\n const heading = `${theme.fg(state.color, state.glyph)} ${theme.bold(displayName)} ${theme.fg(\n 'dim',\n `· ${recordScope(record)} ·`,\n )} ${theme.fg(state.color, state.label)}`;\n const lines = [heading];\n const activity = currentActivity(record, theme);\n if (activity) lines.push(activity);\n\n const failure = readString(record, 'errorMessage') ?? readString(record, 'failureReason');\n if (failure) lines.push(`${theme.fg('error', 'error')} ${theme.fg('text', failure)}`);\n const staleReason = readString(record, 'staleReason');\n if (staleReason) lines.push(`${theme.fg('warning', 'stale')} ${theme.fg('text', staleReason)}`);\n\n const conciseMetadata = [\n readString(record, 'runner') ? `runner ${readString(record, 'runner')}` : undefined,\n readBoolean(record, 'dryRun') ? 'dry run' : undefined,\n readNumber(record, 'exitCode') !== undefined ? `exit ${readNumber(record, 'exitCode')}` : undefined,\n readString(record, 'startedAt') ? `started ${statusTimestamp(readString(record, 'startedAt') ?? '')}` : undefined,\n ].filter((value): value is string => !!value);\n if (conciseMetadata.length > 0) lines.push(theme.fg('dim', conciseMetadata.join(' · ')));\n\n if (!expanded) return lines;\n\n const detailedValues: Array<[string, string | undefined]> = [\n ['workflow', readString(record, 'workflowPath')],\n ['workflow id', readString(record, 'workflowId')],\n ['run id', readString(record, 'runId')],\n ['launcher', launcherLabel(record)],\n ['job', readString(record, 'job')],\n ['outcome', readString(record, 'effectiveOutcome') ?? readString(record, 'outcome')],\n [\n 'finished',\n readString(record, 'finishedAt') ? statusTimestamp(readString(record, 'finishedAt') ?? '') : undefined,\n ],\n ['pid', readNumber(record, 'pid')?.toString()],\n ['repair', readString(record, 'activeRepairId') ? `#${readString(record, 'activeRepairId')}` : undefined],\n ['worktree', readString(record, 'worktreeBranch') ?? readString(record, 'worktreePath')],\n ];\n for (const [label, value] of detailedValues) {\n if (value) lines.push(labelValue(label, value, theme));\n }\n return lines;\n}\n\nfunction renderStatusResult(\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component | undefined {\n const payload = firstPayload(result);\n if (!payload) return undefined;\n const lines = workflowRecordLines(payload, options.expanded, theme);\n return lines ? renderResult(lines, theme, options.expanded) : undefined;\n}\n\nfunction nestedRequestedAt(payload: JsonRecord | undefined): string | undefined {\n return readString(asRecord(payload?.request), 'requestedAt') ?? readString(payload, 'requestedAt');\n}\n\nfunction renderControlResult(\n action: string,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const payload = firstPayload(result);\n const scope = targetScope(args) ?? readString(payload, 'runKey') ?? 'workflow';\n const payloadWorkspace = readString(payload, 'workspace');\n const resolvedScope = payloadWorkspace && !scope.includes('/') ? `${payloadWorkspace}/${scope}` : scope;\n const lines = [\n `${theme.fg('warning', '◐')} ${theme.bold(`${action} requested`)} ${theme.fg('dim', `· ${resolvedScope}`)}`,\n ];\n const requestedAt = nestedRequestedAt(payload);\n if (requestedAt) lines.push(labelValue('requested', statusTimestamp(requestedAt), theme));\n if (options.expanded) lines.push(theme.fg('dim', 'verify the transition with workflow_run status'));\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction matchLine(text: string, pattern: RegExp): string | undefined {\n return text.match(pattern)?.[1]?.trim();\n}\n\nfunction launchSummary(\n text: string,\n args: Record<string, unknown>,\n): { name: string; scope: string; state: WorkflowState } {\n const startedName = matchLine(text, /^Started (.+) in workspace .+\\.$/mu);\n const workspace = matchLine(text, /^Started .+ in workspace (.+)\\.$/mu) ?? readString(args, 'workspace');\n const runKey = matchLine(text, /^Run key:\\s*(.+)$/mu);\n const path = readString(args, 'workflowPath');\n const name = startedName ?? workflowNameFromPath(path);\n const scope = runKey ? (workspace ? `${workspace}/${runKey}` : runKey) : (workspace ?? path ?? 'workflow');\n\n if (/Workflow completed successfully/iu.test(text)) {\n return { name, scope, state: { color: 'success', glyph: '✓', label: 'completed' } };\n }\n if (/Workflow skipped/iu.test(text)) {\n return { name, scope, state: { color: 'muted', glyph: '○', label: 'skipped' } };\n }\n if (/no run was registered/iu.test(text)) {\n return { name, scope, state: { color: 'warning', glyph: '◐', label: 'starting · run key pending' } };\n }\n return { name, scope, state: { color: 'accent', glyph: '◐', label: runKey ? 'started' : 'launch accepted' } };\n}\n\nfunction renderLaunchResult(\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const blocks = textBlocks(result);\n const text = blocks.join('\\n');\n const summary = launchSummary(text, args);\n const lines = [\n `${theme.fg(summary.state.color, summary.state.glyph)} ${theme.bold(summary.name)} ${theme.fg(\n 'dim',\n `· ${summary.scope} ·`,\n )} ${theme.fg(summary.state.color, summary.state.label)}`,\n ];\n const metadata = [\n readString(args, 'job') ? `job ${readString(args, 'job')}` : undefined,\n readString(args, 'runner') ? `runner ${readString(args, 'runner')}` : undefined,\n readBoolean(args, 'dryRun') ? 'dry run' : undefined,\n ].filter((value): value is string => !!value);\n if (metadata.length > 0) lines.push(theme.fg('dim', metadata.join(' · ')));\n if (options.expanded) {\n const details = detailLines(result).filter((line) => !line.startsWith('Next steps for the agent:'));\n if (details.length > 0) lines.push(...details);\n }\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction splitEvidence(text: string): EvidenceSection[] {\n const sections: EvidenceSection[] = [];\n let current: EvidenceSection | undefined;\n for (const line of text.split('\\n')) {\n const match = line.match(/^--- (.+) ---$/u);\n if (match?.[1]) {\n current = { body: [], name: match[1] };\n sections.push(current);\n continue;\n }\n current?.body.push(line);\n }\n return sections;\n}\n\nfunction recoveryRecord(sections: EvidenceSection[]): JsonRecord | undefined {\n const runSection = sections.find((section) => section.name === 'run.json');\n return parseJsonRecord(runSection?.body.join('\\n'));\n}\n\nfunction renderRecoveryEvidenceResult(\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const text = textBlocks(result).join('\\n');\n const sections = splitEvidence(text);\n const record = recoveryRecord(sections);\n const recordLines = record ? workflowRecordLines(record, options.expanded, theme) : undefined;\n const evidenceSections = sections.filter((section) => section.name !== 'run.json');\n const scope = targetScope(args) ?? 'workflow';\n const lines = recordLines ?? [\n `${theme.fg('success', '✓')} ${theme.bold('recovery evidence')} ${theme.fg('dim', `· ${scope}`)}`,\n ];\n const names = evidenceSections.map((section) => section.name);\n lines.push(\n names.length > 0\n ? labelValue('evidence', names.join(', '), theme)\n : theme.fg('muted', 'no durable evidence files recorded'),\n );\n\n if (options.expanded) {\n for (const section of evidenceSections) {\n lines.push(theme.bold(section.name), ...section.body.filter((line) => line.length > 0));\n }\n }\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderSimpleActionResult(\n action: string,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const scope = targetScope(args) ?? 'workflow';\n const actionLabel: Record<string, string> = {\n follow: 'following output',\n open: 'launcher opened',\n recover: readBoolean(args, 'dryRun') ? 'recovery dry run finished' : 'recovery accepted',\n tail: 'launcher output checked',\n };\n const state: WorkflowState =\n action === 'recover'\n ? { color: 'accent', glyph: '◐', label: actionLabel[action] ?? action }\n : { color: 'success', glyph: '✓', label: actionLabel[action] ?? action };\n const lines = [`${theme.fg(state.color, state.glyph)} ${theme.bold(state.label)} ${theme.fg('dim', `· ${scope}`)}`];\n const details = detailLines(result);\n if (action === 'tail') {\n const stageLine = details.find((line) => line.startsWith('Stage:'));\n const workflowLine = details.find((line) => line.startsWith('Workflow:'));\n if (workflowLine) lines.push(workflowLine);\n if (stageLine) lines.push(stageLine);\n }\n if (options.expanded && details.length > 0) lines.push(...details);\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderFailure(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const summary = `${theme.fg('error', '✗')} ${theme.bold(`${action} failed`)}${\n target ? theme.fg('dim', ` · ${target}`) : ''\n }`;\n const details = detailLines(result);\n const shown = options.expanded ? details : details.slice(0, COLLAPSED_DETAIL_LINES);\n const lines = [summary, ...(shown.length > 0 ? shown : [theme.fg('error', 'Unknown workflow error')])];\n const hidden = details.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderPartial(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const details = detailLines(result);\n const shown = details.slice(-PARTIAL_DETAIL_LINES);\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const summary = `${theme.fg('accent', '◐')} ${theme.bold(action)}${target ? theme.fg('dim', ` · ${target}`) : ''}`;\n const hidden = details.length - shown.length;\n const lines = [summary];\n if (hidden > 0) lines.push(theme.fg('dim', `… ${hidden} earlier`));\n lines.push(...shown);\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderFallback(result: WorkflowToolResultLike, options: WorkflowToolRenderOptions, theme: Theme): Component {\n const details = detailLines(result);\n if (details.length === 0) return renderResult([theme.fg('muted', 'No workflow output.')], theme, options.expanded);\n const shown = options.expanded ? details : details.slice(0, COLLAPSED_DETAIL_LINES);\n const lines = [...shown];\n const hidden = details.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n return renderResult(lines, theme, options.expanded);\n}\n\nexport function renderWorkflowToolResult(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n if (options.isPartial) return renderPartial(toolName, args, result, options, theme);\n if (options.isError === true || result.isError === true) {\n return renderFailure(toolName, args, result, options, theme);\n }\n\n if (toolName === 'list_workflows') {\n return renderCatalogResult(result, options, theme) ?? renderFallback(result, options, theme);\n }\n if (toolName === 'launch_workflow') return renderLaunchResult(args, result, options, theme);\n\n const action = readString(args, 'action');\n if (action === 'status') {\n return renderStatusResult(result, options, theme) ?? renderFallback(result, options, theme);\n }\n if (action === 'pause' || action === 'resume' || action === 'stop') {\n return renderControlResult(action, args, result, options, theme);\n }\n if (action === 'recovery-evidence') {\n return renderRecoveryEvidenceResult(args, result, options, theme);\n }\n if (action === 'follow' || action === 'open' || action === 'recover' || action === 'tail') {\n return renderSimpleActionResult(action, args, result, options, theme);\n }\n return renderFallback(result, options, theme);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{DoomToolCall as e,DoomToolResult as t,renderToolHeading as n}from"@agimon-ai/doompi-ui/toolChrome";function r(e){if(!(!e||typeof e!=`object`||Array.isArray(e)))return e}function i(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=e<=31||e===127?` `:n}return t.replace(/\s+/gu,` `).trim()}function a(e,t){let n=e?.[t];if(typeof n==`string`)return i(n)||void 0}function o(e,t){let n=e?.[t];return typeof n==`number`&&Number.isFinite(n)?n:void 0}function s(e,t){let n=e?.[t];return typeof n==`boolean`?n:void 0}function c(e,t){let n=e?.[t];return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]}function l(e){if(e)try{return r(JSON.parse(e))}catch{return}}function u(e){return Array.isArray(e.content)?e.content.flatMap(e=>{let t=r(e);return t?.type===`text`&&typeof t.text==`string`?[t.text]:[]}):[]}function d(e){return u(e).flatMap(e=>e.split(`
|
|
2
|
+
`)).map(e=>e.trimEnd()).filter(e=>e.length>0)}function f(e){return l(u(e)[0])}function p(e,t,n=`${t}s`){return e===1?t:n}function m(e,t,n){return`${n.fg(`dim`,`${e} `)}${n.fg(`text`,t)}`}function h(e){let t=a(e,`runKey`);if(!t)return;let n=a(e,`workspace`);return n?`${n}/${t}`:t}function g(e,t){return e===`list_workflows`?`list`:e===`launch_workflow`?`launch`:a(t,`action`)??`run`}function _(e,t){return e===`list_workflows`?a(t,`directory`):e===`launch_workflow`?a(t,`workflowPath`):h(t)}function v(e,n,r){return new t(e,n,{wrap:r})}function y(e,t){return t.fg(`dim`,`… ${e} more · ctrl+o`)}function b(e){return e.replace(`T`,` `).replace(/\.\d{3}Z$/,`Z`)}function x(e){if(!e)return`workflow`;let t=e.split(`/`).at(-1)??e;return t.replace(/\.workflow\.ya?ml$/u,``)||t}function S(e,t,n){let i=[];if(e===`list_workflows`){let e=o(t,`page`),n=o(t,`pageSize`);e!==void 0&&i.push(`page ${e}`),n!==void 0&&i.push(`${n}/page`),a(t,`filter`)&&i.push(`filtered`);let r=c(t,`tags`);r.length>0&&i.push(`${r.length} ${p(r.length,`tag`)}`)}else if(e===`launch_workflow`){let e=a(t,`job`),n=a(t,`runner`),o=a(t,`workspace`),c=Object.keys(r(t.inputs)??{}).length,l=Object.keys(r(t.env)??{}).length;o&&i.push(`workspace ${o}`),e&&i.push(`job ${e}`),n&&i.push(`runner ${n}`),s(t,`dryRun`)&&i.push(`dry run`),a(t,`prompt`)&&i.push(`with prompt`),c>0&&i.push(`${c} ${p(c,`input`)}`),l>0&&i.push(`${l} env`)}else a(t,`expectedRunId`)&&i.push(`generation checked`),a(t,`reason`)&&i.push(`with reason`),s(t,`dryRun`)&&i.push(`dry run`),a(t,`job`)&&i.push(`job override`),a(t,`runner`)&&i.push(`runner override`);return i.map(e=>n.fg(`dim`,e))}function C(t,r,i){let a=g(t,r),o=_(t,r),s=S(t,r,i),c=[i.bold(a),o?i.fg(`muted`,o):void 0,...s].filter(e=>!!e).join(i.fg(`dim`,` · `));return new e(n(`workflow`,c,i))}function w(e){if(!Array.isArray(e?.workflows))return;let t=e.workflows.flatMap(e=>{let t=r(e),n=a(t,`path`);return!t||!n?[]:[{description:a(t,`description`)??``,name:a(t,`name`)??``,path:n,tags:c(t,`tags`)}]}),n=Array.isArray(e.tags)?e.tags.flatMap(e=>{let t=r(e),n=a(t,`tag`),i=o(t,`count`);return n&&i!==void 0?[{count:i,tag:n}]:[]}):[];return{directory:a(e,`directory`),page:o(e,`page`)??1,pageSize:o(e,`pageSize`)??t.length,tags:n,total:o(e,`total`)??t.length,totalPages:o(e,`totalPages`)??+(t.length>0),workflows:t}}function T(e,t,n){let r=e.name||x(e.path),i=[`${n.fg(`accent`,`›`)} ${n.bold(r)} ${n.fg(`muted`,`· ${e.path}`)}`];return t?(e.description&&i.push(` ${n.fg(`text`,e.description)}`),e.tags.length>0&&i.push(` ${m(`tags`,e.tags.join(`, `),n)}`),i):i}function E(e,t,n){let r=w(f(e));if(!r)return;let i=`page ${r.page}/${r.totalPages}`,a=`${n.fg(r.total>0?`success`:`muted`,r.total>0?`✓`:`○`)} ${n.bold(`${r.total} ${p(r.total,`workflow`)}`)} ${n.fg(`dim`,`· ${i}`)}`,o=t.expanded?r.workflows:r.workflows.slice(0,6),s=[a,...o.flatMap(e=>T(e,t.expanded,n))],c=r.workflows.length-o.length;if(c>0&&s.push(y(c,n)),t.expanded){if(r.tags.length>0){let e=r.tags.map(({count:e,tag:t})=>`${t} (${e})`).join(`, `);s.push(m(`available tags`,e,n))}r.directory&&s.push(m(`directory`,r.directory,n)),s.push(m(`page size`,String(r.pageSize),n))}return v(s,n,t.expanded)}function D(e){let t=a(e,`effectiveStage`)??a(e,`stage`)??`unknown`,n=a(e,`effectiveOutcome`)??a(e,`outcome`),r=a(e,`executionState`);return n===`interrupted`?{color:`warning`,glyph:`!`,label:`interrupted`}:t===`error`||n===`failed`?{color:`error`,glyph:`✗`,label:`failed`}:t===`completed`&&n===`skipped`?{color:`muted`,glyph:`○`,label:`skipped`}:t===`completed`?{color:`success`,glyph:`✓`,label:`completed`}:r===`paused`?{color:`warning`,glyph:`Ⅱ`,label:`paused`}:r===`pause_requested`?{color:`warning`,glyph:`◐`,label:`pause requested`}:r===`resume_requested`?{color:`warning`,glyph:`◐`,label:`resume requested`}:t===`running`?{color:`accent`,glyph:`◐`,label:`running`}:{color:`muted`,glyph:`○`,label:t}}function O(e){let t=a(e,`runKey`)??`unknown run`,n=a(e,`workspace`);return n?`${n}/${t}`:t}function k(e,t){let n=r(e.executionCursor),i=a(n,`job`)??a(e,`job`),o=a(n,`stepName`),s=a(n,`phase`);if(!i&&!o&&!s)return;let c=i??s??`workflow`;return` ${t.fg(`muted`,`[${c}]`)} ${t.fg(`text`,o??s??`running`)}`}function A(e){let t=r(e.launcher),n=a(t,`type`);if(!n)return;let i=a(t,`sessionName`)??a(t,`workspaceId`);return i?`${n} ${i}`:n}function j(e,t,n){let r=a(e,`runKey`),i=a(e,`stage`);if(!r||!i)return;let c=D(e),l=a(e,`displayName`)??r,u=[`${n.fg(c.color,c.glyph)} ${n.bold(l)} ${n.fg(`dim`,`· ${O(e)} ·`)} ${n.fg(c.color,c.label)}`],d=k(e,n);d&&u.push(d);let f=a(e,`errorMessage`)??a(e,`failureReason`);f&&u.push(`${n.fg(`error`,`error`)} ${n.fg(`text`,f)}`);let p=a(e,`staleReason`);p&&u.push(`${n.fg(`warning`,`stale`)} ${n.fg(`text`,p)}`);let h=[a(e,`runner`)?`runner ${a(e,`runner`)}`:void 0,s(e,`dryRun`)?`dry run`:void 0,o(e,`exitCode`)===void 0?void 0:`exit ${o(e,`exitCode`)}`,a(e,`startedAt`)?`started ${b(a(e,`startedAt`)??``)}`:void 0].filter(e=>!!e);if(h.length>0&&u.push(n.fg(`dim`,h.join(` · `))),!t)return u;let g=[[`workflow`,a(e,`workflowPath`)],[`workflow id`,a(e,`workflowId`)],[`run id`,a(e,`runId`)],[`launcher`,A(e)],[`job`,a(e,`job`)],[`outcome`,a(e,`effectiveOutcome`)??a(e,`outcome`)],[`finished`,a(e,`finishedAt`)?b(a(e,`finishedAt`)??``):void 0],[`pid`,o(e,`pid`)?.toString()],[`repair`,a(e,`activeRepairId`)?`#${a(e,`activeRepairId`)}`:void 0],[`worktree`,a(e,`worktreeBranch`)??a(e,`worktreePath`)]];for(let[e,t]of g)t&&u.push(m(e,t,n));return u}function M(e,t,n){let r=f(e);if(!r)return;let i=j(r,t.expanded,n);return i?v(i,n,t.expanded):void 0}function N(e){return a(r(e?.request),`requestedAt`)??a(e,`requestedAt`)}function P(e,t,n,r,i){let o=f(n),s=h(t)??a(o,`runKey`)??`workflow`,c=a(o,`workspace`),l=c&&!s.includes(`/`)?`${c}/${s}`:s,u=[`${i.fg(`warning`,`◐`)} ${i.bold(`${e} requested`)} ${i.fg(`dim`,`· ${l}`)}`],d=N(o);return d&&u.push(m(`requested`,b(d),i)),r.expanded&&u.push(i.fg(`dim`,`verify the transition with workflow_run status`)),v(u,i,r.expanded)}function F(e,t){return e.match(t)?.[1]?.trim()}function I(e,t){let n=F(e,/^Started (.+) in workspace .+\.$/mu),r=F(e,/^Started .+ in workspace (.+)\.$/mu)??a(t,`workspace`),i=F(e,/^Run key:\s*(.+)$/mu),o=a(t,`workflowPath`),s=n??x(o),c=i?r?`${r}/${i}`:i:r??o??`workflow`;return/Workflow completed successfully/iu.test(e)?{name:s,scope:c,state:{color:`success`,glyph:`✓`,label:`completed`}}:/Workflow skipped/iu.test(e)?{name:s,scope:c,state:{color:`muted`,glyph:`○`,label:`skipped`}}:/no run was registered/iu.test(e)?{name:s,scope:c,state:{color:`warning`,glyph:`◐`,label:`starting · run key pending`}}:{name:s,scope:c,state:{color:`accent`,glyph:`◐`,label:i?`started`:`launch accepted`}}}function L(e,t,n,r){let i=I(u(t).join(`
|
|
3
|
+
`),e),o=[`${r.fg(i.state.color,i.state.glyph)} ${r.bold(i.name)} ${r.fg(`dim`,`· ${i.scope} ·`)} ${r.fg(i.state.color,i.state.label)}`],c=[a(e,`job`)?`job ${a(e,`job`)}`:void 0,a(e,`runner`)?`runner ${a(e,`runner`)}`:void 0,s(e,`dryRun`)?`dry run`:void 0].filter(e=>!!e);if(c.length>0&&o.push(r.fg(`dim`,c.join(` · `))),n.expanded){let e=d(t).filter(e=>!e.startsWith(`Next steps for the agent:`));e.length>0&&o.push(...e)}return v(o,r,n.expanded)}function R(e){let t=[],n;for(let r of e.split(`
|
|
4
|
+
`)){let e=r.match(/^--- (.+) ---$/u);if(e?.[1]){n={body:[],name:e[1]},t.push(n);continue}n?.body.push(r)}return t}function z(e){return l(e.find(e=>e.name===`run.json`)?.body.join(`
|
|
5
|
+
`))}function B(e,t,n,r){let i=R(u(t).join(`
|
|
6
|
+
`)),a=z(i),o=a?j(a,n.expanded,r):void 0,s=i.filter(e=>e.name!==`run.json`),c=h(e)??`workflow`,l=o??[`${r.fg(`success`,`✓`)} ${r.bold(`recovery evidence`)} ${r.fg(`dim`,`· ${c}`)}`],d=s.map(e=>e.name);if(l.push(d.length>0?m(`evidence`,d.join(`, `),r):r.fg(`muted`,`no durable evidence files recorded`)),n.expanded)for(let e of s)l.push(r.bold(e.name),...e.body.filter(e=>e.length>0));return v(l,r,n.expanded)}function V(e,t,n,r,i){let a=h(t)??`workflow`,o={follow:`following output`,open:`launcher opened`,recover:s(t,`dryRun`)?`recovery dry run finished`:`recovery accepted`,tail:`launcher output checked`},c=e===`recover`?{color:`accent`,glyph:`◐`,label:o[e]??e}:{color:`success`,glyph:`✓`,label:o[e]??e},l=[`${i.fg(c.color,c.glyph)} ${i.bold(c.label)} ${i.fg(`dim`,`· ${a}`)}`],u=d(n);if(e===`tail`){let e=u.find(e=>e.startsWith(`Stage:`)),t=u.find(e=>e.startsWith(`Workflow:`));t&&l.push(t),e&&l.push(e)}return r.expanded&&u.length>0&&l.push(...u),v(l,i,r.expanded)}function H(e,t,n,r,i){let a=g(e,t),o=_(e,t),s=`${i.fg(`error`,`✗`)} ${i.bold(`${a} failed`)}${o?i.fg(`dim`,` · ${o}`):``}`,c=d(n),l=r.expanded?c:c.slice(0,8),u=[s,...l.length>0?l:[i.fg(`error`,`Unknown workflow error`)]],f=c.length-l.length;return f>0&&u.push(y(f,i)),v(u,i,r.expanded)}function U(e,t,n,r,i){let a=d(n),o=a.slice(-4),s=g(e,t),c=_(e,t),l=`${i.fg(`accent`,`◐`)} ${i.bold(s)}${c?i.fg(`dim`,` · ${c}`):``}`,u=a.length-o.length,f=[l];return u>0&&f.push(i.fg(`dim`,`… ${u} earlier`)),f.push(...o),v(f,i,r.expanded)}function W(e,t,n){let r=d(e);if(r.length===0)return v([n.fg(`muted`,`No workflow output.`)],n,t.expanded);let i=t.expanded?r:r.slice(0,8),a=[...i],o=r.length-i.length;return o>0&&a.push(y(o,n)),v(a,n,t.expanded)}function G(e,t,n,r,i){if(r.isPartial)return U(e,t,n,r,i);if(r.isError===!0||n.isError===!0)return H(e,t,n,r,i);if(e===`list_workflows`)return E(n,r,i)??W(n,r,i);if(e===`launch_workflow`)return L(t,n,r,i);let o=a(t,`action`);return o===`status`?M(n,r,i)??W(n,r,i):o===`pause`||o===`resume`||o===`stop`?P(o,t,n,r,i):o===`recovery-evidence`?B(t,n,r,i):o===`follow`||o===`open`||o===`recover`||o===`tail`?V(o,t,n,r,i):W(n,r,i)}export{C as renderWorkflowToolCall,G as renderWorkflowToolResult};
|
|
7
|
+
//# sourceMappingURL=workflowToolRender.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowToolRender.mjs","names":[],"sources":["../../../src/tui/workflow/workflowToolRender.ts"],"sourcesContent":["import { DoomToolCall, DoomToolResult, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\nimport type { Component } from '@earendil-works/pi-tui';\n\nexport type WorkflowToolName = 'launch_workflow' | 'list_workflows' | 'workflow_run';\nexport type WorkflowPiToolName = WorkflowToolName;\n\ninterface WorkflowToolResultLike {\n content?: unknown;\n isError?: boolean;\n}\n\ninterface WorkflowToolRenderOptions {\n expanded: boolean;\n isError?: boolean;\n isPartial?: boolean;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\ntype WorkflowState = {\n color: ThemeColor;\n glyph: string;\n label: string;\n};\n\ninterface WorkflowCatalogItem {\n description: string;\n name: string;\n path: string;\n tags: string[];\n}\n\ninterface WorkflowCatalog {\n directory?: string;\n page: number;\n pageSize: number;\n tags: Array<{ count: number; tag: string }>;\n total: number;\n totalPages: number;\n workflows: WorkflowCatalogItem[];\n}\n\ninterface EvidenceSection {\n body: string[];\n name: string;\n}\n\nconst COLLAPSED_CATALOG_ROWS = 6;\nconst COLLAPSED_DETAIL_LINES = 8;\nconst PARTIAL_DETAIL_LINES = 4;\n\nfunction asRecord(value: unknown): JsonRecord | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as JsonRecord;\n}\n\nfunction inlineText(value: string): string {\n let normalized = '';\n for (const character of value) {\n const code = character.codePointAt(0) ?? 0;\n normalized += code <= 31 || code === 127 ? ' ' : character;\n }\n return normalized.replace(/\\s+/gu, ' ').trim();\n}\n\nfunction readString(record: JsonRecord | undefined, key: string): string | undefined {\n const value = record?.[key];\n if (typeof value !== 'string') return undefined;\n return inlineText(value) || undefined;\n}\n\nfunction readNumber(record: JsonRecord | undefined, key: string): number | undefined {\n const value = record?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction readBoolean(record: JsonRecord | undefined, key: string): boolean | undefined {\n const value = record?.[key];\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction readStringArray(record: JsonRecord | undefined, key: string): string[] {\n const value = record?.[key];\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === 'string');\n}\n\nfunction parseJsonRecord(text: string | undefined): JsonRecord | undefined {\n if (!text) return undefined;\n try {\n return asRecord(JSON.parse(text));\n } catch {\n return undefined;\n }\n}\n\nfunction textBlocks(result: WorkflowToolResultLike): string[] {\n if (!Array.isArray(result.content)) return [];\n return result.content.flatMap((item) => {\n const record = asRecord(item);\n return record?.type === 'text' && typeof record.text === 'string' ? [record.text] : [];\n });\n}\n\nfunction detailLines(result: WorkflowToolResultLike): string[] {\n return textBlocks(result)\n .flatMap((block) => block.split('\\n'))\n .map((line) => line.trimEnd())\n .filter((line) => line.length > 0);\n}\n\nfunction firstPayload(result: WorkflowToolResultLike): JsonRecord | undefined {\n return parseJsonRecord(textBlocks(result)[0]);\n}\n\nfunction plural(count: number, singular: string, pluralForm = `${singular}s`): string {\n return count === 1 ? singular : pluralForm;\n}\n\nfunction labelValue(label: string, value: string, theme: Theme): string {\n return `${theme.fg('dim', `${label} `)}${theme.fg('text', value)}`;\n}\n\nfunction targetScope(args: Record<string, unknown>): string | undefined {\n const runKey = readString(args, 'runKey');\n if (!runKey) return undefined;\n const workspace = readString(args, 'workspace');\n return workspace ? `${workspace}/${runKey}` : runKey;\n}\n\nfunction toolAction(toolName: WorkflowPiToolName, args: Record<string, unknown>): string {\n if (toolName === 'list_workflows') return 'list';\n if (toolName === 'launch_workflow') return 'launch';\n return readString(args, 'action') ?? 'run';\n}\n\nfunction toolTarget(toolName: WorkflowPiToolName, args: Record<string, unknown>): string | undefined {\n if (toolName === 'list_workflows') return readString(args, 'directory');\n if (toolName === 'launch_workflow') return readString(args, 'workflowPath');\n return targetScope(args);\n}\n\nfunction renderResult(lines: readonly string[], theme: Theme, expanded: boolean): Component {\n return new DoomToolResult(lines, theme, { wrap: expanded });\n}\n\nfunction hiddenDetailLine(hidden: number, theme: Theme): string {\n return theme.fg('dim', `… ${hidden} more · ctrl+o`);\n}\n\nfunction statusTimestamp(value: string): string {\n return value.replace('T', ' ').replace(/\\.\\d{3}Z$/, 'Z');\n}\n\nfunction workflowNameFromPath(path: string | undefined): string {\n if (!path) return 'workflow';\n const filename = path.split('/').at(-1) ?? path;\n return filename.replace(/\\.workflow\\.ya?ml$/u, '') || filename;\n}\n\nfunction callMetadata(toolName: WorkflowPiToolName, args: Record<string, unknown>, theme: Theme): string[] {\n const values: string[] = [];\n\n if (toolName === 'list_workflows') {\n const page = readNumber(args, 'page');\n const pageSize = readNumber(args, 'pageSize');\n if (page !== undefined) values.push(`page ${page}`);\n if (pageSize !== undefined) values.push(`${pageSize}/page`);\n if (readString(args, 'filter')) values.push('filtered');\n const tags = readStringArray(args, 'tags');\n if (tags.length > 0) values.push(`${tags.length} ${plural(tags.length, 'tag')}`);\n } else if (toolName === 'launch_workflow') {\n const job = readString(args, 'job');\n const runner = readString(args, 'runner');\n const workspace = readString(args, 'workspace');\n const inputCount = Object.keys(asRecord(args.inputs) ?? {}).length;\n const environmentCount = Object.keys(asRecord(args.env) ?? {}).length;\n if (workspace) values.push(`workspace ${workspace}`);\n if (job) values.push(`job ${job}`);\n if (runner) values.push(`runner ${runner}`);\n if (readBoolean(args, 'dryRun')) values.push('dry run');\n if (readString(args, 'prompt')) values.push('with prompt');\n if (inputCount > 0) values.push(`${inputCount} ${plural(inputCount, 'input')}`);\n if (environmentCount > 0) values.push(`${environmentCount} env`);\n } else {\n const expectedRunId = readString(args, 'expectedRunId');\n if (expectedRunId) values.push('generation checked');\n if (readString(args, 'reason')) values.push('with reason');\n if (readBoolean(args, 'dryRun')) values.push('dry run');\n if (readString(args, 'job')) values.push('job override');\n if (readString(args, 'runner')) values.push('runner override');\n }\n\n return values.map((value) => theme.fg('dim', value));\n}\n\nexport function renderWorkflowToolCall(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n theme: Theme,\n): Component {\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const metadata = callMetadata(toolName, args, theme);\n const purpose = [theme.bold(action), target ? theme.fg('muted', target) : undefined, ...metadata]\n .filter((part): part is string => !!part)\n .join(theme.fg('dim', ' · '));\n return new DoomToolCall(renderToolHeading('workflow', purpose, theme));\n}\n\nfunction parseCatalog(payload: JsonRecord | undefined): WorkflowCatalog | undefined {\n if (!Array.isArray(payload?.workflows)) return undefined;\n\n const workflows = payload.workflows.flatMap((value) => {\n const record = asRecord(value);\n const path = readString(record, 'path');\n if (!record || !path) return [];\n return [\n {\n description: readString(record, 'description') ?? '',\n name: readString(record, 'name') ?? '',\n path,\n tags: readStringArray(record, 'tags'),\n },\n ];\n });\n const tags = Array.isArray(payload.tags)\n ? payload.tags.flatMap((value) => {\n const record = asRecord(value);\n const tag = readString(record, 'tag');\n const count = readNumber(record, 'count');\n return tag && count !== undefined ? [{ count, tag }] : [];\n })\n : [];\n\n return {\n directory: readString(payload, 'directory'),\n page: readNumber(payload, 'page') ?? 1,\n pageSize: readNumber(payload, 'pageSize') ?? workflows.length,\n tags,\n total: readNumber(payload, 'total') ?? workflows.length,\n totalPages: readNumber(payload, 'totalPages') ?? (workflows.length > 0 ? 1 : 0),\n workflows,\n };\n}\n\nfunction catalogItemLines(item: WorkflowCatalogItem, expanded: boolean, theme: Theme): string[] {\n const name = item.name || workflowNameFromPath(item.path);\n const lines = [`${theme.fg('accent', '›')} ${theme.bold(name)} ${theme.fg('muted', `· ${item.path}`)}`];\n if (!expanded) return lines;\n if (item.description) lines.push(` ${theme.fg('text', item.description)}`);\n if (item.tags.length > 0) lines.push(` ${labelValue('tags', item.tags.join(', '), theme)}`);\n return lines;\n}\n\nfunction renderCatalogResult(\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component | undefined {\n const catalog = parseCatalog(firstPayload(result));\n if (!catalog) return undefined;\n\n const pageLabel = `page ${catalog.page}/${catalog.totalPages}`;\n const summary = `${theme.fg(catalog.total > 0 ? 'success' : 'muted', catalog.total > 0 ? '✓' : '○')} ${theme.bold(\n `${catalog.total} ${plural(catalog.total, 'workflow')}`,\n )} ${theme.fg('dim', `· ${pageLabel}`)}`;\n const shown = options.expanded ? catalog.workflows : catalog.workflows.slice(0, COLLAPSED_CATALOG_ROWS);\n const lines = [summary, ...shown.flatMap((item) => catalogItemLines(item, options.expanded, theme))];\n const hidden = catalog.workflows.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n\n if (options.expanded) {\n if (catalog.tags.length > 0) {\n const tags = catalog.tags.map(({ count, tag }) => `${tag} (${count})`).join(', ');\n lines.push(labelValue('available tags', tags, theme));\n }\n if (catalog.directory) lines.push(labelValue('directory', catalog.directory, theme));\n lines.push(labelValue('page size', String(catalog.pageSize), theme));\n }\n\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction readWorkflowState(record: JsonRecord): WorkflowState {\n const stage = readString(record, 'effectiveStage') ?? readString(record, 'stage') ?? 'unknown';\n const outcome = readString(record, 'effectiveOutcome') ?? readString(record, 'outcome');\n const executionState = readString(record, 'executionState');\n\n if (outcome === 'interrupted') return { color: 'warning', glyph: '!', label: 'interrupted' };\n if (stage === 'error' || outcome === 'failed') return { color: 'error', glyph: '✗', label: 'failed' };\n if (stage === 'completed' && outcome === 'skipped') return { color: 'muted', glyph: '○', label: 'skipped' };\n if (stage === 'completed') return { color: 'success', glyph: '✓', label: 'completed' };\n if (executionState === 'paused') return { color: 'warning', glyph: 'Ⅱ', label: 'paused' };\n if (executionState === 'pause_requested') return { color: 'warning', glyph: '◐', label: 'pause requested' };\n if (executionState === 'resume_requested') return { color: 'warning', glyph: '◐', label: 'resume requested' };\n if (stage === 'running') return { color: 'accent', glyph: '◐', label: 'running' };\n return { color: 'muted', glyph: '○', label: stage };\n}\n\nfunction recordScope(record: JsonRecord): string {\n const runKey = readString(record, 'runKey') ?? 'unknown run';\n const workspace = readString(record, 'workspace');\n return workspace ? `${workspace}/${runKey}` : runKey;\n}\n\nfunction currentActivity(record: JsonRecord, theme: Theme): string | undefined {\n const cursor = asRecord(record.executionCursor);\n const job = readString(cursor, 'job') ?? readString(record, 'job');\n const step = readString(cursor, 'stepName');\n const phase = readString(cursor, 'phase');\n if (!job && !step && !phase) return undefined;\n const context = job ?? phase ?? 'workflow';\n return ` ${theme.fg('muted', `[${context}]`)} ${theme.fg('text', step ?? phase ?? 'running')}`;\n}\n\nfunction launcherLabel(record: JsonRecord): string | undefined {\n const launcher = asRecord(record.launcher);\n const type = readString(launcher, 'type');\n if (!type) return undefined;\n const identity = readString(launcher, 'sessionName') ?? readString(launcher, 'workspaceId');\n return identity ? `${type} ${identity}` : type;\n}\n\nfunction workflowRecordLines(record: JsonRecord, expanded: boolean, theme: Theme): string[] | undefined {\n const runKey = readString(record, 'runKey');\n const stage = readString(record, 'stage');\n if (!runKey || !stage) return undefined;\n\n const state = readWorkflowState(record);\n const displayName = readString(record, 'displayName') ?? runKey;\n const heading = `${theme.fg(state.color, state.glyph)} ${theme.bold(displayName)} ${theme.fg(\n 'dim',\n `· ${recordScope(record)} ·`,\n )} ${theme.fg(state.color, state.label)}`;\n const lines = [heading];\n const activity = currentActivity(record, theme);\n if (activity) lines.push(activity);\n\n const failure = readString(record, 'errorMessage') ?? readString(record, 'failureReason');\n if (failure) lines.push(`${theme.fg('error', 'error')} ${theme.fg('text', failure)}`);\n const staleReason = readString(record, 'staleReason');\n if (staleReason) lines.push(`${theme.fg('warning', 'stale')} ${theme.fg('text', staleReason)}`);\n\n const conciseMetadata = [\n readString(record, 'runner') ? `runner ${readString(record, 'runner')}` : undefined,\n readBoolean(record, 'dryRun') ? 'dry run' : undefined,\n readNumber(record, 'exitCode') !== undefined ? `exit ${readNumber(record, 'exitCode')}` : undefined,\n readString(record, 'startedAt') ? `started ${statusTimestamp(readString(record, 'startedAt') ?? '')}` : undefined,\n ].filter((value): value is string => !!value);\n if (conciseMetadata.length > 0) lines.push(theme.fg('dim', conciseMetadata.join(' · ')));\n\n if (!expanded) return lines;\n\n const detailedValues: Array<[string, string | undefined]> = [\n ['workflow', readString(record, 'workflowPath')],\n ['workflow id', readString(record, 'workflowId')],\n ['run id', readString(record, 'runId')],\n ['launcher', launcherLabel(record)],\n ['job', readString(record, 'job')],\n ['outcome', readString(record, 'effectiveOutcome') ?? readString(record, 'outcome')],\n [\n 'finished',\n readString(record, 'finishedAt') ? statusTimestamp(readString(record, 'finishedAt') ?? '') : undefined,\n ],\n ['pid', readNumber(record, 'pid')?.toString()],\n ['repair', readString(record, 'activeRepairId') ? `#${readString(record, 'activeRepairId')}` : undefined],\n ['worktree', readString(record, 'worktreeBranch') ?? readString(record, 'worktreePath')],\n ];\n for (const [label, value] of detailedValues) {\n if (value) lines.push(labelValue(label, value, theme));\n }\n return lines;\n}\n\nfunction renderStatusResult(\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component | undefined {\n const payload = firstPayload(result);\n if (!payload) return undefined;\n const lines = workflowRecordLines(payload, options.expanded, theme);\n return lines ? renderResult(lines, theme, options.expanded) : undefined;\n}\n\nfunction nestedRequestedAt(payload: JsonRecord | undefined): string | undefined {\n return readString(asRecord(payload?.request), 'requestedAt') ?? readString(payload, 'requestedAt');\n}\n\nfunction renderControlResult(\n action: string,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const payload = firstPayload(result);\n const scope = targetScope(args) ?? readString(payload, 'runKey') ?? 'workflow';\n const payloadWorkspace = readString(payload, 'workspace');\n const resolvedScope = payloadWorkspace && !scope.includes('/') ? `${payloadWorkspace}/${scope}` : scope;\n const lines = [\n `${theme.fg('warning', '◐')} ${theme.bold(`${action} requested`)} ${theme.fg('dim', `· ${resolvedScope}`)}`,\n ];\n const requestedAt = nestedRequestedAt(payload);\n if (requestedAt) lines.push(labelValue('requested', statusTimestamp(requestedAt), theme));\n if (options.expanded) lines.push(theme.fg('dim', 'verify the transition with workflow_run status'));\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction matchLine(text: string, pattern: RegExp): string | undefined {\n return text.match(pattern)?.[1]?.trim();\n}\n\nfunction launchSummary(\n text: string,\n args: Record<string, unknown>,\n): { name: string; scope: string; state: WorkflowState } {\n const startedName = matchLine(text, /^Started (.+) in workspace .+\\.$/mu);\n const workspace = matchLine(text, /^Started .+ in workspace (.+)\\.$/mu) ?? readString(args, 'workspace');\n const runKey = matchLine(text, /^Run key:\\s*(.+)$/mu);\n const path = readString(args, 'workflowPath');\n const name = startedName ?? workflowNameFromPath(path);\n const scope = runKey ? (workspace ? `${workspace}/${runKey}` : runKey) : (workspace ?? path ?? 'workflow');\n\n if (/Workflow completed successfully/iu.test(text)) {\n return { name, scope, state: { color: 'success', glyph: '✓', label: 'completed' } };\n }\n if (/Workflow skipped/iu.test(text)) {\n return { name, scope, state: { color: 'muted', glyph: '○', label: 'skipped' } };\n }\n if (/no run was registered/iu.test(text)) {\n return { name, scope, state: { color: 'warning', glyph: '◐', label: 'starting · run key pending' } };\n }\n return { name, scope, state: { color: 'accent', glyph: '◐', label: runKey ? 'started' : 'launch accepted' } };\n}\n\nfunction renderLaunchResult(\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const blocks = textBlocks(result);\n const text = blocks.join('\\n');\n const summary = launchSummary(text, args);\n const lines = [\n `${theme.fg(summary.state.color, summary.state.glyph)} ${theme.bold(summary.name)} ${theme.fg(\n 'dim',\n `· ${summary.scope} ·`,\n )} ${theme.fg(summary.state.color, summary.state.label)}`,\n ];\n const metadata = [\n readString(args, 'job') ? `job ${readString(args, 'job')}` : undefined,\n readString(args, 'runner') ? `runner ${readString(args, 'runner')}` : undefined,\n readBoolean(args, 'dryRun') ? 'dry run' : undefined,\n ].filter((value): value is string => !!value);\n if (metadata.length > 0) lines.push(theme.fg('dim', metadata.join(' · ')));\n if (options.expanded) {\n const details = detailLines(result).filter((line) => !line.startsWith('Next steps for the agent:'));\n if (details.length > 0) lines.push(...details);\n }\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction splitEvidence(text: string): EvidenceSection[] {\n const sections: EvidenceSection[] = [];\n let current: EvidenceSection | undefined;\n for (const line of text.split('\\n')) {\n const match = line.match(/^--- (.+) ---$/u);\n if (match?.[1]) {\n current = { body: [], name: match[1] };\n sections.push(current);\n continue;\n }\n current?.body.push(line);\n }\n return sections;\n}\n\nfunction recoveryRecord(sections: EvidenceSection[]): JsonRecord | undefined {\n const runSection = sections.find((section) => section.name === 'run.json');\n return parseJsonRecord(runSection?.body.join('\\n'));\n}\n\nfunction renderRecoveryEvidenceResult(\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const text = textBlocks(result).join('\\n');\n const sections = splitEvidence(text);\n const record = recoveryRecord(sections);\n const recordLines = record ? workflowRecordLines(record, options.expanded, theme) : undefined;\n const evidenceSections = sections.filter((section) => section.name !== 'run.json');\n const scope = targetScope(args) ?? 'workflow';\n const lines = recordLines ?? [\n `${theme.fg('success', '✓')} ${theme.bold('recovery evidence')} ${theme.fg('dim', `· ${scope}`)}`,\n ];\n const names = evidenceSections.map((section) => section.name);\n lines.push(\n names.length > 0\n ? labelValue('evidence', names.join(', '), theme)\n : theme.fg('muted', 'no durable evidence files recorded'),\n );\n\n if (options.expanded) {\n for (const section of evidenceSections) {\n lines.push(theme.bold(section.name), ...section.body.filter((line) => line.length > 0));\n }\n }\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderSimpleActionResult(\n action: string,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const scope = targetScope(args) ?? 'workflow';\n const actionLabel: Record<string, string> = {\n follow: 'following output',\n open: 'launcher opened',\n recover: readBoolean(args, 'dryRun') ? 'recovery dry run finished' : 'recovery accepted',\n tail: 'launcher output checked',\n };\n const state: WorkflowState =\n action === 'recover'\n ? { color: 'accent', glyph: '◐', label: actionLabel[action] ?? action }\n : { color: 'success', glyph: '✓', label: actionLabel[action] ?? action };\n const lines = [`${theme.fg(state.color, state.glyph)} ${theme.bold(state.label)} ${theme.fg('dim', `· ${scope}`)}`];\n const details = detailLines(result);\n if (action === 'tail') {\n const stageLine = details.find((line) => line.startsWith('Stage:'));\n const workflowLine = details.find((line) => line.startsWith('Workflow:'));\n if (workflowLine) lines.push(workflowLine);\n if (stageLine) lines.push(stageLine);\n }\n if (options.expanded && details.length > 0) lines.push(...details);\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderFailure(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const summary = `${theme.fg('error', '✗')} ${theme.bold(`${action} failed`)}${\n target ? theme.fg('dim', ` · ${target}`) : ''\n }`;\n const details = detailLines(result);\n const shown = options.expanded ? details : details.slice(0, COLLAPSED_DETAIL_LINES);\n const lines = [summary, ...(shown.length > 0 ? shown : [theme.fg('error', 'Unknown workflow error')])];\n const hidden = details.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderPartial(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const details = detailLines(result);\n const shown = details.slice(-PARTIAL_DETAIL_LINES);\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const summary = `${theme.fg('accent', '◐')} ${theme.bold(action)}${target ? theme.fg('dim', ` · ${target}`) : ''}`;\n const hidden = details.length - shown.length;\n const lines = [summary];\n if (hidden > 0) lines.push(theme.fg('dim', `… ${hidden} earlier`));\n lines.push(...shown);\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderFallback(result: WorkflowToolResultLike, options: WorkflowToolRenderOptions, theme: Theme): Component {\n const details = detailLines(result);\n if (details.length === 0) return renderResult([theme.fg('muted', 'No workflow output.')], theme, options.expanded);\n const shown = options.expanded ? details : details.slice(0, COLLAPSED_DETAIL_LINES);\n const lines = [...shown];\n const hidden = details.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n return renderResult(lines, theme, options.expanded);\n}\n\nexport function renderWorkflowToolResult(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n if (options.isPartial) return renderPartial(toolName, args, result, options, theme);\n if (options.isError === true || result.isError === true) {\n return renderFailure(toolName, args, result, options, theme);\n }\n\n if (toolName === 'list_workflows') {\n return renderCatalogResult(result, options, theme) ?? renderFallback(result, options, theme);\n }\n if (toolName === 'launch_workflow') return renderLaunchResult(args, result, options, theme);\n\n const action = readString(args, 'action');\n if (action === 'status') {\n return renderStatusResult(result, options, theme) ?? renderFallback(result, options, theme);\n }\n if (action === 'pause' || action === 'resume' || action === 'stop') {\n return renderControlResult(action, args, result, options, theme);\n }\n if (action === 'recovery-evidence') {\n return renderRecoveryEvidenceResult(args, result, options, theme);\n }\n if (action === 'follow' || action === 'open' || action === 'recover' || action === 'tail') {\n return renderSimpleActionResult(action, args, result, options, theme);\n }\n return renderFallback(result, options, theme);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-workflow",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.14",
|
|
4
4
|
"description": "Doompi workflow execution and monitoring integration",
|
|
5
5
|
"homepage": "https://agimon.ai",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,9 +38,10 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
40
40
|
"zod": "4.4.3",
|
|
41
|
-
"@agimon-ai/doompi-
|
|
42
|
-
"@agimon-ai/workflow-mcp": "0.23.
|
|
43
|
-
"@agimon-ai/doompi-
|
|
41
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.14",
|
|
42
|
+
"@agimon-ai/workflow-mcp": "0.23.4",
|
|
43
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.14",
|
|
44
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.14"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@earendil-works/pi-coding-agent": "0.84.1",
|
|
@@ -49,8 +50,7 @@
|
|
|
49
50
|
"@vitest/coverage-v8": "4.1.10",
|
|
50
51
|
"tsdown": "0.22.14",
|
|
51
52
|
"typescript": "6.0.3",
|
|
52
|
-
"vitest": "4.1.10"
|
|
53
|
-
"@agimon-ai/doompi-ui": "0.0.1-alpha.13"
|
|
53
|
+
"vitest": "4.1.10"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@earendil-works/pi-coding-agent": "0.84.1",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=[`◐`,`●`,`◑`],t=[`accent`,`mdCode`,`toolDiffAdded`];function n(t){let n=0;for(let e of t)n=Math.imul(n,31)+(e.codePointAt(0)??0)>>>0;return n%e.length}function r(e){if(e===void 0||!Number.isFinite(e))return t[0];let n=Math.floor(Math.max(0,Math.min(1,e))*t.length);return t[Math.min(n,t.length-1)]}function i(t,i){return t.state===`paused`?{glyph:`■`,color:`warning`}:t.state===`starting`?{glyph:`○`,color:`dim`}:{glyph:e[(i+n(t.key))%e.length],color:r(t.progress)}}function a(e){let t=e.slice(0,5),n=e.length-t.length,r=n>0?`+${n}`:``;return{text:`${t.map(e=>e.glyph).join(``)}${r}`,segments:[...t.map(e=>({text:e.glyph,...e.color?{color:e.color}:{}})),...r?[{text:r,color:`dim`}]:[]]}}function o(e,t,n){let r=[{text:n,color:`accent`}],i=n;if(t.length>0){let e=a(t);i+=e.text,r.push(...e.segments)}if(e.failed>0){let t=`${i.endsWith(n)?``:` `}⚠${e.failed}`;i+=t,r.push({text:t,color:`warning`})}return{text:i,segments:r}}function s(e,t=0){if(e.runs.length===0&&e.failed===0)return;let n=e.runs.map(e=>i(e,t)),r=o(e,n,`workflow: `),a=o(e,n,`W `),s=e.runs.some(e=>e.state===`running`);return{text:r.text,footer:{fullText:r.text,compactText:a.text,fullSegments:r.segments,compactSegments:a.segments},fingerprint:`${e.runs.map((e,t)=>`${e.key}:${e.state}:${n[t]?.color}`).join(`|`)}:${e.failed}:${s?t:`static`}`,pulsing:s}}exports.WORKFLOW_PULSE_FRAMES=e,exports.workflowFooterStatus=s;
|
|
2
|
-
//# sourceMappingURL=workflowFooterStatus.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowFooterStatus.cjs","names":[],"sources":["../../../src/tui/workflow/workflowFooterStatus.ts"],"sourcesContent":["/**\n * The workflow footer contribution: one dot per run this session has going.\n *\n * Replaces the full-width progress row that used to sit above the editor. That\n * row cost a terminal line per repaint and grew with the run count; a footer\n * group costs nothing when idle and stays one glance wide however many runs are\n * in flight, which is the same trade doom-team's agent dots make.\n *\n * Pure: takes already-summarised runs and a frame number, returns text and\n * segments. No registry, no TUI, no timer.\n */\n\nimport type { DoomFooterContributionValue, FooterTextSegment } from '@agimon-ai/doompi-extension-contracts/footer';\n\n/** Same three-phase pulse doom-team uses, so every doom footer animates alike. */\nexport const WORKFLOW_PULSE_FRAMES = ['◐', '●', '◑'] as const;\n\n/** House `name: state` shape, matching plan mode and the doom loop. */\nconst FULL_PREFIX = 'workflow: ';\nconst COMPACT_PREFIX = 'W ';\n/**\n * Dots past this collapse into `+N`. The footer is one line shared with every\n * other extension, so a group that grows with the run count is the group that\n * pushes everyone else off the end.\n */\nconst MAX_DOTS = 5;\nconst PAUSED_GLYPH = '■';\nconst STARTING_GLYPH = '○';\nconst FAILED_GLYPH = '⚠';\n\n/**\n * Cold-to-done ramp for a working run, walked by job completion.\n *\n * `warning` is deliberately absent: it already means paused and failed, and a\n * two-thirds-done run must not read as a run needing attention. Blue, cyan and\n * light-green in the doom theme, all members of the footer colour union.\n */\nconst PROGRESS_COLORS = ['accent', 'mdCode', 'toolDiffAdded'] as const satisfies readonly FooterTextSegment['color'][];\n\nexport type WorkflowDotState = 'running' | 'paused' | 'starting';\n\n/** One run's contribution to the row: its identity and what it is doing. */\nexport interface WorkflowFooterRun {\n key: string;\n state: WorkflowDotState;\n /**\n * Jobs finished over jobs total, 0..1.\n *\n * Optional because the position only rides on job-start events: a run whose\n * engine never stamped one keeps the flat first-bucket colour rather than\n * claiming a progress it cannot know.\n */\n progress?: number;\n}\n\nexport interface WorkflowFooterInput {\n runs: readonly WorkflowFooterRun[];\n failed: number;\n}\n\nexport interface WorkflowFooterStatus {\n text: string;\n footer: DoomFooterContributionValue;\n /** Changes exactly when the rendered row would, so repaints can be skipped. */\n fingerprint: string;\n /** True while at least one run is animating, which is what the ticker keys off. */\n pulsing: boolean;\n}\n\ninterface Dot {\n glyph: string;\n color: FooterTextSegment['color'];\n}\n\n/**\n * Per-run phase offset so a fleet of runs does not blink in lockstep, which\n * reads as one flashing indicator rather than several working ones.\n */\nfunction stableOffset(value: string): number {\n let hash = 0;\n for (const character of value) hash = (Math.imul(hash, 31) + (character.codePointAt(0) ?? 0)) >>> 0;\n return hash % WORKFLOW_PULSE_FRAMES.length;\n}\n\n/**\n * Where a run sits on the ramp.\n *\n * Bucketed rather than interpolated because the ramp has three colours and a\n * dot has one: a reader is being asked \"early, middle or nearly done\", which is\n * the most a single glyph can honestly answer.\n */\nfunction progressColor(progress: number | undefined): Dot['color'] {\n if (progress === undefined || !Number.isFinite(progress)) return PROGRESS_COLORS[0];\n const bucket = Math.floor(Math.max(0, Math.min(1, progress)) * PROGRESS_COLORS.length);\n return PROGRESS_COLORS[Math.min(bucket, PROGRESS_COLORS.length - 1)];\n}\n\nfunction dotFor(run: WorkflowFooterRun, frame: number): Dot {\n if (run.state === 'paused') return { glyph: PAUSED_GLYPH, color: 'warning' };\n if (run.state === 'starting') return { glyph: STARTING_GLYPH, color: 'dim' };\n return {\n glyph: WORKFLOW_PULSE_FRAMES[(frame + stableOffset(run.key)) % WORKFLOW_PULSE_FRAMES.length],\n color: progressColor(run.progress),\n };\n}\n\n/** Dots up to the cap, then the count of everything the cap hid. */\nfunction projectDots(dots: readonly Dot[]): { text: string; segments: FooterTextSegment[] } {\n const visible = dots.slice(0, MAX_DOTS);\n const hidden = dots.length - visible.length;\n const suffix = hidden > 0 ? `+${hidden}` : '';\n return {\n text: `${visible.map((dot) => dot.glyph).join('')}${suffix}`,\n segments: [\n ...visible.map((dot) => ({ text: dot.glyph, ...(dot.color ? { color: dot.color } : {}) })),\n ...(suffix ? [{ text: suffix, color: 'dim' as const }] : []),\n ],\n };\n}\n\nfunction project(\n input: WorkflowFooterInput,\n dots: readonly Dot[],\n prefix: string,\n): {\n text: string;\n segments: FooterTextSegment[];\n} {\n const segments: FooterTextSegment[] = [{ text: prefix, color: 'accent' }];\n let text = prefix;\n\n if (dots.length > 0) {\n const projected = projectDots(dots);\n text += projected.text;\n segments.push(...projected.segments);\n }\n\n if (input.failed > 0) {\n const failed = `${text.endsWith(prefix) ? '' : ' '}${FAILED_GLYPH}${input.failed}`;\n text += failed;\n segments.push({ text: failed, color: 'warning' });\n }\n return { text, segments };\n}\n\n/**\n * Render one animation frame, or nothing at all.\n *\n * An absent value removes the group entirely rather than leaving an empty\n * `workflow:` label behind on an idle session.\n */\nexport function workflowFooterStatus(input: WorkflowFooterInput, frame = 0): WorkflowFooterStatus | undefined {\n if (input.runs.length === 0 && input.failed === 0) return undefined;\n\n const dots = input.runs.map((run) => dotFor(run, frame));\n const full = project(input, dots, FULL_PREFIX);\n const compact = project(input, dots, COMPACT_PREFIX);\n const pulsing = input.runs.some((run) => run.state === 'running');\n return {\n text: full.text,\n footer: {\n fullText: full.text,\n compactText: compact.text,\n fullSegments: full.segments,\n compactSegments: compact.segments,\n },\n // Keyed on the resolved colour, not on the raw progress: a run inching\n // along inside one bucket changes nothing on screen and must not repaint.\n fingerprint: `${input.runs.map((run, index) => `${run.key}:${run.state}:${dots[index]?.color}`).join('|')}:${input.failed}:${pulsing ? frame : 'static'}`,\n pulsing,\n };\n}\n"],"mappings":"AAeA,MAAa,EAAwB,CAAC,IAAK,IAAK,GAAG,EAsB7C,EAAkB,CAAC,SAAU,SAAU,eAAe,EAyC5D,SAAS,EAAa,EAAuB,CAC3C,IAAI,EAAO,EACX,IAAK,IAAM,KAAa,EAAO,EAAQ,KAAK,KAAK,EAAM,EAAE,GAAK,EAAU,YAAY,CAAC,GAAK,KAAQ,EAClG,OAAO,EAAO,EAAsB,MACtC,CASA,SAAS,EAAc,EAA4C,CACjE,GAAI,IAAa,IAAA,IAAa,CAAC,OAAO,SAAS,CAAQ,EAAG,OAAO,EAAgB,GACjF,IAAM,EAAS,KAAK,MAAM,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,CAAQ,CAAC,EAAI,EAAgB,MAAM,EACrF,OAAO,EAAgB,KAAK,IAAI,EAAQ,EAAgB,OAAS,CAAC,EACpE,CAEA,SAAS,EAAO,EAAwB,EAAoB,CAG1D,OAFI,EAAI,QAAU,SAAiB,CAAE,MAAO,IAAc,MAAO,SAAU,EACvE,EAAI,QAAU,WAAmB,CAAE,MAAO,IAAgB,MAAO,KAAM,EACpE,CACL,MAAO,GAAuB,EAAQ,EAAa,EAAI,GAAG,GAAK,EAAsB,QACrF,MAAO,EAAc,EAAI,QAAQ,CACnC,CACF,CAGA,SAAS,EAAY,EAAuE,CAC1F,IAAM,EAAU,EAAK,MAAM,EAAG,CAAQ,EAChC,EAAS,EAAK,OAAS,EAAQ,OAC/B,EAAS,EAAS,EAAI,IAAI,IAAW,GAC3C,MAAO,CACL,KAAM,GAAG,EAAQ,IAAK,GAAQ,EAAI,KAAK,CAAC,CAAC,KAAK,EAAE,IAAI,IACpD,SAAU,CACR,GAAG,EAAQ,IAAK,IAAS,CAAE,KAAM,EAAI,MAAO,GAAI,EAAI,MAAQ,CAAE,MAAO,EAAI,KAAM,EAAI,CAAC,CAAG,EAAE,EACzF,GAAI,EAAS,CAAC,CAAE,KAAM,EAAQ,MAAO,KAAe,CAAC,EAAI,CAAC,CAC5D,CACF,CACF,CAEA,SAAS,EACP,EACA,EACA,EAIA,CACA,IAAM,EAAgC,CAAC,CAAE,KAAM,EAAQ,MAAO,QAAS,CAAC,EACpE,EAAO,EAEX,GAAI,EAAK,OAAS,EAAG,CACnB,IAAM,EAAY,EAAY,CAAI,EAClC,GAAQ,EAAU,KAClB,EAAS,KAAK,GAAG,EAAU,QAAQ,CACrC,CAEA,GAAI,EAAM,OAAS,EAAG,CACpB,IAAM,EAAS,GAAG,EAAK,SAAS,CAAM,EAAI,GAAK,OAAqB,EAAM,SAC1E,GAAQ,EACR,EAAS,KAAK,CAAE,KAAM,EAAQ,MAAO,SAAU,CAAC,CAClD,CACA,MAAO,CAAE,OAAM,UAAS,CAC1B,CAQA,SAAgB,EAAqB,EAA4B,EAAQ,EAAqC,CAC5G,GAAI,EAAM,KAAK,SAAW,GAAK,EAAM,SAAW,EAAG,OAEnD,IAAM,EAAO,EAAM,KAAK,IAAK,GAAQ,EAAO,EAAK,CAAK,CAAC,EACjD,EAAO,EAAQ,EAAO,EAAM,YAAW,EACvC,EAAU,EAAQ,EAAO,EAAM,IAAc,EAC7C,EAAU,EAAM,KAAK,KAAM,GAAQ,EAAI,QAAU,SAAS,EAChE,MAAO,CACL,KAAM,EAAK,KACX,OAAQ,CACN,SAAU,EAAK,KACf,YAAa,EAAQ,KACrB,aAAc,EAAK,SACnB,gBAAiB,EAAQ,QAC3B,EAGA,YAAa,GAAG,EAAM,KAAK,KAAK,EAAK,IAAU,GAAG,EAAI,IAAI,GAAG,EAAI,MAAM,GAAG,EAAK,EAAM,EAAE,OAAO,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,EAAM,OAAO,GAAG,EAAU,EAAQ,WAC/I,SACF,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=[`◐`,`●`,`◑`],t=[`accent`,`mdCode`,`toolDiffAdded`];function n(t){let n=0;for(let e of t)n=Math.imul(n,31)+(e.codePointAt(0)??0)>>>0;return n%e.length}function r(e){if(e===void 0||!Number.isFinite(e))return t[0];let n=Math.floor(Math.max(0,Math.min(1,e))*t.length);return t[Math.min(n,t.length-1)]}function i(t,i){return t.state===`paused`?{glyph:`■`,color:`warning`}:t.state===`starting`?{glyph:`○`,color:`dim`}:{glyph:e[(i+n(t.key))%e.length],color:r(t.progress)}}function a(e){let t=e.slice(0,5),n=e.length-t.length,r=n>0?`+${n}`:``;return{text:`${t.map(e=>e.glyph).join(``)}${r}`,segments:[...t.map(e=>({text:e.glyph,...e.color?{color:e.color}:{}})),...r?[{text:r,color:`dim`}]:[]]}}function o(e,t,n){let r=[{text:n,color:`accent`}],i=n;if(t.length>0){let e=a(t);i+=e.text,r.push(...e.segments)}if(e.failed>0){let t=`${i.endsWith(n)?``:` `}⚠${e.failed}`;i+=t,r.push({text:t,color:`warning`})}return{text:i,segments:r}}function s(e,t=0){if(e.runs.length===0&&e.failed===0)return;let n=e.runs.map(e=>i(e,t)),r=o(e,n,`workflow: `),a=o(e,n,`W `),s=e.runs.some(e=>e.state===`running`);return{text:r.text,footer:{fullText:r.text,compactText:a.text,fullSegments:r.segments,compactSegments:a.segments},fingerprint:`${e.runs.map((e,t)=>`${e.key}:${e.state}:${n[t]?.color}`).join(`|`)}:${e.failed}:${s?t:`static`}`,pulsing:s}}export{e as WORKFLOW_PULSE_FRAMES,s as workflowFooterStatus};
|
|
2
|
-
//# sourceMappingURL=workflowFooterStatus.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowFooterStatus.mjs","names":[],"sources":["../../../src/tui/workflow/workflowFooterStatus.ts"],"sourcesContent":["/**\n * The workflow footer contribution: one dot per run this session has going.\n *\n * Replaces the full-width progress row that used to sit above the editor. That\n * row cost a terminal line per repaint and grew with the run count; a footer\n * group costs nothing when idle and stays one glance wide however many runs are\n * in flight, which is the same trade doom-team's agent dots make.\n *\n * Pure: takes already-summarised runs and a frame number, returns text and\n * segments. No registry, no TUI, no timer.\n */\n\nimport type { DoomFooterContributionValue, FooterTextSegment } from '@agimon-ai/doompi-extension-contracts/footer';\n\n/** Same three-phase pulse doom-team uses, so every doom footer animates alike. */\nexport const WORKFLOW_PULSE_FRAMES = ['◐', '●', '◑'] as const;\n\n/** House `name: state` shape, matching plan mode and the doom loop. */\nconst FULL_PREFIX = 'workflow: ';\nconst COMPACT_PREFIX = 'W ';\n/**\n * Dots past this collapse into `+N`. The footer is one line shared with every\n * other extension, so a group that grows with the run count is the group that\n * pushes everyone else off the end.\n */\nconst MAX_DOTS = 5;\nconst PAUSED_GLYPH = '■';\nconst STARTING_GLYPH = '○';\nconst FAILED_GLYPH = '⚠';\n\n/**\n * Cold-to-done ramp for a working run, walked by job completion.\n *\n * `warning` is deliberately absent: it already means paused and failed, and a\n * two-thirds-done run must not read as a run needing attention. Blue, cyan and\n * light-green in the doom theme, all members of the footer colour union.\n */\nconst PROGRESS_COLORS = ['accent', 'mdCode', 'toolDiffAdded'] as const satisfies readonly FooterTextSegment['color'][];\n\nexport type WorkflowDotState = 'running' | 'paused' | 'starting';\n\n/** One run's contribution to the row: its identity and what it is doing. */\nexport interface WorkflowFooterRun {\n key: string;\n state: WorkflowDotState;\n /**\n * Jobs finished over jobs total, 0..1.\n *\n * Optional because the position only rides on job-start events: a run whose\n * engine never stamped one keeps the flat first-bucket colour rather than\n * claiming a progress it cannot know.\n */\n progress?: number;\n}\n\nexport interface WorkflowFooterInput {\n runs: readonly WorkflowFooterRun[];\n failed: number;\n}\n\nexport interface WorkflowFooterStatus {\n text: string;\n footer: DoomFooterContributionValue;\n /** Changes exactly when the rendered row would, so repaints can be skipped. */\n fingerprint: string;\n /** True while at least one run is animating, which is what the ticker keys off. */\n pulsing: boolean;\n}\n\ninterface Dot {\n glyph: string;\n color: FooterTextSegment['color'];\n}\n\n/**\n * Per-run phase offset so a fleet of runs does not blink in lockstep, which\n * reads as one flashing indicator rather than several working ones.\n */\nfunction stableOffset(value: string): number {\n let hash = 0;\n for (const character of value) hash = (Math.imul(hash, 31) + (character.codePointAt(0) ?? 0)) >>> 0;\n return hash % WORKFLOW_PULSE_FRAMES.length;\n}\n\n/**\n * Where a run sits on the ramp.\n *\n * Bucketed rather than interpolated because the ramp has three colours and a\n * dot has one: a reader is being asked \"early, middle or nearly done\", which is\n * the most a single glyph can honestly answer.\n */\nfunction progressColor(progress: number | undefined): Dot['color'] {\n if (progress === undefined || !Number.isFinite(progress)) return PROGRESS_COLORS[0];\n const bucket = Math.floor(Math.max(0, Math.min(1, progress)) * PROGRESS_COLORS.length);\n return PROGRESS_COLORS[Math.min(bucket, PROGRESS_COLORS.length - 1)];\n}\n\nfunction dotFor(run: WorkflowFooterRun, frame: number): Dot {\n if (run.state === 'paused') return { glyph: PAUSED_GLYPH, color: 'warning' };\n if (run.state === 'starting') return { glyph: STARTING_GLYPH, color: 'dim' };\n return {\n glyph: WORKFLOW_PULSE_FRAMES[(frame + stableOffset(run.key)) % WORKFLOW_PULSE_FRAMES.length],\n color: progressColor(run.progress),\n };\n}\n\n/** Dots up to the cap, then the count of everything the cap hid. */\nfunction projectDots(dots: readonly Dot[]): { text: string; segments: FooterTextSegment[] } {\n const visible = dots.slice(0, MAX_DOTS);\n const hidden = dots.length - visible.length;\n const suffix = hidden > 0 ? `+${hidden}` : '';\n return {\n text: `${visible.map((dot) => dot.glyph).join('')}${suffix}`,\n segments: [\n ...visible.map((dot) => ({ text: dot.glyph, ...(dot.color ? { color: dot.color } : {}) })),\n ...(suffix ? [{ text: suffix, color: 'dim' as const }] : []),\n ],\n };\n}\n\nfunction project(\n input: WorkflowFooterInput,\n dots: readonly Dot[],\n prefix: string,\n): {\n text: string;\n segments: FooterTextSegment[];\n} {\n const segments: FooterTextSegment[] = [{ text: prefix, color: 'accent' }];\n let text = prefix;\n\n if (dots.length > 0) {\n const projected = projectDots(dots);\n text += projected.text;\n segments.push(...projected.segments);\n }\n\n if (input.failed > 0) {\n const failed = `${text.endsWith(prefix) ? '' : ' '}${FAILED_GLYPH}${input.failed}`;\n text += failed;\n segments.push({ text: failed, color: 'warning' });\n }\n return { text, segments };\n}\n\n/**\n * Render one animation frame, or nothing at all.\n *\n * An absent value removes the group entirely rather than leaving an empty\n * `workflow:` label behind on an idle session.\n */\nexport function workflowFooterStatus(input: WorkflowFooterInput, frame = 0): WorkflowFooterStatus | undefined {\n if (input.runs.length === 0 && input.failed === 0) return undefined;\n\n const dots = input.runs.map((run) => dotFor(run, frame));\n const full = project(input, dots, FULL_PREFIX);\n const compact = project(input, dots, COMPACT_PREFIX);\n const pulsing = input.runs.some((run) => run.state === 'running');\n return {\n text: full.text,\n footer: {\n fullText: full.text,\n compactText: compact.text,\n fullSegments: full.segments,\n compactSegments: compact.segments,\n },\n // Keyed on the resolved colour, not on the raw progress: a run inching\n // along inside one bucket changes nothing on screen and must not repaint.\n fingerprint: `${input.runs.map((run, index) => `${run.key}:${run.state}:${dots[index]?.color}`).join('|')}:${input.failed}:${pulsing ? frame : 'static'}`,\n pulsing,\n };\n}\n"],"mappings":"AAeA,MAAa,EAAwB,CAAC,IAAK,IAAK,GAAG,EAsB7C,EAAkB,CAAC,SAAU,SAAU,eAAe,EAyC5D,SAAS,EAAa,EAAuB,CAC3C,IAAI,EAAO,EACX,IAAK,IAAM,KAAa,EAAO,EAAQ,KAAK,KAAK,EAAM,EAAE,GAAK,EAAU,YAAY,CAAC,GAAK,KAAQ,EAClG,OAAO,EAAO,EAAsB,MACtC,CASA,SAAS,EAAc,EAA4C,CACjE,GAAI,IAAa,IAAA,IAAa,CAAC,OAAO,SAAS,CAAQ,EAAG,OAAO,EAAgB,GACjF,IAAM,EAAS,KAAK,MAAM,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,CAAQ,CAAC,EAAI,EAAgB,MAAM,EACrF,OAAO,EAAgB,KAAK,IAAI,EAAQ,EAAgB,OAAS,CAAC,EACpE,CAEA,SAAS,EAAO,EAAwB,EAAoB,CAG1D,OAFI,EAAI,QAAU,SAAiB,CAAE,MAAO,IAAc,MAAO,SAAU,EACvE,EAAI,QAAU,WAAmB,CAAE,MAAO,IAAgB,MAAO,KAAM,EACpE,CACL,MAAO,GAAuB,EAAQ,EAAa,EAAI,GAAG,GAAK,EAAsB,QACrF,MAAO,EAAc,EAAI,QAAQ,CACnC,CACF,CAGA,SAAS,EAAY,EAAuE,CAC1F,IAAM,EAAU,EAAK,MAAM,EAAG,CAAQ,EAChC,EAAS,EAAK,OAAS,EAAQ,OAC/B,EAAS,EAAS,EAAI,IAAI,IAAW,GAC3C,MAAO,CACL,KAAM,GAAG,EAAQ,IAAK,GAAQ,EAAI,KAAK,CAAC,CAAC,KAAK,EAAE,IAAI,IACpD,SAAU,CACR,GAAG,EAAQ,IAAK,IAAS,CAAE,KAAM,EAAI,MAAO,GAAI,EAAI,MAAQ,CAAE,MAAO,EAAI,KAAM,EAAI,CAAC,CAAG,EAAE,EACzF,GAAI,EAAS,CAAC,CAAE,KAAM,EAAQ,MAAO,KAAe,CAAC,EAAI,CAAC,CAC5D,CACF,CACF,CAEA,SAAS,EACP,EACA,EACA,EAIA,CACA,IAAM,EAAgC,CAAC,CAAE,KAAM,EAAQ,MAAO,QAAS,CAAC,EACpE,EAAO,EAEX,GAAI,EAAK,OAAS,EAAG,CACnB,IAAM,EAAY,EAAY,CAAI,EAClC,GAAQ,EAAU,KAClB,EAAS,KAAK,GAAG,EAAU,QAAQ,CACrC,CAEA,GAAI,EAAM,OAAS,EAAG,CACpB,IAAM,EAAS,GAAG,EAAK,SAAS,CAAM,EAAI,GAAK,OAAqB,EAAM,SAC1E,GAAQ,EACR,EAAS,KAAK,CAAE,KAAM,EAAQ,MAAO,SAAU,CAAC,CAClD,CACA,MAAO,CAAE,OAAM,UAAS,CAC1B,CAQA,SAAgB,EAAqB,EAA4B,EAAQ,EAAqC,CAC5G,GAAI,EAAM,KAAK,SAAW,GAAK,EAAM,SAAW,EAAG,OAEnD,IAAM,EAAO,EAAM,KAAK,IAAK,GAAQ,EAAO,EAAK,CAAK,CAAC,EACjD,EAAO,EAAQ,EAAO,EAAM,YAAW,EACvC,EAAU,EAAQ,EAAO,EAAM,IAAc,EAC7C,EAAU,EAAM,KAAK,KAAM,GAAQ,EAAI,QAAU,SAAS,EAChE,MAAO,CACL,KAAM,EAAK,KACX,OAAQ,CACN,SAAU,EAAK,KACf,YAAa,EAAQ,KACrB,aAAc,EAAK,SACnB,gBAAiB,EAAQ,QAC3B,EAGA,YAAa,GAAG,EAAM,KAAK,KAAK,EAAK,IAAU,GAAG,EAAI,IAAI,GAAG,EAAI,MAAM,GAAG,EAAK,EAAM,EAAE,OAAO,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,EAAM,OAAO,GAAG,EAAU,EAAQ,WAC/I,SACF,CACF"}
|