@agimon-ai/doompi-task 0.0.1-alpha.45 → 0.0.1-alpha.48

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.
@@ -1,3 +1,3 @@
1
- const e=require("../../schemas/task.cjs"),t=require("../../types/telemetry.cjs"),n=require("../../services/delegation/manager.cjs"),r=require("../../commands/index.cjs"),i=require("../../types/config.cjs"),a=require("../../commands/task/taskTool.cjs"),o=require("../../services/contextContribution.cjs"),s=require("../../services/narration/taskNarration.cjs"),c=require("../store/paths.cjs"),l=require("../store/taskStore.cjs"),u=require("../../tui/taskOverlay2.cjs"),d=require("../node/delegationPlatform.cjs"),f=require("../telemetry/logSinkTelemetry.cjs");let p=require("@earendil-works/pi-tui"),m=require("@agimon-ai/doompi-extension-contracts/delegation"),h=require("@agimon-ai/doompi-extension-contracts/background-work"),g=require("@agimon-ai/doompi-extension-contracts/cordis-host"),_=require("@agimon-ai/doompi-extension-contracts/context-contributions"),v=require("@agimon-ai/doompi-extension-contracts/narration"),y=require("@agimon-ai/doompi-extension-contracts/readiness"),b=require("@agimon-ai/doompi-extension-contracts/ui-hub");const x=o.TASK_CONTEXT_CONTRIBUTION_SOURCE;function S(g,S){let C=f.createTaskErrorReporter(),w=f.toFailureReporter(C),T=!0,E=0,D,O,k,A,j,M,N=!1,P,F,I,L=e=>{C.recordWarning(`doom_task.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-task could not record a narration failure: ${String(e)}`)})},R={narrate(e){let t=D,n=(0,v.createNarrationRequest)(e);if(!(!t||!n))try{Promise.resolve(t.request(n)).catch(L)}catch(e){L(e)}}};g.inject([v.DOOM_NARRATION_SERVICE],e=>{let t=(0,v.requireDoomNarrationService)(e);return D=t,()=>{D===t&&(D=void 0)}});let z=async(e,t)=>{t?.throwIfAborted();let n=I;if(!n)throw Error(`doom-task session initialization has not started`);if(n.sessionManager!==e.sessionManager)throw Error(`doom-task readiness belongs to a stale Pi session`);if(await n.operation,t?.throwIfAborted(),!T||n!==I||!F)throw Error(`doom-task readiness belongs to a stale extension generation`)},B=()=>{j?.(),j=void 0,N=!1,M?.update(),k?.reset(),A?.dispose(),F=void 0};g.effect(()=>async()=>{T=!1,E+=1;let e=I?.operation;I=void 0;try{e&&await Promise.allSettled([e]),B(),k?.dispose(),P?.(),P=void 0,O?.dispose()}finally{await C.shutdown()}},x);let V=new l.TaskStore({report:w,onCommitted:(e,t)=>s.narrateTaskCommit(R,e,t)});O=V,g.inject([_.DOOM_CONTEXT_CONTRIBUTIONS_SERVICE],e=>{let t=(0,_.requireDoomContextContributions)(e).register(o.createTaskContextContribution(()=>V.snapshot.tasks));return()=>t.dispose()});let H=()=>{T&&(A?.update(),M?.update())},U=new n.DelegationManager({store:V,cwd:process.cwd(),platform:d.createNodeDelegationPlatform(),notify:(e,t)=>S.sendMessage(e,t),getSessionId:()=>F,onChange:H,runTimeoutMs:i.getDelegationTimeoutMs(),report:w,onNotifyError:(e,t)=>{C.recordNotificationError(e,t)}});k=U,g.inject([m.DOOM_DELEGATION_SERVICE],e=>{let t=(0,m.readDoomDelegationService)(e);if(t)return U.bind(e,t)}),g.inject([h.DOOM_BACKGROUND_WORK_SERVICE],e=>{let t=(0,h.readDoomBackgroundWorkService)(e);if(!t)return;let r=t.register({provider:n.BACKGROUND_WORK_PROVIDER,listActiveWork:()=>N?U.listActiveWork():[]});return M=r,()=>{r.dispose(),M===r&&(M=void 0)}});let W=new u.TaskOverlay({getTasks:()=>V.snapshot.tasks,delegation:U});A=W,a.registerTaskTool(S,{store:V,delegation:U,maxTasks:i.getMaxTasks(),onChange:H,report:w,waitUntilReady:z}),r.registerTasksCommand(S,V,z),g.inject([b.DOOM_UI_HUB_SERVICE],t=>{let n=(0,b.requireDoomUiHub)(t).registerLeader({source:x,bindings:[{id:`tasks.open`,path:[{key:`t`,label:`tasks`,detail:`session task list`,order:65},{key:`l`,label:`list`,detail:`tasks in this session`}],command:{name:e.COMMAND_NAME}}]}),r=()=>n.dispose();return P=r,()=>{r(),P===r&&(P=void 0)}}),S.registerMessageRenderer(n.NOTIFY_CUSTOM_TYPE,(e,t,n)=>{let r=typeof e.content==`string`?e.content:``;return new p.Text(n.fg(`muted`,r),0,0)});let G=i.resolveCollapseKey();G!==`off`&&S.registerShortcut(G,{description:`Collapse or expand the task overlay`,handler:async e=>{!T||!e.hasUI||(await z(e),!(!T||!W.isRegistered())&&W.toggleCollapse())}}),S.on(`session_start`,async(e,n)=>{if(!T)return;let r=++E;F!==void 0&&B();let a=n,o=async e=>{let n=()=>!e?.aborted&&T&&r===E;try{let e=a.sessionManager?.getSessionId();if(!e)throw Error(`doom-task requires a session id`);F=e,N=!a.hasUI,M?.update(),V.configureSession(c.resolveSessionKey(e));let r=await c.removeLegacyStoreDirectoryAsync(V.storePath);if(!n())return;let o=c.hasStorePathOverride()?{removed:[],errors:[]}:await c.sweepStoreFilesAsync(V.storePath,i.getStoreTtlMs());if(!n())return;let s=[...r.errors,...o.errors];for(let e of s)w.warn(t.TASK_EVENT.storeSweepFailed,Error(e));if(s.length>0&&a.hasUI&&a.ui.notify(s.join(`
2
- `),`warning`),a.hasUI&&W.setUICtx(a.ui),await V.readAsync(n),!n()||(j=V.onExternalChange(H),await U.reconcile(n),!n()))return;H()}catch(e){if(!n())return;throw B(),w.error(t.TASK_EVENT.sessionStartFailed,e),e}},s=(0,y.readDoomReadinessCoordinator)(g);if(!s){let e=o();return I={sessionManager:n.sessionManager,operation:e},e}let l=I?.operation,u=(async()=>{l&&await Promise.allSettled([l]),!(!T||r!==E)&&await s.start(x,`${n.sessionManager.getSessionId()}:${r}`,async e=>(await o(e),{value:void 0})).wait()})();I={sessionManager:n.sessionManager,operation:u},Promise.allSettled([u])}),S.on(`session_compact`,H),S.on(`session_tree`,H),S.on(`tool_execution_end`,e=>{T&&e.toolName===`task`&&H()})}async function C(e){let t=await(0,g.connectDoomCordisHost)(e,x),n=t.root.plugin(w,{pi:e});try{await n}catch(e){try{await n.dispose()}finally{await t.dispose()}throw e}let r;e.on(`session_shutdown`,()=>r??=(async()=>{try{await n.dispose()}finally{await t.dispose()}})())}function w(e,t){S(e,t.pi)}exports.default=C,exports.taskExtension=C,exports.installTaskRuntime=S;
1
+ const e=require("../../schemas/task.cjs"),t=require("../../types/telemetry.cjs"),n=require("../../services/delegation/manager.cjs"),r=require("../../commands/index.cjs"),i=require("../../types/config.cjs"),a=require("../../commands/task/taskTool.cjs"),o=require("../../services/contextContribution.cjs"),s=require("../../services/narration/taskNarration.cjs"),c=require("../store/paths.cjs"),l=require("../store/taskStore.cjs"),u=require("../../tui/taskOverlay2.cjs"),d=require("../node/delegationPlatform.cjs"),f=require("../telemetry/logSinkTelemetry.cjs");let p=require("@earendil-works/pi-tui"),m=require("@agimon-ai/doompi-extension-contracts/delegation"),h=require("@agimon-ai/doompi-extension-contracts/background-work"),g=require("@agimon-ai/doompi-extension-contracts/cordis-host"),_=require("@agimon-ai/doompi-extension-contracts/context-contributions"),v=require("@agimon-ai/doompi-extension-contracts/narration"),y=require("@agimon-ai/doompi-extension-contracts/readiness"),b=require("@agimon-ai/doompi-extension-contracts/ui-hub");const x=o.TASK_CONTEXT_CONTRIBUTION_SOURCE,S=`error.type`,C=`task.stage`,w={sessionId:`session_id`,storeCleanup:`store_cleanup`,storeSweep:`store_sweep`,storeRead:`store_read`,reconcile:`reconcile`,refresh:`refresh`};function T(e){return e instanceof Error?e.name||`Error`:typeof e==`string`?`StringError`:e&&typeof e==`object`?`ObjectError`:`UnknownError`}function E(g,E){let D=f.createTaskErrorReporter(),O=f.toFailureReporter(D),k=!0,A=0,j,M,N,P,F,I,L=!1,R,z,B,V=e=>{D.recordWarning(`doom_task.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-task could not record a narration failure: ${String(e)}`)})},H={narrate(e){let t=j,n=(0,v.createNarrationRequest)(e);if(!(!t||!n))try{Promise.resolve(t.request(n)).catch(V)}catch(e){V(e)}}};g.inject([v.DOOM_NARRATION_SERVICE],e=>{let t=(0,v.requireDoomNarrationService)(e);return j=t,()=>{j===t&&(j=void 0)}});let U=async(e,t)=>{t?.throwIfAborted();let n=B;if(!n)throw Error(`doom-task session initialization has not started`);if(n.sessionManager!==e.sessionManager)throw Error(`doom-task readiness belongs to a stale Pi session`);if(await n.operation,t?.throwIfAborted(),!k||n!==B||!z)throw Error(`doom-task readiness belongs to a stale extension generation`)},W=()=>{F?.(),F=void 0,L=!1,I?.update(),N?.reset(),P?.dispose(),z=void 0};g.effect(()=>async()=>{k=!1,A+=1;let e=B?.operation;B=void 0;try{e&&await Promise.allSettled([e]),W(),N?.dispose(),R?.(),R=void 0,M?.dispose()}finally{await D.shutdown()}},x);let G=new l.TaskStore({report:O,onCommitted:(e,t)=>s.narrateTaskCommit(H,e,t)});M=G,g.inject([_.DOOM_CONTEXT_CONTRIBUTIONS_SERVICE],e=>{let t=(0,_.requireDoomContextContributions)(e).register(o.createTaskContextContribution(()=>G.snapshot.tasks));return()=>t.dispose()});let K=()=>{k&&(P?.update(),I?.update())},q=new n.DelegationManager({store:G,cwd:process.cwd(),platform:d.createNodeDelegationPlatform(),notify:(e,t)=>E.sendMessage(e,t),getSessionId:()=>z,onChange:K,runTimeoutMs:i.getDelegationTimeoutMs(),report:O,onNotifyError:(e,t)=>{D.recordNotificationError(e,t)}});N=q,g.inject([m.DOOM_DELEGATION_SERVICE],e=>{let t=(0,m.readDoomDelegationService)(e);if(t)return q.bind(e,t)}),g.inject([h.DOOM_BACKGROUND_WORK_SERVICE],e=>{let t=(0,h.readDoomBackgroundWorkService)(e);if(!t)return;let r=t.register({provider:n.BACKGROUND_WORK_PROVIDER,listActiveWork:()=>L?q.listActiveWork():[]});return I=r,()=>{r.dispose(),I===r&&(I=void 0)}});let J=new u.TaskOverlay({getTasks:()=>G.snapshot.tasks,delegation:q});P=J,a.registerTaskTool(E,{store:G,delegation:q,maxTasks:i.getMaxTasks(),onChange:K,report:O,waitUntilReady:U}),r.registerTasksCommand(E,G,U),g.inject([b.DOOM_UI_HUB_SERVICE],t=>{let n=(0,b.requireDoomUiHub)(t).registerLeader({source:x,bindings:[{id:`tasks.open`,path:[{key:`t`,label:`tasks`,detail:`session task list`,order:65},{key:`l`,label:`list`,detail:`tasks in this session`}],command:{name:e.COMMAND_NAME}}]}),r=()=>n.dispose();return R=r,()=>{r(),R===r&&(R=void 0)}}),E.registerMessageRenderer(n.NOTIFY_CUSTOM_TYPE,(e,t,n)=>{let r=typeof e.content==`string`?e.content:``;return new p.Text(n.fg(`muted`,r),0,0)});let Y=i.resolveCollapseKey();Y!==`off`&&E.registerShortcut(Y,{description:`Collapse or expand the task overlay`,handler:async e=>{!k||!e.hasUI||(await U(e),!(!k||!J.isRegistered())&&J.toggleCollapse())}}),E.on(`session_start`,async(e,n)=>{if(!k)return;let r=++A;z!==void 0&&W();let a=n,o=async e=>{let n=()=>!e?.aborted&&k&&r===A,o=w.sessionId,s=async(e,n)=>{o=e;try{await n()}catch(n){O.warn(t.TASK_EVENT.sessionStartDegraded,n,{[C]:e,[S]:T(n)})}};try{let e=a.sessionManager?.getSessionId();if(!e)throw Error(`doom-task requires a session id`);z=e,L=!a.hasUI,I?.update(),G.configureSession(c.resolveSessionKey(e)),o=w.storeCleanup;let r=await c.removeLegacyStoreDirectoryAsync(G.storePath);if(!n())return;o=w.storeSweep;let l=c.hasStorePathOverride()?{removed:[],errors:[]}:await c.sweepStoreFilesAsync(G.storePath,i.getStoreTtlMs());if(!n())return;let u=[...r.errors,...l.errors];for(let e of u)O.warn(t.TASK_EVENT.storeSweepFailed,Error(e));if(u.length>0&&a.hasUI&&a.ui.notify(u.join(`
2
+ `),`warning`),a.hasUI&&J.setUICtx(a.ui),await s(w.storeRead,async()=>{await G.readAsync(n)}),!n()||(F=G.onExternalChange(K),await s(w.reconcile,async()=>{await q.reconcile(n)}),!n()))return;o=w.refresh,K()}catch(e){if(!n())return;throw W(),O.error(t.TASK_EVENT.sessionStartFailed,e,{[C]:o,[S]:T(e)}),e}},s=(0,y.readDoomReadinessCoordinator)(g);if(!s){let e=o();return B={sessionManager:n.sessionManager,operation:e},e}let l=B?.operation,u=(async()=>{l&&await Promise.allSettled([l]),!(!k||r!==A)&&await s.start(x,`${n.sessionManager.getSessionId()}:${r}`,async e=>(await o(e),{value:void 0})).wait()})();B={sessionManager:n.sessionManager,operation:u},Promise.allSettled([u])}),E.on(`session_compact`,K),E.on(`session_tree`,K),E.on(`tool_execution_end`,e=>{k&&e.toolName===`task`&&K()})}async function D(e){let t=await(0,g.connectDoomCordisHost)(e,x),n=t.root.plugin(O,{pi:e});try{await n}catch(e){try{await n.dispose()}finally{await t.dispose()}throw e}let r;e.on(`session_shutdown`,()=>r??=(async()=>{try{await n.dispose()}finally{await t.dispose()}})())}function O(e,t){E(e,t.pi)}exports.default=D,exports.taskExtension=D,exports.installTaskRuntime=E;
3
3
  //# sourceMappingURL=extension.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.cjs","names":["TASK_CONTEXT_CONTRIBUTION_SOURCE","createTaskErrorReporter","toFailureReporter","createNarrationRequest","DOOM_NARRATION_SERVICE","requireDoomNarrationService","TaskStore","narrateTaskCommit","DOOM_CONTEXT_CONTRIBUTIONS_SERVICE","requireDoomContextContributions","createTaskContextContribution","DelegationManager","createNodeDelegationPlatform","getDelegationTimeoutMs","DOOM_DELEGATION_SERVICE","readDoomDelegationService","DOOM_BACKGROUND_WORK_SERVICE","readDoomBackgroundWorkService","BACKGROUND_WORK_PROVIDER","TaskOverlay","getMaxTasks","DOOM_UI_HUB_SERVICE","requireDoomUiHub","COMMAND_NAME","NOTIFY_CUSTOM_TYPE","Text","resolveCollapseKey","resolveSessionKey","removeLegacyStoreDirectoryAsync","hasStorePathOverride","sweepStoreFilesAsync","getStoreTtlMs","TASK_EVENT","readDoomReadinessCoordinator","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n type BackgroundWorkProviderHandle,\n readDoomBackgroundWorkService,\n} from '@agimon-ai/doompi-extension-contracts/background-work';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_CONTEXT_CONTRIBUTIONS_SERVICE,\n requireDoomContextContributions,\n} from '@agimon-ai/doompi-extension-contracts/context-contributions';\nimport { DOOM_DELEGATION_SERVICE, readDoomDelegationService } from '@agimon-ai/doompi-extension-contracts/delegation';\nimport {\n createNarrationRequest,\n DOOM_NARRATION_SERVICE,\n type DoomNarrationService,\n requireDoomNarrationService,\n} from '@agimon-ai/doompi-extension-contracts/narration';\nimport { readDoomReadinessCoordinator } from '@agimon-ai/doompi-extension-contracts/readiness';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { Text } from '@earendil-works/pi-tui';\nimport { registerTasksCommand } from '../../commands/index.ts';\nimport { registerTaskTool } from '../../commands/task/taskTool.ts';\nimport { COMMAND_NAME, TOOL_NAME } from '../../schemas/task.ts';\nimport { BACKGROUND_WORK_PROVIDER, DelegationManager, NOTIFY_CUSTOM_TYPE } from '../../services/delegation/manager.ts';\nimport { createTaskContextContribution, TASK_CONTEXT_CONTRIBUTION_SOURCE } from '../../services/contextContribution.ts';\nimport { narrateTaskCommit, type TaskNarrationSink } from '../../services/narration/taskNarration.ts';\nimport {\n hasStorePathOverride,\n removeLegacyStoreDirectoryAsync,\n resolveSessionKey,\n sweepStoreFilesAsync,\n} from '../../adapters/store/paths';\nimport { TaskStore } from '../../adapters/store/taskStore';\nimport { TaskOverlay } from '../../tui/taskOverlay.ts';\nimport {\n COLLAPSE_KEY_OFF,\n getDelegationTimeoutMs,\n getMaxTasks,\n getStoreTtlMs,\n resolveCollapseKey,\n} from '../../types/config.ts';\nimport { createNodeDelegationPlatform } from '../node/delegationPlatform.ts';\nimport { createTaskErrorReporter, TASK_EVENT, toFailureReporter } from '../telemetry/logSinkTelemetry.ts';\n\ninterface SessionContextLike {\n hasUI: boolean;\n ui: Parameters<TaskOverlay['setUICtx']>[0];\n sessionManager?: { getSessionId(): string };\n}\n\nconst LEADER_SOURCE = TASK_CONTEXT_CONTRIBUTION_SOURCE;\nconst NARRATION_FAILED_EVENT = 'doom_task.narration_failed';\n/** Between doom-plan's 60 and the core help group's 70, so `t` sits after `p`. */\nconst LEADER_GROUP_ORDER = 65;\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_COMPACT_EVENT = 'session_compact';\nconst SESSION_TREE_EVENT = 'session_tree';\nconst TOOL_EXECUTION_END_EVENT = 'tool_execution_end';\n\n/** Install Task state and external resources into its host-owned Cordis plugin fiber. */\nexport function installTaskRuntime(cordis: Context, pi: ExtensionAPI): void {\n const errorReporter = createTaskErrorReporter();\n const report = toFailureReporter(errorReporter);\n let active = true;\n let sessionGeneration = 0;\n let narrationService: DoomNarrationService | undefined;\n let store: TaskStore | undefined;\n let delegation: DelegationManager | undefined;\n let overlay: TaskOverlay | undefined;\n let unwatch: (() => void) | undefined;\n let backgroundWork: BackgroundWorkProviderHandle | undefined;\n let backgroundWorkEnabled = false;\n let disposeLeader: (() => void) | undefined;\n let sessionId: string | undefined;\n let sessionInitialization:\n | {\n readonly sessionManager: object;\n readonly operation: Promise<void>;\n }\n | undefined;\n\n const reportNarrationFailure = (error: unknown): void => {\n void errorReporter.recordWarning(NARRATION_FAILED_EVENT, error).catch((telemetryError: unknown) => {\n process.emitWarning(`Doom-task could not record a narration failure: ${String(telemetryError)}`);\n });\n };\n const narrationSink: TaskNarrationSink = {\n narrate(text) {\n const service = narrationService;\n const request = createNarrationRequest(text);\n if (!service || !request) return;\n try {\n void Promise.resolve(service.request(request)).catch(reportNarrationFailure);\n } catch (error) {\n reportNarrationFailure(error);\n }\n },\n };\n\n cordis.inject([DOOM_NARRATION_SERVICE], (serviceContext) => {\n const service = requireDoomNarrationService(serviceContext);\n narrationService = service;\n return () => {\n if (narrationService === service) narrationService = undefined;\n };\n });\n\n const waitForSessionReadiness = async (\n context: { readonly sessionManager: object },\n signal?: AbortSignal,\n ): Promise<void> => {\n signal?.throwIfAborted();\n const current = sessionInitialization;\n if (!current) throw new Error('doom-task session initialization has not started');\n if (current.sessionManager !== context.sessionManager) {\n throw new Error('doom-task readiness belongs to a stale Pi session');\n }\n await current.operation;\n signal?.throwIfAborted();\n if (!active || current !== sessionInitialization || !sessionId) {\n throw new Error('doom-task readiness belongs to a stale extension generation');\n }\n };\n\n const clearSessionResources = (): void => {\n unwatch?.();\n unwatch = undefined;\n backgroundWorkEnabled = false;\n backgroundWork?.update();\n delegation?.reset();\n overlay?.dispose();\n sessionId = undefined;\n };\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n const pendingInitialization = sessionInitialization?.operation;\n sessionInitialization = undefined;\n try {\n if (pendingInitialization) await Promise.allSettled([pendingInitialization]);\n clearSessionResources();\n delegation?.dispose();\n disposeLeader?.();\n disposeLeader = undefined;\n store?.dispose();\n } finally {\n await errorReporter.shutdown();\n }\n },\n LEADER_SOURCE,\n );\n\n const taskStore = new TaskStore({\n report,\n onCommitted: (previous, committed) => narrateTaskCommit(narrationSink, previous, committed),\n });\n store = taskStore;\n\n cordis.inject([DOOM_CONTEXT_CONTRIBUTIONS_SERVICE], (contextContributionsContext) => {\n const registration = requireDoomContextContributions(contextContributionsContext).register(\n createTaskContextContribution(() => taskStore.snapshot.tasks),\n );\n return () => registration.dispose();\n });\n\n const refresh = (): void => {\n if (!active) return;\n overlay?.update();\n backgroundWork?.update();\n };\n\n const delegationManager = new DelegationManager({\n store: taskStore,\n cwd: process.cwd(),\n platform: createNodeDelegationPlatform(),\n notify: (message, options) => pi.sendMessage(message, options),\n getSessionId: () => sessionId,\n onChange: refresh,\n runTimeoutMs: getDelegationTimeoutMs(),\n report,\n onNotifyError: (error, taskId) => {\n void errorReporter.recordNotificationError(error, taskId);\n },\n });\n delegation = delegationManager;\n\n cordis.inject([DOOM_DELEGATION_SERVICE], (serviceContext) => {\n const service = readDoomDelegationService(serviceContext);\n if (!service) return undefined;\n return delegationManager.bind(serviceContext, service);\n });\n\n cordis.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const handle = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => (backgroundWorkEnabled ? delegationManager.listActiveWork() : []),\n });\n backgroundWork = handle;\n return () => {\n handle.dispose();\n if (backgroundWork === handle) backgroundWork = undefined;\n };\n });\n\n const taskOverlay = new TaskOverlay({ getTasks: () => taskStore.snapshot.tasks, delegation: delegationManager });\n overlay = taskOverlay;\n\n registerTaskTool(pi, {\n store: taskStore,\n delegation: delegationManager,\n maxTasks: getMaxTasks(),\n onChange: refresh,\n report,\n waitUntilReady: waitForSessionReadiness,\n });\n registerTasksCommand(pi, taskStore, waitForSessionReadiness);\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader({\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'tasks.open',\n path: [\n { key: 't', label: 'tasks', detail: 'session task list', order: LEADER_GROUP_ORDER },\n { key: 'l', label: 'list', detail: 'tasks in this session' },\n ],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n const dispose = (): void => contribution.dispose();\n disposeLeader = dispose;\n return () => {\n dispose();\n if (disposeLeader === dispose) disposeLeader = undefined;\n };\n });\n\n pi.registerMessageRenderer(NOTIFY_CUSTOM_TYPE, (message, _options, theme) => {\n const content = typeof message.content === 'string' ? message.content : '';\n return new Text(theme.fg('muted', content), 0, 0);\n });\n\n const collapseKey = resolveCollapseKey();\n if (collapseKey !== COLLAPSE_KEY_OFF) {\n pi.registerShortcut(collapseKey as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (ctx) => {\n if (!active || !ctx.hasUI) return;\n await waitForSessionReadiness(ctx);\n if (!active || !taskOverlay.isRegistered()) return;\n taskOverlay.toggleCollapse();\n },\n });\n }\n\n pi.on(SESSION_START_EVENT, async (_event, ctx) => {\n if (!active) return;\n const ownGeneration = ++sessionGeneration;\n if (sessionId !== undefined) clearSessionResources();\n const context = ctx as unknown as SessionContextLike;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n const isCurrent = (): boolean => !signal?.aborted && active && ownGeneration === sessionGeneration;\n try {\n const currentSessionId = context.sessionManager?.getSessionId();\n if (!currentSessionId) throw new Error('doom-task requires a session id');\n sessionId = currentSessionId;\n backgroundWorkEnabled = !context.hasUI;\n backgroundWork?.update();\n taskStore.configureSession(resolveSessionKey(currentSessionId));\n const cleanup = await removeLegacyStoreDirectoryAsync(taskStore.storePath);\n if (!isCurrent()) return;\n const sweep = hasStorePathOverride()\n ? { removed: [], errors: [] }\n : await sweepStoreFilesAsync(taskStore.storePath, getStoreTtlMs());\n if (!isCurrent()) return;\n const storeErrors = [...cleanup.errors, ...sweep.errors];\n for (const failure of storeErrors) report.warn(TASK_EVENT.storeSweepFailed, new Error(failure));\n if (storeErrors.length > 0 && context.hasUI) context.ui.notify(storeErrors.join('\\n'), 'warning');\n if (context.hasUI) taskOverlay.setUICtx(context.ui);\n await taskStore.readAsync(isCurrent);\n if (!isCurrent()) return;\n unwatch = taskStore.onExternalChange(refresh);\n await delegationManager.reconcile(isCurrent);\n if (!isCurrent()) return;\n refresh();\n } catch (error) {\n if (!isCurrent()) return;\n clearSessionResources();\n report.error(TASK_EVENT.sessionStartFailed, error);\n throw error;\n }\n };\n\n const coordinator = readDoomReadinessCoordinator(cordis);\n if (!coordinator) {\n const operation = initialize();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n return operation;\n }\n\n const previous = sessionInitialization?.operation;\n const operation = (async (): Promise<void> => {\n if (previous) await Promise.allSettled([previous]);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n LEADER_SOURCE,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n // Config's coordinator owns the single failure notification. This detached\n // observer only keeps a failed background generation from going unhandled.\n void Promise.allSettled([operation]);\n return undefined;\n });\n\n pi.on(SESSION_COMPACT_EVENT, refresh);\n pi.on(SESSION_TREE_EVENT, refresh);\n pi.on(TOOL_EXECUTION_END_EVENT, (event) => {\n if (active && event.toolName === TOOL_NAME) refresh();\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function taskExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, LEADER_SOURCE);\n const fiber = connection.root.plugin(taskPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface TaskPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction taskPlugin(cordis: Context, config: TaskPluginConfig): void {\n installTaskRuntime(cordis, config.pi);\n}\n\nexport default taskExtension;\n"],"mappings":"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"}
1
+ {"version":3,"file":"extension.cjs","names":["TASK_CONTEXT_CONTRIBUTION_SOURCE","createTaskErrorReporter","toFailureReporter","createNarrationRequest","DOOM_NARRATION_SERVICE","requireDoomNarrationService","TaskStore","narrateTaskCommit","DOOM_CONTEXT_CONTRIBUTIONS_SERVICE","requireDoomContextContributions","createTaskContextContribution","DelegationManager","createNodeDelegationPlatform","getDelegationTimeoutMs","DOOM_DELEGATION_SERVICE","readDoomDelegationService","DOOM_BACKGROUND_WORK_SERVICE","readDoomBackgroundWorkService","BACKGROUND_WORK_PROVIDER","TaskOverlay","getMaxTasks","DOOM_UI_HUB_SERVICE","requireDoomUiHub","COMMAND_NAME","NOTIFY_CUSTOM_TYPE","Text","resolveCollapseKey","TASK_EVENT","resolveSessionKey","removeLegacyStoreDirectoryAsync","hasStorePathOverride","sweepStoreFilesAsync","getStoreTtlMs","readDoomReadinessCoordinator","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n type BackgroundWorkProviderHandle,\n readDoomBackgroundWorkService,\n} from '@agimon-ai/doompi-extension-contracts/background-work';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_CONTEXT_CONTRIBUTIONS_SERVICE,\n requireDoomContextContributions,\n} from '@agimon-ai/doompi-extension-contracts/context-contributions';\nimport { DOOM_DELEGATION_SERVICE, readDoomDelegationService } from '@agimon-ai/doompi-extension-contracts/delegation';\nimport {\n createNarrationRequest,\n DOOM_NARRATION_SERVICE,\n type DoomNarrationService,\n requireDoomNarrationService,\n} from '@agimon-ai/doompi-extension-contracts/narration';\nimport { readDoomReadinessCoordinator } from '@agimon-ai/doompi-extension-contracts/readiness';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { Text } from '@earendil-works/pi-tui';\nimport { registerTasksCommand } from '../../commands/index.ts';\nimport { registerTaskTool } from '../../commands/task/taskTool.ts';\nimport { COMMAND_NAME, TOOL_NAME } from '../../schemas/task.ts';\nimport { BACKGROUND_WORK_PROVIDER, DelegationManager, NOTIFY_CUSTOM_TYPE } from '../../services/delegation/manager.ts';\nimport { createTaskContextContribution, TASK_CONTEXT_CONTRIBUTION_SOURCE } from '../../services/contextContribution.ts';\nimport { narrateTaskCommit, type TaskNarrationSink } from '../../services/narration/taskNarration.ts';\nimport {\n hasStorePathOverride,\n removeLegacyStoreDirectoryAsync,\n resolveSessionKey,\n sweepStoreFilesAsync,\n} from '../../adapters/store/paths';\nimport { TaskStore } from '../../adapters/store/taskStore';\nimport { TaskOverlay } from '../../tui/taskOverlay.ts';\nimport {\n COLLAPSE_KEY_OFF,\n getDelegationTimeoutMs,\n getMaxTasks,\n getStoreTtlMs,\n resolveCollapseKey,\n} from '../../types/config.ts';\nimport { createNodeDelegationPlatform } from '../node/delegationPlatform.ts';\nimport { createTaskErrorReporter, TASK_EVENT, toFailureReporter } from '../telemetry/logSinkTelemetry.ts';\n\ninterface SessionContextLike {\n hasUI: boolean;\n ui: Parameters<TaskOverlay['setUICtx']>[0];\n sessionManager?: { getSessionId(): string };\n}\n\nconst LEADER_SOURCE = TASK_CONTEXT_CONTRIBUTION_SOURCE;\nconst NARRATION_FAILED_EVENT = 'doom_task.narration_failed';\n/** Between doom-plan's 60 and the core help group's 70, so `t` sits after `p`. */\nconst LEADER_GROUP_ORDER = 65;\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_COMPACT_EVENT = 'session_compact';\nconst SESSION_TREE_EVENT = 'session_tree';\nconst TOOL_EXECUTION_END_EVENT = 'tool_execution_end';\nconst ERROR_TYPE_ATTRIBUTE = 'error.type';\n/**\n * Which part of session start ran. The telemetry sanitizer drops free text, so\n * the failing branch has to travel as a stable low-cardinality token.\n */\nconst SESSION_START_STAGE_ATTRIBUTE = 'task.stage';\nconst SESSION_START_STAGE = {\n sessionId: 'session_id',\n storeCleanup: 'store_cleanup',\n storeSweep: 'store_sweep',\n storeRead: 'store_read',\n reconcile: 'reconcile',\n refresh: 'refresh',\n} as const;\ntype SessionStartStage = (typeof SESSION_START_STAGE)[keyof typeof SESSION_START_STAGE];\n\n/**\n * Mirror of the telemetry sink's own `error.type` token. The sink overwrites it\n * for any defined error, so this only guarantees the attribute exists when the\n * thrown value is `undefined`.\n */\nfunction errorTypeToken(error: unknown): string {\n if (error instanceof Error) return error.name || 'Error';\n if (typeof error === 'string') return 'StringError';\n if (error && typeof error === 'object') return 'ObjectError';\n return 'UnknownError';\n}\n/** Install Task state and external resources into its host-owned Cordis plugin fiber. */\nexport function installTaskRuntime(cordis: Context, pi: ExtensionAPI): void {\n const errorReporter = createTaskErrorReporter();\n const report = toFailureReporter(errorReporter);\n let active = true;\n let sessionGeneration = 0;\n let narrationService: DoomNarrationService | undefined;\n let store: TaskStore | undefined;\n let delegation: DelegationManager | undefined;\n let overlay: TaskOverlay | undefined;\n let unwatch: (() => void) | undefined;\n let backgroundWork: BackgroundWorkProviderHandle | undefined;\n let backgroundWorkEnabled = false;\n let disposeLeader: (() => void) | undefined;\n let sessionId: string | undefined;\n let sessionInitialization:\n | {\n readonly sessionManager: object;\n readonly operation: Promise<void>;\n }\n | undefined;\n\n const reportNarrationFailure = (error: unknown): void => {\n void errorReporter.recordWarning(NARRATION_FAILED_EVENT, error).catch((telemetryError: unknown) => {\n process.emitWarning(`Doom-task could not record a narration failure: ${String(telemetryError)}`);\n });\n };\n const narrationSink: TaskNarrationSink = {\n narrate(text) {\n const service = narrationService;\n const request = createNarrationRequest(text);\n if (!service || !request) return;\n try {\n void Promise.resolve(service.request(request)).catch(reportNarrationFailure);\n } catch (error) {\n reportNarrationFailure(error);\n }\n },\n };\n\n cordis.inject([DOOM_NARRATION_SERVICE], (serviceContext) => {\n const service = requireDoomNarrationService(serviceContext);\n narrationService = service;\n return () => {\n if (narrationService === service) narrationService = undefined;\n };\n });\n\n const waitForSessionReadiness = async (\n context: { readonly sessionManager: object },\n signal?: AbortSignal,\n ): Promise<void> => {\n signal?.throwIfAborted();\n const current = sessionInitialization;\n if (!current) throw new Error('doom-task session initialization has not started');\n if (current.sessionManager !== context.sessionManager) {\n throw new Error('doom-task readiness belongs to a stale Pi session');\n }\n await current.operation;\n signal?.throwIfAborted();\n if (!active || current !== sessionInitialization || !sessionId) {\n throw new Error('doom-task readiness belongs to a stale extension generation');\n }\n };\n\n const clearSessionResources = (): void => {\n unwatch?.();\n unwatch = undefined;\n backgroundWorkEnabled = false;\n backgroundWork?.update();\n delegation?.reset();\n overlay?.dispose();\n sessionId = undefined;\n };\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n const pendingInitialization = sessionInitialization?.operation;\n sessionInitialization = undefined;\n try {\n if (pendingInitialization) await Promise.allSettled([pendingInitialization]);\n clearSessionResources();\n delegation?.dispose();\n disposeLeader?.();\n disposeLeader = undefined;\n store?.dispose();\n } finally {\n await errorReporter.shutdown();\n }\n },\n LEADER_SOURCE,\n );\n\n const taskStore = new TaskStore({\n report,\n onCommitted: (previous, committed) => narrateTaskCommit(narrationSink, previous, committed),\n });\n store = taskStore;\n\n cordis.inject([DOOM_CONTEXT_CONTRIBUTIONS_SERVICE], (contextContributionsContext) => {\n const registration = requireDoomContextContributions(contextContributionsContext).register(\n createTaskContextContribution(() => taskStore.snapshot.tasks),\n );\n return () => registration.dispose();\n });\n\n const refresh = (): void => {\n if (!active) return;\n overlay?.update();\n backgroundWork?.update();\n };\n\n const delegationManager = new DelegationManager({\n store: taskStore,\n cwd: process.cwd(),\n platform: createNodeDelegationPlatform(),\n notify: (message, options) => pi.sendMessage(message, options),\n getSessionId: () => sessionId,\n onChange: refresh,\n runTimeoutMs: getDelegationTimeoutMs(),\n report,\n onNotifyError: (error, taskId) => {\n void errorReporter.recordNotificationError(error, taskId);\n },\n });\n delegation = delegationManager;\n\n cordis.inject([DOOM_DELEGATION_SERVICE], (serviceContext) => {\n const service = readDoomDelegationService(serviceContext);\n if (!service) return undefined;\n return delegationManager.bind(serviceContext, service);\n });\n\n cordis.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const handle = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => (backgroundWorkEnabled ? delegationManager.listActiveWork() : []),\n });\n backgroundWork = handle;\n return () => {\n handle.dispose();\n if (backgroundWork === handle) backgroundWork = undefined;\n };\n });\n\n const taskOverlay = new TaskOverlay({ getTasks: () => taskStore.snapshot.tasks, delegation: delegationManager });\n overlay = taskOverlay;\n\n registerTaskTool(pi, {\n store: taskStore,\n delegation: delegationManager,\n maxTasks: getMaxTasks(),\n onChange: refresh,\n report,\n waitUntilReady: waitForSessionReadiness,\n });\n registerTasksCommand(pi, taskStore, waitForSessionReadiness);\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader({\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'tasks.open',\n path: [\n { key: 't', label: 'tasks', detail: 'session task list', order: LEADER_GROUP_ORDER },\n { key: 'l', label: 'list', detail: 'tasks in this session' },\n ],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n const dispose = (): void => contribution.dispose();\n disposeLeader = dispose;\n return () => {\n dispose();\n if (disposeLeader === dispose) disposeLeader = undefined;\n };\n });\n\n pi.registerMessageRenderer(NOTIFY_CUSTOM_TYPE, (message, _options, theme) => {\n const content = typeof message.content === 'string' ? message.content : '';\n return new Text(theme.fg('muted', content), 0, 0);\n });\n\n const collapseKey = resolveCollapseKey();\n if (collapseKey !== COLLAPSE_KEY_OFF) {\n pi.registerShortcut(collapseKey as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (ctx) => {\n if (!active || !ctx.hasUI) return;\n await waitForSessionReadiness(ctx);\n if (!active || !taskOverlay.isRegistered()) return;\n taskOverlay.toggleCollapse();\n },\n });\n }\n\n pi.on(SESSION_START_EVENT, async (_event, ctx) => {\n if (!active) return;\n const ownGeneration = ++sessionGeneration;\n if (sessionId !== undefined) clearSessionResources();\n const context = ctx as unknown as SessionContextLike;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n const isCurrent = (): boolean => !signal?.aborted && active && ownGeneration === sessionGeneration;\n let stage: SessionStartStage = SESSION_START_STAGE.sessionId;\n /**\n * Run a stage whose failure only degrades task visibility. The session\n * still starts, and the warning keeps the degraded state from being silent.\n */\n const degrade = async (next: SessionStartStage, operation: () => Promise<void>): Promise<void> => {\n stage = next;\n try {\n await operation();\n } catch (error) {\n report.warn(TASK_EVENT.sessionStartDegraded, error, {\n [SESSION_START_STAGE_ATTRIBUTE]: next,\n [ERROR_TYPE_ATTRIBUTE]: errorTypeToken(error),\n });\n }\n };\n try {\n const currentSessionId = context.sessionManager?.getSessionId();\n if (!currentSessionId) throw new Error('doom-task requires a session id');\n sessionId = currentSessionId;\n backgroundWorkEnabled = !context.hasUI;\n backgroundWork?.update();\n taskStore.configureSession(resolveSessionKey(currentSessionId));\n stage = SESSION_START_STAGE.storeCleanup;\n const cleanup = await removeLegacyStoreDirectoryAsync(taskStore.storePath);\n if (!isCurrent()) return;\n stage = SESSION_START_STAGE.storeSweep;\n const sweep = hasStorePathOverride()\n ? { removed: [], errors: [] }\n : await sweepStoreFilesAsync(taskStore.storePath, getStoreTtlMs());\n if (!isCurrent()) return;\n const storeErrors = [...cleanup.errors, ...sweep.errors];\n for (const failure of storeErrors) report.warn(TASK_EVENT.storeSweepFailed, new Error(failure));\n if (storeErrors.length > 0 && context.hasUI) context.ui.notify(storeErrors.join('\\n'), 'warning');\n if (context.hasUI) taskOverlay.setUICtx(context.ui);\n await degrade(SESSION_START_STAGE.storeRead, async () => {\n await taskStore.readAsync(isCurrent);\n });\n if (!isCurrent()) return;\n unwatch = taskStore.onExternalChange(refresh);\n await degrade(SESSION_START_STAGE.reconcile, async () => {\n await delegationManager.reconcile(isCurrent);\n });\n if (!isCurrent()) return;\n stage = SESSION_START_STAGE.refresh;\n refresh();\n } catch (error) {\n if (!isCurrent()) return;\n clearSessionResources();\n report.error(TASK_EVENT.sessionStartFailed, error, {\n [SESSION_START_STAGE_ATTRIBUTE]: stage,\n [ERROR_TYPE_ATTRIBUTE]: errorTypeToken(error),\n });\n throw error;\n }\n };\n\n const coordinator = readDoomReadinessCoordinator(cordis);\n if (!coordinator) {\n const operation = initialize();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n return operation;\n }\n\n const previous = sessionInitialization?.operation;\n const operation = (async (): Promise<void> => {\n if (previous) await Promise.allSettled([previous]);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n LEADER_SOURCE,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n // Config's coordinator owns the single failure notification. This detached\n // observer only keeps a failed background generation from going unhandled.\n void Promise.allSettled([operation]);\n return undefined;\n });\n\n pi.on(SESSION_COMPACT_EVENT, refresh);\n pi.on(SESSION_TREE_EVENT, refresh);\n pi.on(TOOL_EXECUTION_END_EVENT, (event) => {\n if (active && event.toolName === TOOL_NAME) refresh();\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function taskExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, LEADER_SOURCE);\n const fiber = connection.root.plugin(taskPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface TaskPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction taskPlugin(cordis: Context, config: TaskPluginConfig): void {\n installTaskRuntime(cordis, config.pi);\n}\n\nexport default taskExtension;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBA+DgB,mBAAmB,QAAQ,SAAS,IAAI;;iBAmRlC,cAAc,IAAI,eAAe"}
1
+ {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAyFgB,mBAAmB,QAAQ,SAAS,IAAI;;iBA6SlC,cAAc,IAAI,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBA+DgB,mBAAmB,QAAQ,SAAS,IAAI;;iBAmRlC,cAAc,IAAI,eAAe"}
1
+ {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAyFgB,mBAAmB,QAAQ,SAAS,IAAI;;iBA6SlC,cAAc,IAAI,eAAe"}
@@ -1,3 +1,3 @@
1
- import{COMMAND_NAME as e}from"../../schemas/task.mjs";import{TASK_EVENT as t}from"../../types/telemetry.mjs";import{BACKGROUND_WORK_PROVIDER as n,DelegationManager as r,NOTIFY_CUSTOM_TYPE as i}from"../../services/delegation/manager.mjs";import{registerTasksCommand as a}from"../../commands/index.mjs";import{getDelegationTimeoutMs as o,getMaxTasks as s,getStoreTtlMs as c,resolveCollapseKey as l}from"../../types/config.mjs";import{registerTaskTool as u}from"../../commands/task/taskTool.mjs";import{TASK_CONTEXT_CONTRIBUTION_SOURCE as d,createTaskContextContribution as f}from"../../services/contextContribution.mjs";import{narrateTaskCommit as p}from"../../services/narration/taskNarration.mjs";import{hasStorePathOverride as m,removeLegacyStoreDirectoryAsync as h,resolveSessionKey as g,sweepStoreFilesAsync as _}from"../store/paths.mjs";import{TaskStore as v}from"../store/taskStore.mjs";import{TaskOverlay as y}from"../../tui/taskOverlay2.mjs";import{createNodeDelegationPlatform as b}from"../node/delegationPlatform.mjs";import{createTaskErrorReporter as ee,toFailureReporter as te}from"../telemetry/logSinkTelemetry.mjs";import{Text as ne}from"@earendil-works/pi-tui";import{DOOM_DELEGATION_SERVICE as re,readDoomDelegationService as x}from"@agimon-ai/doompi-extension-contracts/delegation";import{DOOM_BACKGROUND_WORK_SERVICE as S,readDoomBackgroundWorkService as C}from"@agimon-ai/doompi-extension-contracts/background-work";import{connectDoomCordisHost as w}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_CONTEXT_CONTRIBUTIONS_SERVICE as T,requireDoomContextContributions as E}from"@agimon-ai/doompi-extension-contracts/context-contributions";import{DOOM_NARRATION_SERVICE as ie,createNarrationRequest as ae,requireDoomNarrationService as D}from"@agimon-ai/doompi-extension-contracts/narration";import{readDoomReadinessCoordinator as O}from"@agimon-ai/doompi-extension-contracts/readiness";import{DOOM_UI_HUB_SERVICE as k,requireDoomUiHub as A}from"@agimon-ai/doompi-extension-contracts/ui-hub";const j=d;function M(d,w){let M=ee(),N=te(M),P=!0,F=0,I,L,R,z,B,V,H=!1,U,W,G,K=e=>{M.recordWarning(`doom_task.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-task could not record a narration failure: ${String(e)}`)})},oe={narrate(e){let t=I,n=ae(e);if(!(!t||!n))try{Promise.resolve(t.request(n)).catch(K)}catch(e){K(e)}}};d.inject([ie],e=>{let t=D(e);return I=t,()=>{I===t&&(I=void 0)}});let q=async(e,t)=>{t?.throwIfAborted();let n=G;if(!n)throw Error(`doom-task session initialization has not started`);if(n.sessionManager!==e.sessionManager)throw Error(`doom-task readiness belongs to a stale Pi session`);if(await n.operation,t?.throwIfAborted(),!P||n!==G||!W)throw Error(`doom-task readiness belongs to a stale extension generation`)},J=()=>{B?.(),B=void 0,H=!1,V?.update(),R?.reset(),z?.dispose(),W=void 0};d.effect(()=>async()=>{P=!1,F+=1;let e=G?.operation;G=void 0;try{e&&await Promise.allSettled([e]),J(),R?.dispose(),U?.(),U=void 0,L?.dispose()}finally{await M.shutdown()}},j);let Y=new v({report:N,onCommitted:(e,t)=>p(oe,e,t)});L=Y,d.inject([T],e=>{let t=E(e).register(f(()=>Y.snapshot.tasks));return()=>t.dispose()});let X=()=>{P&&(z?.update(),V?.update())},Z=new r({store:Y,cwd:process.cwd(),platform:b(),notify:(e,t)=>w.sendMessage(e,t),getSessionId:()=>W,onChange:X,runTimeoutMs:o(),report:N,onNotifyError:(e,t)=>{M.recordNotificationError(e,t)}});R=Z,d.inject([re],e=>{let t=x(e);if(t)return Z.bind(e,t)}),d.inject([S],e=>{let t=C(e);if(!t)return;let r=t.register({provider:n,listActiveWork:()=>H?Z.listActiveWork():[]});return V=r,()=>{r.dispose(),V===r&&(V=void 0)}});let Q=new y({getTasks:()=>Y.snapshot.tasks,delegation:Z});z=Q,u(w,{store:Y,delegation:Z,maxTasks:s(),onChange:X,report:N,waitUntilReady:q}),a(w,Y,q),d.inject([k],t=>{let n=A(t).registerLeader({source:j,bindings:[{id:`tasks.open`,path:[{key:`t`,label:`tasks`,detail:`session task list`,order:65},{key:`l`,label:`list`,detail:`tasks in this session`}],command:{name:e}}]}),r=()=>n.dispose();return U=r,()=>{r(),U===r&&(U=void 0)}}),w.registerMessageRenderer(i,(e,t,n)=>{let r=typeof e.content==`string`?e.content:``;return new ne(n.fg(`muted`,r),0,0)});let $=l();$!==`off`&&w.registerShortcut($,{description:`Collapse or expand the task overlay`,handler:async e=>{!P||!e.hasUI||(await q(e),!(!P||!Q.isRegistered())&&Q.toggleCollapse())}}),w.on(`session_start`,async(e,n)=>{if(!P)return;let r=++F;W!==void 0&&J();let i=n,a=async e=>{let n=()=>!e?.aborted&&P&&r===F;try{let e=i.sessionManager?.getSessionId();if(!e)throw Error(`doom-task requires a session id`);W=e,H=!i.hasUI,V?.update(),Y.configureSession(g(e));let r=await h(Y.storePath);if(!n())return;let a=m()?{removed:[],errors:[]}:await _(Y.storePath,c());if(!n())return;let o=[...r.errors,...a.errors];for(let e of o)N.warn(t.storeSweepFailed,Error(e));if(o.length>0&&i.hasUI&&i.ui.notify(o.join(`
2
- `),`warning`),i.hasUI&&Q.setUICtx(i.ui),await Y.readAsync(n),!n()||(B=Y.onExternalChange(X),await Z.reconcile(n),!n()))return;X()}catch(e){if(!n())return;throw J(),N.error(t.sessionStartFailed,e),e}},o=O(d);if(!o){let e=a();return G={sessionManager:n.sessionManager,operation:e},e}let s=G?.operation,l=(async()=>{s&&await Promise.allSettled([s]),!(!P||r!==F)&&await o.start(j,`${n.sessionManager.getSessionId()}:${r}`,async e=>(await a(e),{value:void 0})).wait()})();G={sessionManager:n.sessionManager,operation:l},Promise.allSettled([l])}),w.on(`session_compact`,X),w.on(`session_tree`,X),w.on(`tool_execution_end`,e=>{P&&e.toolName===`task`&&X()})}async function N(e){let t=await w(e,j),n=t.root.plugin(P,{pi:e});try{await n}catch(e){try{await n.dispose()}finally{await t.dispose()}throw e}let r;e.on(`session_shutdown`,()=>r??=(async()=>{try{await n.dispose()}finally{await t.dispose()}})())}function P(e,t){M(e,t.pi)}export{N as default,N as taskExtension,M as installTaskRuntime};
1
+ import{COMMAND_NAME as e}from"../../schemas/task.mjs";import{TASK_EVENT as t}from"../../types/telemetry.mjs";import{BACKGROUND_WORK_PROVIDER as n,DelegationManager as r,NOTIFY_CUSTOM_TYPE as i}from"../../services/delegation/manager.mjs";import{registerTasksCommand as a}from"../../commands/index.mjs";import{getDelegationTimeoutMs as o,getMaxTasks as s,getStoreTtlMs as c,resolveCollapseKey as l}from"../../types/config.mjs";import{registerTaskTool as u}from"../../commands/task/taskTool.mjs";import{TASK_CONTEXT_CONTRIBUTION_SOURCE as d,createTaskContextContribution as f}from"../../services/contextContribution.mjs";import{narrateTaskCommit as p}from"../../services/narration/taskNarration.mjs";import{hasStorePathOverride as m,removeLegacyStoreDirectoryAsync as h,resolveSessionKey as ee,sweepStoreFilesAsync as te}from"../store/paths.mjs";import{TaskStore as ne}from"../store/taskStore.mjs";import{TaskOverlay as re}from"../../tui/taskOverlay2.mjs";import{createNodeDelegationPlatform as ie}from"../node/delegationPlatform.mjs";import{createTaskErrorReporter as ae,toFailureReporter as g}from"../telemetry/logSinkTelemetry.mjs";import{Text as _}from"@earendil-works/pi-tui";import{DOOM_DELEGATION_SERVICE as v,readDoomDelegationService as oe}from"@agimon-ai/doompi-extension-contracts/delegation";import{DOOM_BACKGROUND_WORK_SERVICE as se,readDoomBackgroundWorkService as ce}from"@agimon-ai/doompi-extension-contracts/background-work";import{connectDoomCordisHost as y}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_CONTEXT_CONTRIBUTIONS_SERVICE as b,requireDoomContextContributions as x}from"@agimon-ai/doompi-extension-contracts/context-contributions";import{DOOM_NARRATION_SERVICE as S,createNarrationRequest as C,requireDoomNarrationService as w}from"@agimon-ai/doompi-extension-contracts/narration";import{readDoomReadinessCoordinator as T}from"@agimon-ai/doompi-extension-contracts/readiness";import{DOOM_UI_HUB_SERVICE as E,requireDoomUiHub as le}from"@agimon-ai/doompi-extension-contracts/ui-hub";const D=d,O=`error.type`,k=`task.stage`,A={sessionId:`session_id`,storeCleanup:`store_cleanup`,storeSweep:`store_sweep`,storeRead:`store_read`,reconcile:`reconcile`,refresh:`refresh`};function j(e){return e instanceof Error?e.name||`Error`:typeof e==`string`?`StringError`:e&&typeof e==`object`?`ObjectError`:`UnknownError`}function M(d,y){let M=ae(),N=g(M),P=!0,F=0,I,L,R,z,B,V,H=!1,U,W,G,K=e=>{M.recordWarning(`doom_task.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-task could not record a narration failure: ${String(e)}`)})},ue={narrate(e){let t=I,n=C(e);if(!(!t||!n))try{Promise.resolve(t.request(n)).catch(K)}catch(e){K(e)}}};d.inject([S],e=>{let t=w(e);return I=t,()=>{I===t&&(I=void 0)}});let q=async(e,t)=>{t?.throwIfAborted();let n=G;if(!n)throw Error(`doom-task session initialization has not started`);if(n.sessionManager!==e.sessionManager)throw Error(`doom-task readiness belongs to a stale Pi session`);if(await n.operation,t?.throwIfAborted(),!P||n!==G||!W)throw Error(`doom-task readiness belongs to a stale extension generation`)},J=()=>{B?.(),B=void 0,H=!1,V?.update(),R?.reset(),z?.dispose(),W=void 0};d.effect(()=>async()=>{P=!1,F+=1;let e=G?.operation;G=void 0;try{e&&await Promise.allSettled([e]),J(),R?.dispose(),U?.(),U=void 0,L?.dispose()}finally{await M.shutdown()}},D);let Y=new ne({report:N,onCommitted:(e,t)=>p(ue,e,t)});L=Y,d.inject([b],e=>{let t=x(e).register(f(()=>Y.snapshot.tasks));return()=>t.dispose()});let X=()=>{P&&(z?.update(),V?.update())},Z=new r({store:Y,cwd:process.cwd(),platform:ie(),notify:(e,t)=>y.sendMessage(e,t),getSessionId:()=>W,onChange:X,runTimeoutMs:o(),report:N,onNotifyError:(e,t)=>{M.recordNotificationError(e,t)}});R=Z,d.inject([v],e=>{let t=oe(e);if(t)return Z.bind(e,t)}),d.inject([se],e=>{let t=ce(e);if(!t)return;let r=t.register({provider:n,listActiveWork:()=>H?Z.listActiveWork():[]});return V=r,()=>{r.dispose(),V===r&&(V=void 0)}});let Q=new re({getTasks:()=>Y.snapshot.tasks,delegation:Z});z=Q,u(y,{store:Y,delegation:Z,maxTasks:s(),onChange:X,report:N,waitUntilReady:q}),a(y,Y,q),d.inject([E],t=>{let n=le(t).registerLeader({source:D,bindings:[{id:`tasks.open`,path:[{key:`t`,label:`tasks`,detail:`session task list`,order:65},{key:`l`,label:`list`,detail:`tasks in this session`}],command:{name:e}}]}),r=()=>n.dispose();return U=r,()=>{r(),U===r&&(U=void 0)}}),y.registerMessageRenderer(i,(e,t,n)=>{let r=typeof e.content==`string`?e.content:``;return new _(n.fg(`muted`,r),0,0)});let $=l();$!==`off`&&y.registerShortcut($,{description:`Collapse or expand the task overlay`,handler:async e=>{!P||!e.hasUI||(await q(e),!(!P||!Q.isRegistered())&&Q.toggleCollapse())}}),y.on(`session_start`,async(e,n)=>{if(!P)return;let r=++F;W!==void 0&&J();let i=n,a=async e=>{let n=()=>!e?.aborted&&P&&r===F,a=A.sessionId,o=async(e,n)=>{a=e;try{await n()}catch(n){N.warn(t.sessionStartDegraded,n,{[k]:e,[O]:j(n)})}};try{let e=i.sessionManager?.getSessionId();if(!e)throw Error(`doom-task requires a session id`);W=e,H=!i.hasUI,V?.update(),Y.configureSession(ee(e)),a=A.storeCleanup;let r=await h(Y.storePath);if(!n())return;a=A.storeSweep;let s=m()?{removed:[],errors:[]}:await te(Y.storePath,c());if(!n())return;let l=[...r.errors,...s.errors];for(let e of l)N.warn(t.storeSweepFailed,Error(e));if(l.length>0&&i.hasUI&&i.ui.notify(l.join(`
2
+ `),`warning`),i.hasUI&&Q.setUICtx(i.ui),await o(A.storeRead,async()=>{await Y.readAsync(n)}),!n()||(B=Y.onExternalChange(X),await o(A.reconcile,async()=>{await Z.reconcile(n)}),!n()))return;a=A.refresh,X()}catch(e){if(!n())return;throw J(),N.error(t.sessionStartFailed,e,{[k]:a,[O]:j(e)}),e}},o=T(d);if(!o){let e=a();return G={sessionManager:n.sessionManager,operation:e},e}let s=G?.operation,l=(async()=>{s&&await Promise.allSettled([s]),!(!P||r!==F)&&await o.start(D,`${n.sessionManager.getSessionId()}:${r}`,async e=>(await a(e),{value:void 0})).wait()})();G={sessionManager:n.sessionManager,operation:l},Promise.allSettled([l])}),y.on(`session_compact`,X),y.on(`session_tree`,X),y.on(`tool_execution_end`,e=>{P&&e.toolName===`task`&&X()})}async function N(e){let t=await y(e,D),n=t.root.plugin(P,{pi:e});try{await n}catch(e){try{await n.dispose()}finally{await t.dispose()}throw e}let r;e.on(`session_shutdown`,()=>r??=(async()=>{try{await n.dispose()}finally{await t.dispose()}})())}function P(e,t){M(e,t.pi)}export{N as default,N as taskExtension,M as installTaskRuntime};
3
3
  //# sourceMappingURL=extension.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n type BackgroundWorkProviderHandle,\n readDoomBackgroundWorkService,\n} from '@agimon-ai/doompi-extension-contracts/background-work';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_CONTEXT_CONTRIBUTIONS_SERVICE,\n requireDoomContextContributions,\n} from '@agimon-ai/doompi-extension-contracts/context-contributions';\nimport { DOOM_DELEGATION_SERVICE, readDoomDelegationService } from '@agimon-ai/doompi-extension-contracts/delegation';\nimport {\n createNarrationRequest,\n DOOM_NARRATION_SERVICE,\n type DoomNarrationService,\n requireDoomNarrationService,\n} from '@agimon-ai/doompi-extension-contracts/narration';\nimport { readDoomReadinessCoordinator } from '@agimon-ai/doompi-extension-contracts/readiness';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { Text } from '@earendil-works/pi-tui';\nimport { registerTasksCommand } from '../../commands/index.ts';\nimport { registerTaskTool } from '../../commands/task/taskTool.ts';\nimport { COMMAND_NAME, TOOL_NAME } from '../../schemas/task.ts';\nimport { BACKGROUND_WORK_PROVIDER, DelegationManager, NOTIFY_CUSTOM_TYPE } from '../../services/delegation/manager.ts';\nimport { createTaskContextContribution, TASK_CONTEXT_CONTRIBUTION_SOURCE } from '../../services/contextContribution.ts';\nimport { narrateTaskCommit, type TaskNarrationSink } from '../../services/narration/taskNarration.ts';\nimport {\n hasStorePathOverride,\n removeLegacyStoreDirectoryAsync,\n resolveSessionKey,\n sweepStoreFilesAsync,\n} from '../../adapters/store/paths';\nimport { TaskStore } from '../../adapters/store/taskStore';\nimport { TaskOverlay } from '../../tui/taskOverlay.ts';\nimport {\n COLLAPSE_KEY_OFF,\n getDelegationTimeoutMs,\n getMaxTasks,\n getStoreTtlMs,\n resolveCollapseKey,\n} from '../../types/config.ts';\nimport { createNodeDelegationPlatform } from '../node/delegationPlatform.ts';\nimport { createTaskErrorReporter, TASK_EVENT, toFailureReporter } from '../telemetry/logSinkTelemetry.ts';\n\ninterface SessionContextLike {\n hasUI: boolean;\n ui: Parameters<TaskOverlay['setUICtx']>[0];\n sessionManager?: { getSessionId(): string };\n}\n\nconst LEADER_SOURCE = TASK_CONTEXT_CONTRIBUTION_SOURCE;\nconst NARRATION_FAILED_EVENT = 'doom_task.narration_failed';\n/** Between doom-plan's 60 and the core help group's 70, so `t` sits after `p`. */\nconst LEADER_GROUP_ORDER = 65;\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_COMPACT_EVENT = 'session_compact';\nconst SESSION_TREE_EVENT = 'session_tree';\nconst TOOL_EXECUTION_END_EVENT = 'tool_execution_end';\n\n/** Install Task state and external resources into its host-owned Cordis plugin fiber. */\nexport function installTaskRuntime(cordis: Context, pi: ExtensionAPI): void {\n const errorReporter = createTaskErrorReporter();\n const report = toFailureReporter(errorReporter);\n let active = true;\n let sessionGeneration = 0;\n let narrationService: DoomNarrationService | undefined;\n let store: TaskStore | undefined;\n let delegation: DelegationManager | undefined;\n let overlay: TaskOverlay | undefined;\n let unwatch: (() => void) | undefined;\n let backgroundWork: BackgroundWorkProviderHandle | undefined;\n let backgroundWorkEnabled = false;\n let disposeLeader: (() => void) | undefined;\n let sessionId: string | undefined;\n let sessionInitialization:\n | {\n readonly sessionManager: object;\n readonly operation: Promise<void>;\n }\n | undefined;\n\n const reportNarrationFailure = (error: unknown): void => {\n void errorReporter.recordWarning(NARRATION_FAILED_EVENT, error).catch((telemetryError: unknown) => {\n process.emitWarning(`Doom-task could not record a narration failure: ${String(telemetryError)}`);\n });\n };\n const narrationSink: TaskNarrationSink = {\n narrate(text) {\n const service = narrationService;\n const request = createNarrationRequest(text);\n if (!service || !request) return;\n try {\n void Promise.resolve(service.request(request)).catch(reportNarrationFailure);\n } catch (error) {\n reportNarrationFailure(error);\n }\n },\n };\n\n cordis.inject([DOOM_NARRATION_SERVICE], (serviceContext) => {\n const service = requireDoomNarrationService(serviceContext);\n narrationService = service;\n return () => {\n if (narrationService === service) narrationService = undefined;\n };\n });\n\n const waitForSessionReadiness = async (\n context: { readonly sessionManager: object },\n signal?: AbortSignal,\n ): Promise<void> => {\n signal?.throwIfAborted();\n const current = sessionInitialization;\n if (!current) throw new Error('doom-task session initialization has not started');\n if (current.sessionManager !== context.sessionManager) {\n throw new Error('doom-task readiness belongs to a stale Pi session');\n }\n await current.operation;\n signal?.throwIfAborted();\n if (!active || current !== sessionInitialization || !sessionId) {\n throw new Error('doom-task readiness belongs to a stale extension generation');\n }\n };\n\n const clearSessionResources = (): void => {\n unwatch?.();\n unwatch = undefined;\n backgroundWorkEnabled = false;\n backgroundWork?.update();\n delegation?.reset();\n overlay?.dispose();\n sessionId = undefined;\n };\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n const pendingInitialization = sessionInitialization?.operation;\n sessionInitialization = undefined;\n try {\n if (pendingInitialization) await Promise.allSettled([pendingInitialization]);\n clearSessionResources();\n delegation?.dispose();\n disposeLeader?.();\n disposeLeader = undefined;\n store?.dispose();\n } finally {\n await errorReporter.shutdown();\n }\n },\n LEADER_SOURCE,\n );\n\n const taskStore = new TaskStore({\n report,\n onCommitted: (previous, committed) => narrateTaskCommit(narrationSink, previous, committed),\n });\n store = taskStore;\n\n cordis.inject([DOOM_CONTEXT_CONTRIBUTIONS_SERVICE], (contextContributionsContext) => {\n const registration = requireDoomContextContributions(contextContributionsContext).register(\n createTaskContextContribution(() => taskStore.snapshot.tasks),\n );\n return () => registration.dispose();\n });\n\n const refresh = (): void => {\n if (!active) return;\n overlay?.update();\n backgroundWork?.update();\n };\n\n const delegationManager = new DelegationManager({\n store: taskStore,\n cwd: process.cwd(),\n platform: createNodeDelegationPlatform(),\n notify: (message, options) => pi.sendMessage(message, options),\n getSessionId: () => sessionId,\n onChange: refresh,\n runTimeoutMs: getDelegationTimeoutMs(),\n report,\n onNotifyError: (error, taskId) => {\n void errorReporter.recordNotificationError(error, taskId);\n },\n });\n delegation = delegationManager;\n\n cordis.inject([DOOM_DELEGATION_SERVICE], (serviceContext) => {\n const service = readDoomDelegationService(serviceContext);\n if (!service) return undefined;\n return delegationManager.bind(serviceContext, service);\n });\n\n cordis.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const handle = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => (backgroundWorkEnabled ? delegationManager.listActiveWork() : []),\n });\n backgroundWork = handle;\n return () => {\n handle.dispose();\n if (backgroundWork === handle) backgroundWork = undefined;\n };\n });\n\n const taskOverlay = new TaskOverlay({ getTasks: () => taskStore.snapshot.tasks, delegation: delegationManager });\n overlay = taskOverlay;\n\n registerTaskTool(pi, {\n store: taskStore,\n delegation: delegationManager,\n maxTasks: getMaxTasks(),\n onChange: refresh,\n report,\n waitUntilReady: waitForSessionReadiness,\n });\n registerTasksCommand(pi, taskStore, waitForSessionReadiness);\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader({\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'tasks.open',\n path: [\n { key: 't', label: 'tasks', detail: 'session task list', order: LEADER_GROUP_ORDER },\n { key: 'l', label: 'list', detail: 'tasks in this session' },\n ],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n const dispose = (): void => contribution.dispose();\n disposeLeader = dispose;\n return () => {\n dispose();\n if (disposeLeader === dispose) disposeLeader = undefined;\n };\n });\n\n pi.registerMessageRenderer(NOTIFY_CUSTOM_TYPE, (message, _options, theme) => {\n const content = typeof message.content === 'string' ? message.content : '';\n return new Text(theme.fg('muted', content), 0, 0);\n });\n\n const collapseKey = resolveCollapseKey();\n if (collapseKey !== COLLAPSE_KEY_OFF) {\n pi.registerShortcut(collapseKey as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (ctx) => {\n if (!active || !ctx.hasUI) return;\n await waitForSessionReadiness(ctx);\n if (!active || !taskOverlay.isRegistered()) return;\n taskOverlay.toggleCollapse();\n },\n });\n }\n\n pi.on(SESSION_START_EVENT, async (_event, ctx) => {\n if (!active) return;\n const ownGeneration = ++sessionGeneration;\n if (sessionId !== undefined) clearSessionResources();\n const context = ctx as unknown as SessionContextLike;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n const isCurrent = (): boolean => !signal?.aborted && active && ownGeneration === sessionGeneration;\n try {\n const currentSessionId = context.sessionManager?.getSessionId();\n if (!currentSessionId) throw new Error('doom-task requires a session id');\n sessionId = currentSessionId;\n backgroundWorkEnabled = !context.hasUI;\n backgroundWork?.update();\n taskStore.configureSession(resolveSessionKey(currentSessionId));\n const cleanup = await removeLegacyStoreDirectoryAsync(taskStore.storePath);\n if (!isCurrent()) return;\n const sweep = hasStorePathOverride()\n ? { removed: [], errors: [] }\n : await sweepStoreFilesAsync(taskStore.storePath, getStoreTtlMs());\n if (!isCurrent()) return;\n const storeErrors = [...cleanup.errors, ...sweep.errors];\n for (const failure of storeErrors) report.warn(TASK_EVENT.storeSweepFailed, new Error(failure));\n if (storeErrors.length > 0 && context.hasUI) context.ui.notify(storeErrors.join('\\n'), 'warning');\n if (context.hasUI) taskOverlay.setUICtx(context.ui);\n await taskStore.readAsync(isCurrent);\n if (!isCurrent()) return;\n unwatch = taskStore.onExternalChange(refresh);\n await delegationManager.reconcile(isCurrent);\n if (!isCurrent()) return;\n refresh();\n } catch (error) {\n if (!isCurrent()) return;\n clearSessionResources();\n report.error(TASK_EVENT.sessionStartFailed, error);\n throw error;\n }\n };\n\n const coordinator = readDoomReadinessCoordinator(cordis);\n if (!coordinator) {\n const operation = initialize();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n return operation;\n }\n\n const previous = sessionInitialization?.operation;\n const operation = (async (): Promise<void> => {\n if (previous) await Promise.allSettled([previous]);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n LEADER_SOURCE,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n // Config's coordinator owns the single failure notification. This detached\n // observer only keeps a failed background generation from going unhandled.\n void Promise.allSettled([operation]);\n return undefined;\n });\n\n pi.on(SESSION_COMPACT_EVENT, refresh);\n pi.on(SESSION_TREE_EVENT, refresh);\n pi.on(TOOL_EXECUTION_END_EVENT, (event) => {\n if (active && event.toolName === TOOL_NAME) refresh();\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function taskExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, LEADER_SOURCE);\n const fiber = connection.root.plugin(taskPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface TaskPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction taskPlugin(cordis: Context, config: TaskPluginConfig): void {\n installTaskRuntime(cordis, config.pi);\n}\n\nexport default taskExtension;\n"],"mappings":"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"}
1
+ {"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n type BackgroundWorkProviderHandle,\n readDoomBackgroundWorkService,\n} from '@agimon-ai/doompi-extension-contracts/background-work';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_CONTEXT_CONTRIBUTIONS_SERVICE,\n requireDoomContextContributions,\n} from '@agimon-ai/doompi-extension-contracts/context-contributions';\nimport { DOOM_DELEGATION_SERVICE, readDoomDelegationService } from '@agimon-ai/doompi-extension-contracts/delegation';\nimport {\n createNarrationRequest,\n DOOM_NARRATION_SERVICE,\n type DoomNarrationService,\n requireDoomNarrationService,\n} from '@agimon-ai/doompi-extension-contracts/narration';\nimport { readDoomReadinessCoordinator } from '@agimon-ai/doompi-extension-contracts/readiness';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { Text } from '@earendil-works/pi-tui';\nimport { registerTasksCommand } from '../../commands/index.ts';\nimport { registerTaskTool } from '../../commands/task/taskTool.ts';\nimport { COMMAND_NAME, TOOL_NAME } from '../../schemas/task.ts';\nimport { BACKGROUND_WORK_PROVIDER, DelegationManager, NOTIFY_CUSTOM_TYPE } from '../../services/delegation/manager.ts';\nimport { createTaskContextContribution, TASK_CONTEXT_CONTRIBUTION_SOURCE } from '../../services/contextContribution.ts';\nimport { narrateTaskCommit, type TaskNarrationSink } from '../../services/narration/taskNarration.ts';\nimport {\n hasStorePathOverride,\n removeLegacyStoreDirectoryAsync,\n resolveSessionKey,\n sweepStoreFilesAsync,\n} from '../../adapters/store/paths';\nimport { TaskStore } from '../../adapters/store/taskStore';\nimport { TaskOverlay } from '../../tui/taskOverlay.ts';\nimport {\n COLLAPSE_KEY_OFF,\n getDelegationTimeoutMs,\n getMaxTasks,\n getStoreTtlMs,\n resolveCollapseKey,\n} from '../../types/config.ts';\nimport { createNodeDelegationPlatform } from '../node/delegationPlatform.ts';\nimport { createTaskErrorReporter, TASK_EVENT, toFailureReporter } from '../telemetry/logSinkTelemetry.ts';\n\ninterface SessionContextLike {\n hasUI: boolean;\n ui: Parameters<TaskOverlay['setUICtx']>[0];\n sessionManager?: { getSessionId(): string };\n}\n\nconst LEADER_SOURCE = TASK_CONTEXT_CONTRIBUTION_SOURCE;\nconst NARRATION_FAILED_EVENT = 'doom_task.narration_failed';\n/** Between doom-plan's 60 and the core help group's 70, so `t` sits after `p`. */\nconst LEADER_GROUP_ORDER = 65;\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_COMPACT_EVENT = 'session_compact';\nconst SESSION_TREE_EVENT = 'session_tree';\nconst TOOL_EXECUTION_END_EVENT = 'tool_execution_end';\nconst ERROR_TYPE_ATTRIBUTE = 'error.type';\n/**\n * Which part of session start ran. The telemetry sanitizer drops free text, so\n * the failing branch has to travel as a stable low-cardinality token.\n */\nconst SESSION_START_STAGE_ATTRIBUTE = 'task.stage';\nconst SESSION_START_STAGE = {\n sessionId: 'session_id',\n storeCleanup: 'store_cleanup',\n storeSweep: 'store_sweep',\n storeRead: 'store_read',\n reconcile: 'reconcile',\n refresh: 'refresh',\n} as const;\ntype SessionStartStage = (typeof SESSION_START_STAGE)[keyof typeof SESSION_START_STAGE];\n\n/**\n * Mirror of the telemetry sink's own `error.type` token. The sink overwrites it\n * for any defined error, so this only guarantees the attribute exists when the\n * thrown value is `undefined`.\n */\nfunction errorTypeToken(error: unknown): string {\n if (error instanceof Error) return error.name || 'Error';\n if (typeof error === 'string') return 'StringError';\n if (error && typeof error === 'object') return 'ObjectError';\n return 'UnknownError';\n}\n/** Install Task state and external resources into its host-owned Cordis plugin fiber. */\nexport function installTaskRuntime(cordis: Context, pi: ExtensionAPI): void {\n const errorReporter = createTaskErrorReporter();\n const report = toFailureReporter(errorReporter);\n let active = true;\n let sessionGeneration = 0;\n let narrationService: DoomNarrationService | undefined;\n let store: TaskStore | undefined;\n let delegation: DelegationManager | undefined;\n let overlay: TaskOverlay | undefined;\n let unwatch: (() => void) | undefined;\n let backgroundWork: BackgroundWorkProviderHandle | undefined;\n let backgroundWorkEnabled = false;\n let disposeLeader: (() => void) | undefined;\n let sessionId: string | undefined;\n let sessionInitialization:\n | {\n readonly sessionManager: object;\n readonly operation: Promise<void>;\n }\n | undefined;\n\n const reportNarrationFailure = (error: unknown): void => {\n void errorReporter.recordWarning(NARRATION_FAILED_EVENT, error).catch((telemetryError: unknown) => {\n process.emitWarning(`Doom-task could not record a narration failure: ${String(telemetryError)}`);\n });\n };\n const narrationSink: TaskNarrationSink = {\n narrate(text) {\n const service = narrationService;\n const request = createNarrationRequest(text);\n if (!service || !request) return;\n try {\n void Promise.resolve(service.request(request)).catch(reportNarrationFailure);\n } catch (error) {\n reportNarrationFailure(error);\n }\n },\n };\n\n cordis.inject([DOOM_NARRATION_SERVICE], (serviceContext) => {\n const service = requireDoomNarrationService(serviceContext);\n narrationService = service;\n return () => {\n if (narrationService === service) narrationService = undefined;\n };\n });\n\n const waitForSessionReadiness = async (\n context: { readonly sessionManager: object },\n signal?: AbortSignal,\n ): Promise<void> => {\n signal?.throwIfAborted();\n const current = sessionInitialization;\n if (!current) throw new Error('doom-task session initialization has not started');\n if (current.sessionManager !== context.sessionManager) {\n throw new Error('doom-task readiness belongs to a stale Pi session');\n }\n await current.operation;\n signal?.throwIfAborted();\n if (!active || current !== sessionInitialization || !sessionId) {\n throw new Error('doom-task readiness belongs to a stale extension generation');\n }\n };\n\n const clearSessionResources = (): void => {\n unwatch?.();\n unwatch = undefined;\n backgroundWorkEnabled = false;\n backgroundWork?.update();\n delegation?.reset();\n overlay?.dispose();\n sessionId = undefined;\n };\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n const pendingInitialization = sessionInitialization?.operation;\n sessionInitialization = undefined;\n try {\n if (pendingInitialization) await Promise.allSettled([pendingInitialization]);\n clearSessionResources();\n delegation?.dispose();\n disposeLeader?.();\n disposeLeader = undefined;\n store?.dispose();\n } finally {\n await errorReporter.shutdown();\n }\n },\n LEADER_SOURCE,\n );\n\n const taskStore = new TaskStore({\n report,\n onCommitted: (previous, committed) => narrateTaskCommit(narrationSink, previous, committed),\n });\n store = taskStore;\n\n cordis.inject([DOOM_CONTEXT_CONTRIBUTIONS_SERVICE], (contextContributionsContext) => {\n const registration = requireDoomContextContributions(contextContributionsContext).register(\n createTaskContextContribution(() => taskStore.snapshot.tasks),\n );\n return () => registration.dispose();\n });\n\n const refresh = (): void => {\n if (!active) return;\n overlay?.update();\n backgroundWork?.update();\n };\n\n const delegationManager = new DelegationManager({\n store: taskStore,\n cwd: process.cwd(),\n platform: createNodeDelegationPlatform(),\n notify: (message, options) => pi.sendMessage(message, options),\n getSessionId: () => sessionId,\n onChange: refresh,\n runTimeoutMs: getDelegationTimeoutMs(),\n report,\n onNotifyError: (error, taskId) => {\n void errorReporter.recordNotificationError(error, taskId);\n },\n });\n delegation = delegationManager;\n\n cordis.inject([DOOM_DELEGATION_SERVICE], (serviceContext) => {\n const service = readDoomDelegationService(serviceContext);\n if (!service) return undefined;\n return delegationManager.bind(serviceContext, service);\n });\n\n cordis.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const handle = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => (backgroundWorkEnabled ? delegationManager.listActiveWork() : []),\n });\n backgroundWork = handle;\n return () => {\n handle.dispose();\n if (backgroundWork === handle) backgroundWork = undefined;\n };\n });\n\n const taskOverlay = new TaskOverlay({ getTasks: () => taskStore.snapshot.tasks, delegation: delegationManager });\n overlay = taskOverlay;\n\n registerTaskTool(pi, {\n store: taskStore,\n delegation: delegationManager,\n maxTasks: getMaxTasks(),\n onChange: refresh,\n report,\n waitUntilReady: waitForSessionReadiness,\n });\n registerTasksCommand(pi, taskStore, waitForSessionReadiness);\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = requireDoomUiHub(uiContext).registerLeader({\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'tasks.open',\n path: [\n { key: 't', label: 'tasks', detail: 'session task list', order: LEADER_GROUP_ORDER },\n { key: 'l', label: 'list', detail: 'tasks in this session' },\n ],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n const dispose = (): void => contribution.dispose();\n disposeLeader = dispose;\n return () => {\n dispose();\n if (disposeLeader === dispose) disposeLeader = undefined;\n };\n });\n\n pi.registerMessageRenderer(NOTIFY_CUSTOM_TYPE, (message, _options, theme) => {\n const content = typeof message.content === 'string' ? message.content : '';\n return new Text(theme.fg('muted', content), 0, 0);\n });\n\n const collapseKey = resolveCollapseKey();\n if (collapseKey !== COLLAPSE_KEY_OFF) {\n pi.registerShortcut(collapseKey as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (ctx) => {\n if (!active || !ctx.hasUI) return;\n await waitForSessionReadiness(ctx);\n if (!active || !taskOverlay.isRegistered()) return;\n taskOverlay.toggleCollapse();\n },\n });\n }\n\n pi.on(SESSION_START_EVENT, async (_event, ctx) => {\n if (!active) return;\n const ownGeneration = ++sessionGeneration;\n if (sessionId !== undefined) clearSessionResources();\n const context = ctx as unknown as SessionContextLike;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n const isCurrent = (): boolean => !signal?.aborted && active && ownGeneration === sessionGeneration;\n let stage: SessionStartStage = SESSION_START_STAGE.sessionId;\n /**\n * Run a stage whose failure only degrades task visibility. The session\n * still starts, and the warning keeps the degraded state from being silent.\n */\n const degrade = async (next: SessionStartStage, operation: () => Promise<void>): Promise<void> => {\n stage = next;\n try {\n await operation();\n } catch (error) {\n report.warn(TASK_EVENT.sessionStartDegraded, error, {\n [SESSION_START_STAGE_ATTRIBUTE]: next,\n [ERROR_TYPE_ATTRIBUTE]: errorTypeToken(error),\n });\n }\n };\n try {\n const currentSessionId = context.sessionManager?.getSessionId();\n if (!currentSessionId) throw new Error('doom-task requires a session id');\n sessionId = currentSessionId;\n backgroundWorkEnabled = !context.hasUI;\n backgroundWork?.update();\n taskStore.configureSession(resolveSessionKey(currentSessionId));\n stage = SESSION_START_STAGE.storeCleanup;\n const cleanup = await removeLegacyStoreDirectoryAsync(taskStore.storePath);\n if (!isCurrent()) return;\n stage = SESSION_START_STAGE.storeSweep;\n const sweep = hasStorePathOverride()\n ? { removed: [], errors: [] }\n : await sweepStoreFilesAsync(taskStore.storePath, getStoreTtlMs());\n if (!isCurrent()) return;\n const storeErrors = [...cleanup.errors, ...sweep.errors];\n for (const failure of storeErrors) report.warn(TASK_EVENT.storeSweepFailed, new Error(failure));\n if (storeErrors.length > 0 && context.hasUI) context.ui.notify(storeErrors.join('\\n'), 'warning');\n if (context.hasUI) taskOverlay.setUICtx(context.ui);\n await degrade(SESSION_START_STAGE.storeRead, async () => {\n await taskStore.readAsync(isCurrent);\n });\n if (!isCurrent()) return;\n unwatch = taskStore.onExternalChange(refresh);\n await degrade(SESSION_START_STAGE.reconcile, async () => {\n await delegationManager.reconcile(isCurrent);\n });\n if (!isCurrent()) return;\n stage = SESSION_START_STAGE.refresh;\n refresh();\n } catch (error) {\n if (!isCurrent()) return;\n clearSessionResources();\n report.error(TASK_EVENT.sessionStartFailed, error, {\n [SESSION_START_STAGE_ATTRIBUTE]: stage,\n [ERROR_TYPE_ATTRIBUTE]: errorTypeToken(error),\n });\n throw error;\n }\n };\n\n const coordinator = readDoomReadinessCoordinator(cordis);\n if (!coordinator) {\n const operation = initialize();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n return operation;\n }\n\n const previous = sessionInitialization?.operation;\n const operation = (async (): Promise<void> => {\n if (previous) await Promise.allSettled([previous]);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n LEADER_SOURCE,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n sessionInitialization = { sessionManager: ctx.sessionManager, operation };\n // Config's coordinator owns the single failure notification. This detached\n // observer only keeps a failed background generation from going unhandled.\n void Promise.allSettled([operation]);\n return undefined;\n });\n\n pi.on(SESSION_COMPACT_EVENT, refresh);\n pi.on(SESSION_TREE_EVENT, refresh);\n pi.on(TOOL_EXECUTION_END_EVENT, (event) => {\n if (active && event.toolName === TOOL_NAME) refresh();\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function taskExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, LEADER_SOURCE);\n const fiber = connection.root.plugin(taskPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface TaskPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction taskPlugin(cordis: Context, config: TaskPluginConfig): void {\n installTaskRuntime(cordis, config.pi);\n}\n\nexport default taskExtension;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e={notificationFailed:`doom_task.notification_failed`,storeReadFailed:`doom_task.store_read_failed`,storeLockTimeout:`doom_task.store_lock_timeout`,storeLockBreakFailed:`doom_task.store_lock_break_failed`,storeWatchFailed:`doom_task.store_watch_failed`,storeListenerFailed:`doom_task.store_listener_failed`,storeCommitListenerFailed:`doom_task.store_commit_listener_failed`,storeSweepFailed:`doom_task.store_sweep_failed`,sessionStartFailed:`doom_task.session_start_failed`,toolFailed:`doom_task.tool_failed`,delegationRequestFailed:`doom_task.delegation_request_failed`,delegationStartFailed:`doom_task.delegation_start_failed`,delegationResponseFailed:`doom_task.delegation_response_failed`,delegationSettleFailed:`doom_task.delegation_settle_failed`,delegationTimedOut:`doom_task.delegation_timed_out`,delegationOrphaned:`doom_task.delegation_orphaned`,delegationAssigned:`doom_task.delegation_assigned`,delegationCompleted:`doom_task.delegation_completed`};exports.TASK_EVENT=e;
1
+ const e={notificationFailed:`doom_task.notification_failed`,storeReadFailed:`doom_task.store_read_failed`,storeLockTimeout:`doom_task.store_lock_timeout`,storeLockBreakFailed:`doom_task.store_lock_break_failed`,storeWatchFailed:`doom_task.store_watch_failed`,storeListenerFailed:`doom_task.store_listener_failed`,storeCommitListenerFailed:`doom_task.store_commit_listener_failed`,storeSweepFailed:`doom_task.store_sweep_failed`,sessionStartFailed:`doom_task.session_start_failed`,sessionStartDegraded:`doom_task.session_start_degraded`,toolFailed:`doom_task.tool_failed`,delegationRequestFailed:`doom_task.delegation_request_failed`,delegationStartFailed:`doom_task.delegation_start_failed`,delegationResponseFailed:`doom_task.delegation_response_failed`,delegationSettleFailed:`doom_task.delegation_settle_failed`,delegationTimedOut:`doom_task.delegation_timed_out`,delegationOrphaned:`doom_task.delegation_orphaned`,delegationAssigned:`doom_task.delegation_assigned`,delegationCompleted:`doom_task.delegation_completed`};exports.TASK_EVENT=e;
2
2
  //# sourceMappingURL=telemetry.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.cjs","names":[],"sources":["../../src/types/telemetry.ts"],"sourcesContent":["export const TASK_EVENT = {\n notificationFailed: 'doom_task.notification_failed',\n storeReadFailed: 'doom_task.store_read_failed',\n storeLockTimeout: 'doom_task.store_lock_timeout',\n storeLockBreakFailed: 'doom_task.store_lock_break_failed',\n storeWatchFailed: 'doom_task.store_watch_failed',\n storeListenerFailed: 'doom_task.store_listener_failed',\n storeCommitListenerFailed: 'doom_task.store_commit_listener_failed',\n storeSweepFailed: 'doom_task.store_sweep_failed',\n sessionStartFailed: 'doom_task.session_start_failed',\n toolFailed: 'doom_task.tool_failed',\n delegationRequestFailed: 'doom_task.delegation_request_failed',\n delegationStartFailed: 'doom_task.delegation_start_failed',\n delegationResponseFailed: 'doom_task.delegation_response_failed',\n delegationSettleFailed: 'doom_task.delegation_settle_failed',\n delegationTimedOut: 'doom_task.delegation_timed_out',\n delegationOrphaned: 'doom_task.delegation_orphaned',\n // Info-level lifecycle events. These are the measurement pair: the shape of\n // the brief that went out, and the cost of the run it produced.\n delegationAssigned: 'doom_task.delegation_assigned',\n delegationCompleted: 'doom_task.delegation_completed',\n} as const;\n\nexport type TaskEventName = (typeof TASK_EVENT)[keyof typeof TASK_EVENT];\nexport type TaskErrorAttributes = Record<string, string | number | boolean>;\nexport type TaskErrorSink = (event: TaskEventName, error: unknown, attributes?: TaskErrorAttributes) => void;\nexport type TaskEventSink = (event: TaskEventName, attributes?: TaskErrorAttributes) => void;\n\n/** Host-neutral telemetry port consumed by Task services and store adapters. */\nexport interface TaskFailureReporter {\n error: TaskErrorSink;\n warn: TaskErrorSink;\n /** Info-level event sink. Required so an unwired reporter fails to compile. */\n event: TaskEventSink;\n}\n"],"mappings":"AAAA,MAAa,EAAa,CACxB,mBAAoB,gCACpB,gBAAiB,8BACjB,iBAAkB,+BAClB,qBAAsB,oCACtB,iBAAkB,+BAClB,oBAAqB,kCACrB,0BAA2B,yCAC3B,iBAAkB,+BAClB,mBAAoB,iCACpB,WAAY,wBACZ,wBAAyB,sCACzB,sBAAuB,oCACvB,yBAA0B,uCAC1B,uBAAwB,qCACxB,mBAAoB,iCACpB,mBAAoB,gCAGpB,mBAAoB,gCACpB,oBAAqB,gCACvB"}
1
+ {"version":3,"file":"telemetry.cjs","names":[],"sources":["../../src/types/telemetry.ts"],"sourcesContent":["export const TASK_EVENT = {\n notificationFailed: 'doom_task.notification_failed',\n storeReadFailed: 'doom_task.store_read_failed',\n storeLockTimeout: 'doom_task.store_lock_timeout',\n storeLockBreakFailed: 'doom_task.store_lock_break_failed',\n storeWatchFailed: 'doom_task.store_watch_failed',\n storeListenerFailed: 'doom_task.store_listener_failed',\n storeCommitListenerFailed: 'doom_task.store_commit_listener_failed',\n storeSweepFailed: 'doom_task.store_sweep_failed',\n sessionStartFailed: 'doom_task.session_start_failed',\n sessionStartDegraded: 'doom_task.session_start_degraded',\n toolFailed: 'doom_task.tool_failed',\n delegationRequestFailed: 'doom_task.delegation_request_failed',\n delegationStartFailed: 'doom_task.delegation_start_failed',\n delegationResponseFailed: 'doom_task.delegation_response_failed',\n delegationSettleFailed: 'doom_task.delegation_settle_failed',\n delegationTimedOut: 'doom_task.delegation_timed_out',\n delegationOrphaned: 'doom_task.delegation_orphaned',\n // Info-level lifecycle events. These are the measurement pair: the shape of\n // the brief that went out, and the cost of the run it produced.\n delegationAssigned: 'doom_task.delegation_assigned',\n delegationCompleted: 'doom_task.delegation_completed',\n} as const;\n\nexport type TaskEventName = (typeof TASK_EVENT)[keyof typeof TASK_EVENT];\nexport type TaskErrorAttributes = Record<string, string | number | boolean>;\nexport type TaskErrorSink = (event: TaskEventName, error: unknown, attributes?: TaskErrorAttributes) => void;\nexport type TaskEventSink = (event: TaskEventName, attributes?: TaskErrorAttributes) => void;\n\n/** Host-neutral telemetry port consumed by Task services and store adapters. */\nexport interface TaskFailureReporter {\n error: TaskErrorSink;\n warn: TaskErrorSink;\n /** Info-level event sink. Required so an unwired reporter fails to compile. */\n event: TaskEventSink;\n}\n"],"mappings":"AAAA,MAAa,EAAa,CACxB,mBAAoB,gCACpB,gBAAiB,8BACjB,iBAAkB,+BAClB,qBAAsB,oCACtB,iBAAkB,+BAClB,oBAAqB,kCACrB,0BAA2B,yCAC3B,iBAAkB,+BAClB,mBAAoB,iCACpB,qBAAsB,mCACtB,WAAY,wBACZ,wBAAyB,sCACzB,sBAAuB,oCACvB,yBAA0B,uCAC1B,uBAAwB,qCACxB,mBAAoB,iCACpB,mBAAoB,gCAGpB,mBAAoB,gCACpB,oBAAqB,gCACvB"}
@@ -9,6 +9,7 @@ declare const TASK_EVENT: {
9
9
  readonly storeCommitListenerFailed: 'doom_task.store_commit_listener_failed';
10
10
  readonly storeSweepFailed: 'doom_task.store_sweep_failed';
11
11
  readonly sessionStartFailed: 'doom_task.session_start_failed';
12
+ readonly sessionStartDegraded: 'doom_task.session_start_degraded';
12
13
  readonly toolFailed: 'doom_task.tool_failed';
13
14
  readonly delegationRequestFailed: 'doom_task.delegation_request_failed';
14
15
  readonly delegationStartFailed: 'doom_task.delegation_start_failed';
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.cts","names":[],"sources":["../../src/types/telemetry.ts"],"mappings":";cAAa;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WAGA;WACA;;KAGU,wBAAwB,yBAAyB;KACjD,sBAAsB;KACtB,iBAAiB,OAAO,eAAe,gBAAgB,aAAa;KACpE,iBAAiB,OAAO,eAAe,aAAa;;UAG/C;EACf,OAAO;EACP,MAAM;;EAEN,OAAO"}
1
+ {"version":3,"file":"telemetry.d.cts","names":[],"sources":["../../src/types/telemetry.ts"],"mappings":";cAAa;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WAGA;WACA;;KAGU,wBAAwB,yBAAyB;KACjD,sBAAsB;KACtB,iBAAiB,OAAO,eAAe,gBAAgB,aAAa;KACpE,iBAAiB,OAAO,eAAe,aAAa;;UAG/C;EACf,OAAO;EACP,MAAM;;EAEN,OAAO"}
@@ -9,6 +9,7 @@ declare const TASK_EVENT: {
9
9
  readonly storeCommitListenerFailed: 'doom_task.store_commit_listener_failed';
10
10
  readonly storeSweepFailed: 'doom_task.store_sweep_failed';
11
11
  readonly sessionStartFailed: 'doom_task.session_start_failed';
12
+ readonly sessionStartDegraded: 'doom_task.session_start_degraded';
12
13
  readonly toolFailed: 'doom_task.tool_failed';
13
14
  readonly delegationRequestFailed: 'doom_task.delegation_request_failed';
14
15
  readonly delegationStartFailed: 'doom_task.delegation_start_failed';
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.mts","names":[],"sources":["../../src/types/telemetry.ts"],"mappings":";cAAa;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WAGA;WACA;;KAGU,wBAAwB,yBAAyB;KACjD,sBAAsB;KACtB,iBAAiB,OAAO,eAAe,gBAAgB,aAAa;KACpE,iBAAiB,OAAO,eAAe,aAAa;;UAG/C;EACf,OAAO;EACP,MAAM;;EAEN,OAAO"}
1
+ {"version":3,"file":"telemetry.d.mts","names":[],"sources":["../../src/types/telemetry.ts"],"mappings":";cAAa;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WAGA;WACA;;KAGU,wBAAwB,yBAAyB;KACjD,sBAAsB;KACtB,iBAAiB,OAAO,eAAe,gBAAgB,aAAa;KACpE,iBAAiB,OAAO,eAAe,aAAa;;UAG/C;EACf,OAAO;EACP,MAAM;;EAEN,OAAO"}
@@ -1,2 +1,2 @@
1
- const e={notificationFailed:`doom_task.notification_failed`,storeReadFailed:`doom_task.store_read_failed`,storeLockTimeout:`doom_task.store_lock_timeout`,storeLockBreakFailed:`doom_task.store_lock_break_failed`,storeWatchFailed:`doom_task.store_watch_failed`,storeListenerFailed:`doom_task.store_listener_failed`,storeCommitListenerFailed:`doom_task.store_commit_listener_failed`,storeSweepFailed:`doom_task.store_sweep_failed`,sessionStartFailed:`doom_task.session_start_failed`,toolFailed:`doom_task.tool_failed`,delegationRequestFailed:`doom_task.delegation_request_failed`,delegationStartFailed:`doom_task.delegation_start_failed`,delegationResponseFailed:`doom_task.delegation_response_failed`,delegationSettleFailed:`doom_task.delegation_settle_failed`,delegationTimedOut:`doom_task.delegation_timed_out`,delegationOrphaned:`doom_task.delegation_orphaned`,delegationAssigned:`doom_task.delegation_assigned`,delegationCompleted:`doom_task.delegation_completed`};export{e as TASK_EVENT};
1
+ const e={notificationFailed:`doom_task.notification_failed`,storeReadFailed:`doom_task.store_read_failed`,storeLockTimeout:`doom_task.store_lock_timeout`,storeLockBreakFailed:`doom_task.store_lock_break_failed`,storeWatchFailed:`doom_task.store_watch_failed`,storeListenerFailed:`doom_task.store_listener_failed`,storeCommitListenerFailed:`doom_task.store_commit_listener_failed`,storeSweepFailed:`doom_task.store_sweep_failed`,sessionStartFailed:`doom_task.session_start_failed`,sessionStartDegraded:`doom_task.session_start_degraded`,toolFailed:`doom_task.tool_failed`,delegationRequestFailed:`doom_task.delegation_request_failed`,delegationStartFailed:`doom_task.delegation_start_failed`,delegationResponseFailed:`doom_task.delegation_response_failed`,delegationSettleFailed:`doom_task.delegation_settle_failed`,delegationTimedOut:`doom_task.delegation_timed_out`,delegationOrphaned:`doom_task.delegation_orphaned`,delegationAssigned:`doom_task.delegation_assigned`,delegationCompleted:`doom_task.delegation_completed`};export{e as TASK_EVENT};
2
2
  //# sourceMappingURL=telemetry.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.mjs","names":[],"sources":["../../src/types/telemetry.ts"],"sourcesContent":["export const TASK_EVENT = {\n notificationFailed: 'doom_task.notification_failed',\n storeReadFailed: 'doom_task.store_read_failed',\n storeLockTimeout: 'doom_task.store_lock_timeout',\n storeLockBreakFailed: 'doom_task.store_lock_break_failed',\n storeWatchFailed: 'doom_task.store_watch_failed',\n storeListenerFailed: 'doom_task.store_listener_failed',\n storeCommitListenerFailed: 'doom_task.store_commit_listener_failed',\n storeSweepFailed: 'doom_task.store_sweep_failed',\n sessionStartFailed: 'doom_task.session_start_failed',\n toolFailed: 'doom_task.tool_failed',\n delegationRequestFailed: 'doom_task.delegation_request_failed',\n delegationStartFailed: 'doom_task.delegation_start_failed',\n delegationResponseFailed: 'doom_task.delegation_response_failed',\n delegationSettleFailed: 'doom_task.delegation_settle_failed',\n delegationTimedOut: 'doom_task.delegation_timed_out',\n delegationOrphaned: 'doom_task.delegation_orphaned',\n // Info-level lifecycle events. These are the measurement pair: the shape of\n // the brief that went out, and the cost of the run it produced.\n delegationAssigned: 'doom_task.delegation_assigned',\n delegationCompleted: 'doom_task.delegation_completed',\n} as const;\n\nexport type TaskEventName = (typeof TASK_EVENT)[keyof typeof TASK_EVENT];\nexport type TaskErrorAttributes = Record<string, string | number | boolean>;\nexport type TaskErrorSink = (event: TaskEventName, error: unknown, attributes?: TaskErrorAttributes) => void;\nexport type TaskEventSink = (event: TaskEventName, attributes?: TaskErrorAttributes) => void;\n\n/** Host-neutral telemetry port consumed by Task services and store adapters. */\nexport interface TaskFailureReporter {\n error: TaskErrorSink;\n warn: TaskErrorSink;\n /** Info-level event sink. Required so an unwired reporter fails to compile. */\n event: TaskEventSink;\n}\n"],"mappings":"AAAA,MAAa,EAAa,CACxB,mBAAoB,gCACpB,gBAAiB,8BACjB,iBAAkB,+BAClB,qBAAsB,oCACtB,iBAAkB,+BAClB,oBAAqB,kCACrB,0BAA2B,yCAC3B,iBAAkB,+BAClB,mBAAoB,iCACpB,WAAY,wBACZ,wBAAyB,sCACzB,sBAAuB,oCACvB,yBAA0B,uCAC1B,uBAAwB,qCACxB,mBAAoB,iCACpB,mBAAoB,gCAGpB,mBAAoB,gCACpB,oBAAqB,gCACvB"}
1
+ {"version":3,"file":"telemetry.mjs","names":[],"sources":["../../src/types/telemetry.ts"],"sourcesContent":["export const TASK_EVENT = {\n notificationFailed: 'doom_task.notification_failed',\n storeReadFailed: 'doom_task.store_read_failed',\n storeLockTimeout: 'doom_task.store_lock_timeout',\n storeLockBreakFailed: 'doom_task.store_lock_break_failed',\n storeWatchFailed: 'doom_task.store_watch_failed',\n storeListenerFailed: 'doom_task.store_listener_failed',\n storeCommitListenerFailed: 'doom_task.store_commit_listener_failed',\n storeSweepFailed: 'doom_task.store_sweep_failed',\n sessionStartFailed: 'doom_task.session_start_failed',\n sessionStartDegraded: 'doom_task.session_start_degraded',\n toolFailed: 'doom_task.tool_failed',\n delegationRequestFailed: 'doom_task.delegation_request_failed',\n delegationStartFailed: 'doom_task.delegation_start_failed',\n delegationResponseFailed: 'doom_task.delegation_response_failed',\n delegationSettleFailed: 'doom_task.delegation_settle_failed',\n delegationTimedOut: 'doom_task.delegation_timed_out',\n delegationOrphaned: 'doom_task.delegation_orphaned',\n // Info-level lifecycle events. These are the measurement pair: the shape of\n // the brief that went out, and the cost of the run it produced.\n delegationAssigned: 'doom_task.delegation_assigned',\n delegationCompleted: 'doom_task.delegation_completed',\n} as const;\n\nexport type TaskEventName = (typeof TASK_EVENT)[keyof typeof TASK_EVENT];\nexport type TaskErrorAttributes = Record<string, string | number | boolean>;\nexport type TaskErrorSink = (event: TaskEventName, error: unknown, attributes?: TaskErrorAttributes) => void;\nexport type TaskEventSink = (event: TaskEventName, attributes?: TaskErrorAttributes) => void;\n\n/** Host-neutral telemetry port consumed by Task services and store adapters. */\nexport interface TaskFailureReporter {\n error: TaskErrorSink;\n warn: TaskErrorSink;\n /** Info-level event sink. Required so an unwired reporter fails to compile. */\n event: TaskEventSink;\n}\n"],"mappings":"AAAA,MAAa,EAAa,CACxB,mBAAoB,gCACpB,gBAAiB,8BACjB,iBAAkB,+BAClB,qBAAsB,oCACtB,iBAAkB,+BAClB,oBAAqB,kCACrB,0BAA2B,yCAC3B,iBAAkB,+BAClB,mBAAoB,iCACpB,qBAAsB,mCACtB,WAAY,wBACZ,wBAAyB,sCACzB,sBAAuB,oCACvB,yBAA0B,uCAC1B,uBAAwB,qCACxB,mBAAoB,iCACpB,mBAAoB,gCAGpB,mBAAoB,gCACpB,oBAAqB,gCACvB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-task",
3
- "version": "0.0.1-alpha.45",
3
+ "version": "0.0.1-alpha.48",
4
4
  "description": "Persistent, dependency-aware task graphs and subagent delegation for Pi coding sessions.",
5
5
  "keywords": [
6
6
  "ai",
@@ -27,7 +27,8 @@
27
27
  },
28
28
  "files": [
29
29
  "dist",
30
- "web",
30
+ "src/web",
31
+ "src/exports/webClient.ts",
31
32
  "src/types/webTasks.ts",
32
33
  "package.json"
33
34
  ],
@@ -165,11 +166,11 @@
165
166
  "dependencies": {
166
167
  "@deepseek-ai/cordis": "4.0.1",
167
168
  "typebox": "1.3.19",
168
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.44",
169
- "@agimon-ai/doompi-ui": "0.0.1-alpha.45",
170
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.5",
171
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.8",
172
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.44"
169
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.47",
170
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.47",
171
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.48",
172
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.8",
173
+ "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.11"
173
174
  },
174
175
  "devDependencies": {
175
176
  "@earendil-works/pi-coding-agent": "0.84.4",
@@ -204,7 +205,7 @@
204
205
  "channels": [
205
206
  "task_graph"
206
207
  ],
207
- "client": "./web/index.ts",
208
+ "client": "./src/exports/webClient.ts",
208
209
  "hub": {
209
210
  "entry": "./src/exports/webHub.ts",
210
211
  "dist": "./dist/webHub.mjs"
@@ -218,7 +219,7 @@
218
219
  "scripts": {
219
220
  "build": "tsdown",
220
221
  "test": "vitest --run",
221
- "typecheck": "tsc --noEmit && tsc --noEmit -p web/tsconfig.json",
222
+ "typecheck": "tsc --noEmit && tsc --noEmit -p src/web/tsconfig.json",
222
223
  "lint": "oxlint . && oxfmt . --check",
223
224
  "fixcode": "oxlint . --fix && oxfmt ."
224
225
  }
@@ -0,0 +1 @@
1
+ export { webPlugin } from '../web/index.ts';
@@ -0,0 +1,250 @@
1
+ import {
2
+ Button,
3
+ Dialog,
4
+ DialogBody,
5
+ DialogContent,
6
+ DialogFooter,
7
+ DialogHeader,
8
+ DialogTitle,
9
+ Input,
10
+ Select,
11
+ SelectContent,
12
+ SelectItem,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ Textarea,
16
+ } from '@agimon-ai/doompi-web-components';
17
+ import type { SessionFrameSender } from '@agimon-ai/doompi-web-contracts';
18
+ import { type ReactNode, useState } from 'react';
19
+ import type { WebTask, WebTaskStatus } from '../types/webTasks.ts';
20
+ import { requestTaskEdit, requestTaskMessage, taskEditDraft, type TaskEditDraft } from './tasksStore.ts';
21
+
22
+ /** How the one task dialog is opened: reading it, changing it, or steering its run. */
23
+ export type TaskDialogMode = 'view' | 'edit' | 'message';
24
+
25
+ const STATUS_CHOICES: readonly WebTaskStatus[] = ['pending', 'in_progress', 'completed', 'failed'];
26
+ const DESCRIPTION_ROWS = 5;
27
+ const MESSAGE_ROWS = 4;
28
+
29
+ function FieldLabel({ children }: { children: string }) {
30
+ return <span className="text-[9px] font-bold tracking-[0.18em] text-doom-faint">{children}</span>;
31
+ }
32
+
33
+ function Field({ label, children }: { label: string; children: ReactNode }) {
34
+ return (
35
+ <div className="flex flex-col gap-1.5">
36
+ <FieldLabel>{label}</FieldLabel>
37
+ {children}
38
+ </div>
39
+ );
40
+ }
41
+
42
+ function ReadOnlyValue({ value, testId }: { value: string; testId: string }) {
43
+ return (
44
+ <span data-testid={testId} className="whitespace-pre-wrap break-words text-[11px] leading-relaxed text-doom-dim">
45
+ {value}
46
+ </span>
47
+ );
48
+ }
49
+
50
+ function TaskFacts({ task }: { task: WebTask }) {
51
+ return (
52
+ <div className="flex flex-wrap gap-5">
53
+ <Field label="STATUS">
54
+ <ReadOnlyValue testId="task-detail-status" value={task.status.replace('_', ' ')} />
55
+ </Field>
56
+ {task.activeForm ? (
57
+ <Field label="ACTIVE FORM">
58
+ <ReadOnlyValue testId="task-detail-active-form" value={task.activeForm} />
59
+ </Field>
60
+ ) : null}
61
+ {task.owner ? (
62
+ <Field label="OWNER">
63
+ <ReadOnlyValue testId="task-detail-owner" value={task.owner} />
64
+ </Field>
65
+ ) : null}
66
+ {task.delegation?.agent ? (
67
+ <Field label="DELEGATED TO">
68
+ <ReadOnlyValue
69
+ testId="task-detail-agent"
70
+ value={
71
+ task.delegation.state ? `${task.delegation.agent} · ${task.delegation.state}` : task.delegation.agent
72
+ }
73
+ />
74
+ </Field>
75
+ ) : null}
76
+ {task.blockedBy.length > 0 ? (
77
+ <Field label="BLOCKED BY">
78
+ <ReadOnlyValue testId="task-detail-blocked-by" value={task.blockedBy.map((id) => `#${id}`).join(', ')} />
79
+ </Field>
80
+ ) : null}
81
+ {task.updatedAt ? (
82
+ <Field label="UPDATED">
83
+ <ReadOnlyValue testId="task-detail-updated" value={task.updatedAt} />
84
+ </Field>
85
+ ) : null}
86
+ </div>
87
+ );
88
+ }
89
+
90
+ /**
91
+ * One task, read first and changed second. Every change leaves as a prompt the
92
+ * session can see, so the agent remains the only writer of task state.
93
+ */
94
+ export function TaskDetailDialog({
95
+ task,
96
+ sessionId,
97
+ mode: initialMode,
98
+ send,
99
+ onClose,
100
+ }: {
101
+ task: WebTask;
102
+ sessionId: string;
103
+ mode: TaskDialogMode;
104
+ send: SessionFrameSender;
105
+ onClose: () => void;
106
+ }) {
107
+ const [mode, setMode] = useState<TaskDialogMode>(initialMode);
108
+ const [draft, setDraft] = useState<TaskEditDraft>(() => taskEditDraft(task));
109
+ const [message, setMessage] = useState('');
110
+ const agent = task.delegation?.agent;
111
+
112
+ const submitEdit = (): void => {
113
+ if (requestTaskEdit(send, sessionId, task, draft)) onClose();
114
+ };
115
+ const submitMessage = (): void => {
116
+ if (!message.trim() || !agent) return;
117
+ requestTaskMessage(send, sessionId, task.id, agent, message);
118
+ onClose();
119
+ };
120
+
121
+ return (
122
+ <Dialog
123
+ open
124
+ onOpenChange={(open) => {
125
+ if (!open) onClose();
126
+ }}
127
+ >
128
+ <DialogContent width="lg" data-testid="task-detail-dialog" data-mode={mode} aria-describedby={undefined}>
129
+ <DialogHeader>
130
+ <div className="flex min-w-0 flex-col items-start gap-1 sm:flex-row sm:items-center sm:gap-2.5">
131
+ <span className="text-[9px] text-doom-faint">task #{task.id}</span>
132
+ <DialogTitle data-testid="task-detail-title" className="max-w-full break-words">
133
+ {task.subject}
134
+ </DialogTitle>
135
+ </div>
136
+ </DialogHeader>
137
+ <DialogBody>
138
+ {mode === 'view' ? (
139
+ <>
140
+ <Field label="DESCRIPTION">
141
+ <ReadOnlyValue
142
+ testId="task-detail-description"
143
+ value={task.description ?? 'no description on this task'}
144
+ />
145
+ </Field>
146
+ <TaskFacts task={task} />
147
+ <DialogFooter>
148
+ <span className="min-w-0 flex-1" />
149
+ {agent ? (
150
+ <Button
151
+ variant="outline"
152
+ size="xs"
153
+ data-testid="task-detail-open-message"
154
+ onClick={() => setMode('message')}
155
+ >
156
+ message agent
157
+ </Button>
158
+ ) : null}
159
+ <Button variant="primary" size="xs" data-testid="task-detail-open-edit" onClick={() => setMode('edit')}>
160
+ edit
161
+ </Button>
162
+ </DialogFooter>
163
+ </>
164
+ ) : null}
165
+
166
+ {mode === 'edit' ? (
167
+ <>
168
+ <Field label="SUBJECT">
169
+ <Input
170
+ data-testid="task-detail-subject-input"
171
+ autoFocus
172
+ value={draft.subject}
173
+ onChange={(event) => setDraft({ ...draft, subject: event.target.value })}
174
+ />
175
+ </Field>
176
+ <Field label="DESCRIPTION">
177
+ <Textarea
178
+ data-testid="task-detail-description-input"
179
+ rows={DESCRIPTION_ROWS}
180
+ value={draft.description}
181
+ placeholder="what this task covers"
182
+ onChange={(event) => setDraft({ ...draft, description: event.target.value })}
183
+ />
184
+ </Field>
185
+ <Field label="STATUS">
186
+ <Select
187
+ value={draft.status}
188
+ onValueChange={(value) => setDraft({ ...draft, status: value as WebTaskStatus })}
189
+ >
190
+ <SelectTrigger
191
+ data-testid="task-detail-status-input"
192
+ className="h-7 w-full min-w-0 text-[10px] sm:min-w-[200px]"
193
+ >
194
+ <SelectValue />
195
+ </SelectTrigger>
196
+ <SelectContent>
197
+ {STATUS_CHOICES.map((status) => (
198
+ <SelectItem key={status} value={status}>
199
+ {status.replace('_', ' ')}
200
+ </SelectItem>
201
+ ))}
202
+ </SelectContent>
203
+ </Select>
204
+ </Field>
205
+ <DialogFooter className="flex-wrap sm:flex-nowrap">
206
+ <span className="w-full text-[9px] text-doom-faint sm:w-auto">
207
+ saved as one prompt naming only the fields you changed
208
+ </span>
209
+ <span className="min-w-0 flex-1" />
210
+ <Button variant="outline" size="xs" data-testid="task-detail-cancel" onClick={onClose}>
211
+ cancel
212
+ </Button>
213
+ <Button variant="primary" size="xs" data-testid="task-detail-save" onClick={submitEdit}>
214
+ save
215
+ </Button>
216
+ </DialogFooter>
217
+ </>
218
+ ) : null}
219
+
220
+ {mode === 'message' && agent ? (
221
+ <>
222
+ <Field label={`MESSAGE TO ${agent.toUpperCase()}`}>
223
+ <Textarea
224
+ data-testid="task-detail-message-input"
225
+ autoFocus
226
+ rows={MESSAGE_ROWS}
227
+ value={message}
228
+ placeholder="guidance for the run working on this task"
229
+ onChange={(event) => setMessage(event.target.value)}
230
+ />
231
+ </Field>
232
+ <DialogFooter className="flex-wrap sm:flex-nowrap">
233
+ <span className="w-full text-[9px] text-doom-faint sm:w-auto">
234
+ steers the run, it does not edit the task
235
+ </span>
236
+ <span className="min-w-0 flex-1" />
237
+ <Button variant="outline" size="xs" data-testid="task-detail-cancel" onClick={onClose}>
238
+ cancel
239
+ </Button>
240
+ <Button variant="primary" size="xs" data-testid="task-detail-send" onClick={submitMessage}>
241
+ send
242
+ </Button>
243
+ </DialogFooter>
244
+ </>
245
+ ) : null}
246
+ </DialogBody>
247
+ </DialogContent>
248
+ </Dialog>
249
+ );
250
+ }
@@ -10,15 +10,15 @@ import {
10
10
  EditIcon,
11
11
  KebabIcon,
12
12
  MessageIcon,
13
- Textarea,
14
13
  TrashIcon,
15
14
  type DotTone,
16
15
  } from '@agimon-ai/doompi-web-components';
17
16
  import type { WebPluginSlotProps } from '@agimon-ai/doompi-web-contracts';
18
17
  import { useStore } from '@tanstack/react-store';
19
- import { useRef, useState } from 'react';
20
- import type { WebTask } from '../src/types/webTasks.ts';
21
- import { requestTaskInstruction, requestTaskRemoval, tasks, type TaskInstructionKind } from './tasksStore.ts';
18
+ import { useState } from 'react';
19
+ import type { WebTask } from '../types/webTasks.ts';
20
+ import { TaskDetailDialog, type TaskDialogMode } from './TaskDetailDialog.tsx';
21
+ import { requestTaskRemoval, tasks } from './tasksStore.ts';
22
22
 
23
23
  const STATUS_TONE: Readonly<Record<WebTask['status'], DotTone>> = {
24
24
  pending: 'muted',
@@ -28,29 +28,24 @@ const STATUS_TONE: Readonly<Record<WebTask['status'], DotTone>> = {
28
28
  deleted: 'muted',
29
29
  };
30
30
 
31
- interface EditorState {
31
+ interface DialogState {
32
32
  taskId: number;
33
- kind: TaskInstructionKind;
34
- value: string;
33
+ mode: TaskDialogMode;
35
34
  }
36
35
 
37
36
  function TaskRow({
38
37
  task,
39
38
  sessionId,
40
39
  sendSessionFrame,
41
- editor,
42
- setEditor,
40
+ openDialog,
43
41
  }: {
44
42
  task: WebTask;
45
43
  sessionId: string;
46
44
  sendSessionFrame: WebPluginSlotProps['sendSessionFrame'];
47
- editor: EditorState | undefined;
48
- setEditor: (editor: EditorState | undefined) => void;
45
+ openDialog: (mode: TaskDialogMode) => void;
49
46
  }) {
50
- const editing = editor?.taskId === task.id;
51
- const editorRequested = useRef(false);
52
- const editorInput = useRef<HTMLTextAreaElement>(null);
53
47
  const detail = task.status === 'in_progress' ? task.activeForm : task.description;
48
+ const agent = task.delegation?.agent;
54
49
  return (
55
50
  <div
56
51
  data-testid={`activity-task-${task.id}`}
@@ -59,12 +54,15 @@ function TaskRow({
59
54
  >
60
55
  <div className="grid min-w-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-1.5">
61
56
  <Dot tone={STATUS_TONE[task.status]} pulse={task.status === 'in_progress'} />
62
- <span
57
+ <button
58
+ type="button"
63
59
  data-testid={`activity-task-title-${task.id}`}
64
- className={`min-w-0 truncate text-[10px] font-bold ${task.status === 'failed' ? 'text-doom-red' : task.status === 'completed' ? 'text-doom-dim' : 'text-doom-hi'}`}
60
+ title={task.subject}
61
+ onClick={() => openDialog('view')}
62
+ className={`min-w-0 truncate text-left text-[10px] font-bold hover:underline ${task.status === 'failed' ? 'text-doom-red' : task.status === 'completed' ? 'text-doom-dim' : 'text-doom-hi'}`}
65
63
  >
66
64
  <span className="text-doom-faint">#{task.id}</span> {task.subject}
67
- </span>
65
+ </button>
68
66
  <span className="shrink-0 text-[8px] text-doom-faint">{task.status.replace('_', ' ')}</span>
69
67
  {task.status !== 'completed' && task.status !== 'deleted' ? (
70
68
  <DropdownMenu>
@@ -80,35 +78,20 @@ function TaskRow({
80
78
  <KebabIcon className="h-3 w-3" />
81
79
  </Button>
82
80
  </DropdownMenuTrigger>
83
- <DropdownMenuContent
84
- data-testid={`activity-task-menu-list-${task.id}`}
85
- onCloseAutoFocus={(event) => {
86
- if (!editorRequested.current) return;
87
- editorRequested.current = false;
88
- event.preventDefault();
89
- editorInput.current?.focus();
90
- }}
91
- >
92
- <DropdownMenuItem
93
- data-testid={`activity-task-edit-${task.id}`}
94
- onSelect={() => {
95
- editorRequested.current = true;
96
- setEditor({ taskId: task.id, kind: 'edit', value: '' });
97
- }}
98
- >
81
+ <DropdownMenuContent data-testid={`activity-task-menu-list-${task.id}`}>
82
+ <DropdownMenuItem data-testid={`activity-task-edit-${task.id}`} onSelect={() => openDialog('edit')}>
99
83
  <EditIcon className="h-3 w-3" />
100
84
  Edit
101
85
  </DropdownMenuItem>
102
- <DropdownMenuItem
103
- data-testid={`activity-task-note-${task.id}`}
104
- onSelect={() => {
105
- editorRequested.current = true;
106
- setEditor({ taskId: task.id, kind: 'note', value: '' });
107
- }}
108
- >
109
- <MessageIcon className="h-3 w-3" />
110
- Note
111
- </DropdownMenuItem>
86
+ {agent ? (
87
+ <DropdownMenuItem
88
+ data-testid={`activity-task-message-${task.id}`}
89
+ onSelect={() => openDialog('message')}
90
+ >
91
+ <MessageIcon className="h-3 w-3" />
92
+ Message agent
93
+ </DropdownMenuItem>
94
+ ) : null}
112
95
  <DropdownMenuItem
113
96
  variant="destructive"
114
97
  data-testid={`activity-task-remove-${task.id}`}
@@ -121,49 +104,13 @@ function TaskRow({
121
104
  </DropdownMenu>
122
105
  ) : null}
123
106
  </div>
124
- {detail || task.delegation?.agent || task.blockedBy.length > 0 ? (
107
+ {detail || agent || task.blockedBy.length > 0 ? (
125
108
  <span className="truncate pl-3 text-[9px] text-doom-faint">
126
- {task.delegation?.agent ? `[${task.delegation.agent}] ` : ''}
109
+ {agent ? `[${agent}] ` : ''}
127
110
  {detail ?? ''}
128
111
  {task.blockedBy.length > 0 ? ` · blocked by ${task.blockedBy.map((id) => `#${id}`).join(', ')}` : ''}
129
112
  </span>
130
113
  ) : null}
131
- {editing ? (
132
- <form
133
- className="flex flex-col gap-1 pl-3"
134
- onSubmit={(event) => {
135
- event.preventDefault();
136
- if (!editor.value.trim()) return;
137
- requestTaskInstruction(sendSessionFrame, sessionId, task.id, editor.kind, editor.value);
138
- setEditor(undefined);
139
- }}
140
- >
141
- <Textarea
142
- ref={editorInput}
143
- autoFocus
144
- rows={2}
145
- size="sm"
146
- value={editor.value}
147
- placeholder={editor.kind === 'note' ? 'note for the agent' : 'what should change'}
148
- onChange={(event) => setEditor({ ...editor, value: event.target.value })}
149
- className="text-[9px]"
150
- />
151
- <div className="flex gap-2">
152
- <Button type="submit" variant="link" size="xs" className="h-auto px-0 text-[8px]">
153
- send
154
- </Button>
155
- <Button
156
- type="button"
157
- variant="link"
158
- size="xs"
159
- className="h-auto px-0 text-[8px] text-doom-faint"
160
- onClick={() => setEditor(undefined)}
161
- >
162
- cancel
163
- </Button>
164
- </div>
165
- </form>
166
- ) : null}
167
114
  </div>
168
115
  );
169
116
  }
@@ -172,11 +119,12 @@ function TaskRow({
172
119
  export function TasksActivitySection({ sessionId, sendSessionFrame }: WebPluginSlotProps) {
173
120
  const sessionTasks = useStore(tasks.store, (state) => tasks.select(state, sessionId).tasks);
174
121
  const [historyOpen, setHistoryOpen] = useState(false);
175
- const [editor, setEditor] = useState<EditorState>();
122
+ const [dialog, setDialog] = useState<DialogState>();
176
123
  if (sessionId === null || sessionTasks.length === 0) return null;
177
124
 
178
125
  const active = sessionTasks.filter((task) => task.status === 'pending' || task.status === 'in_progress');
179
126
  const history = sessionTasks.filter((task) => task.status === 'completed' || task.status === 'failed');
127
+ const dialogTask = dialog ? sessionTasks.find((task) => task.id === dialog.taskId) : undefined;
180
128
  return (
181
129
  <section data-testid="activity-tasks" className="flex flex-col gap-2 border-b border-doom-border-soft px-3 py-3">
182
130
  <div className="flex items-center gap-2 px-1">
@@ -201,8 +149,7 @@ export function TasksActivitySection({ sessionId, sendSessionFrame }: WebPluginS
201
149
  task={task}
202
150
  sessionId={sessionId}
203
151
  sendSessionFrame={sendSessionFrame}
204
- editor={editor}
205
- setEditor={setEditor}
152
+ openDialog={(mode) => setDialog({ taskId: task.id, mode })}
206
153
  />
207
154
  ))}
208
155
  </div>
@@ -232,14 +179,23 @@ export function TasksActivitySection({ sessionId, sendSessionFrame }: WebPluginS
232
179
  task={task}
233
180
  sessionId={sessionId}
234
181
  sendSessionFrame={sendSessionFrame}
235
- editor={editor}
236
- setEditor={setEditor}
182
+ openDialog={(mode) => setDialog({ taskId: task.id, mode })}
237
183
  />
238
184
  ))}
239
185
  </div>
240
186
  ) : null}
241
187
  </div>
242
188
  ) : null}
189
+ {dialog && dialogTask ? (
190
+ <TaskDetailDialog
191
+ key={`${dialogTask.id}-${dialog.mode}`}
192
+ task={dialogTask}
193
+ sessionId={sessionId}
194
+ mode={dialog.mode}
195
+ send={sendSessionFrame}
196
+ onClose={() => setDialog(undefined)}
197
+ />
198
+ ) : null}
243
199
  </section>
244
200
  );
245
201
  }
@@ -1,5 +1,5 @@
1
1
  import { defineSessionStore, type SessionFrameSender } from '@agimon-ai/doompi-web-contracts';
2
- import { TASKS_CHANNEL_TYPE, type WebTask, type WebTasksPayload } from '../src/types/webTasks.ts';
2
+ import { TASKS_CHANNEL_TYPE, type WebTask, type WebTasksPayload } from '../types/webTasks.ts';
3
3
 
4
4
  export interface TasksSession {
5
5
  tasks: WebTask[];
@@ -44,24 +44,65 @@ export const tasksChannel = tasks.channel<WebTasksPayload>({
44
44
  reduce: (_current, payload) => payload,
45
45
  });
46
46
 
47
- export type TaskInstructionKind = 'edit' | 'note';
47
+ /** The fields the cockpit lets a human change on a task. */
48
+ export interface TaskEditDraft {
49
+ subject: string;
50
+ description: string;
51
+ status: WebTask['status'];
52
+ }
53
+
54
+ export function taskEditDraft(task: WebTask): TaskEditDraft {
55
+ return { subject: task.subject, description: task.description ?? '', status: task.status };
56
+ }
48
57
 
49
- export function taskInstruction(taskId: number, kind: TaskInstructionKind, instruction: string): string {
50
- const detail = instruction.trim();
51
- if (kind === 'note') {
52
- return `Send this note to the agent working on task #${taskId}, then update the task if needed: ${detail}`;
58
+ function changedFields(task: WebTask, draft: TaskEditDraft): string[] {
59
+ const changes: string[] = [];
60
+ const subject = draft.subject.trim();
61
+ const description = draft.description.trim();
62
+ if (subject && subject !== task.subject) changes.push(`set subject to "${subject}"`);
63
+ if (description !== (task.description ?? '').trim()) {
64
+ changes.push(description ? `set description to "${description}"` : 'clear the description');
53
65
  }
54
- return `Update task #${taskId} according to this instruction. Preserve a valid dependency graph: ${detail}`;
66
+ if (draft.status !== task.status) changes.push(`set status to ${draft.status}`);
67
+ return changes;
68
+ }
69
+
70
+ /**
71
+ * The agent stays the only writer of task state, so an edit leaves the cockpit
72
+ * as one prompt naming exactly the fields the human changed. Nothing changed
73
+ * means nothing to send.
74
+ */
75
+ export function taskEditInstruction(task: WebTask, draft: TaskEditDraft): string | null {
76
+ const changes = changedFields(task, draft);
77
+ if (changes.length === 0) return null;
78
+ return `Update task #${task.id}: ${changes.join('; ')}. Change nothing else and keep the dependency graph valid.`;
79
+ }
80
+
81
+ export function requestTaskEdit(
82
+ send: SessionFrameSender,
83
+ sessionId: string,
84
+ task: WebTask,
85
+ draft: TaskEditDraft,
86
+ ): boolean {
87
+ const message = taskEditInstruction(task, draft);
88
+ if (message === null) return false;
89
+ send(sessionId, { type: 'prompt', message });
90
+ return true;
91
+ }
92
+
93
+ /** Steering for the subagent that owns a delegated task. */
94
+ export function taskMessageInstruction(taskId: number, agent: string, message: string): string {
95
+ return `Steer the ${agent} run working on task #${taskId} with this message, then update the task if its plan changed: ${message.trim()}`;
55
96
  }
56
97
 
57
- export function requestTaskInstruction(
98
+ export function requestTaskMessage(
58
99
  send: SessionFrameSender,
59
100
  sessionId: string,
60
101
  taskId: number,
61
- kind: TaskInstructionKind,
62
- instruction: string,
102
+ agent: string,
103
+ message: string,
63
104
  ): void {
64
- send(sessionId, { type: 'prompt', message: taskInstruction(taskId, kind, instruction) });
105
+ send(sessionId, { type: 'prompt', message: taskMessageInstruction(taskId, agent, message) });
65
106
  }
66
107
 
67
108
  export function requestTaskRemoval(send: SessionFrameSender, sessionId: string, taskId: number): void {
@@ -19,5 +19,5 @@
19
19
  "forceConsistentCasingInFileNames": true,
20
20
  "types": ["node"]
21
21
  },
22
- "include": [".", "../src/types"]
22
+ "include": [".", "../types", "../exports/webClient.ts"]
23
23
  }
File without changes
File without changes
File without changes