@agimon-ai/doompi-task 0.0.1-alpha.28 → 0.0.1-alpha.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,13 @@
1
1
  # @agimon-ai/doompi-task
2
2
 
3
- A persistent, dependency-aware task graph for Pi, with an optional delegation bridge to DoomPi Team.
3
+ A persistent, dependency-aware task graph for Pi, with an optional delegation bridge to DoomPi
4
+ Team.
4
5
 
5
6
  Part of the [DoomPi distribution](https://www.npmjs.com/package/@agimon-ai/doompi).
6
7
 
7
- Task owns task records and `tasks.json`. Team owns agents and runs. Loading both lets Task delegate pending work through Team's session-bound `doom/delegation` Cordis service; it does not merge their persistence.
8
+ Task owns task records and `tasks.json`. Team owns agents and runs. Loading both lets Task delegate
9
+ pending work through Team's session-bound `doom/delegation` Cordis service; it does not merge their
10
+ persistence.
8
11
 
9
12
  > **Alpha:** task and delegation contracts may change between releases.
10
13
 
@@ -34,7 +37,8 @@ For standalone Pi:
34
37
  pi install npm:@agimon-ai/doompi-task
35
38
  ```
36
39
 
37
- Task works without Team for local graph management. Add `@agimon-ai/doompi-team` to the same selected mode when `assign` and `cancel` should launch or control subagents.
40
+ Task works without Team for local graph management. Add `@agimon-ai/doompi-team` to the same
41
+ selected mode when `assign` and `cancel` should launch or control subagents.
38
42
 
39
43
  ## Use the `task` tool
40
44
 
@@ -45,7 +49,7 @@ Supported actions are:
45
49
  | `upsert` | Create tasks or update status, metadata, dependencies, and details |
46
50
  | `list`, `get` | Read the graph or one task |
47
51
  | `delete` | Tombstone a task |
48
- | `clear` | Close and reset a completed graph |
52
+ | `clear` | Close and reset the graph after active delegations stop |
49
53
  | `assign` | Delegate a pending, unblocked task through Team |
50
54
  | `cancel` | Stop a delegated run and return its task to pending |
51
55
 
@@ -62,11 +66,14 @@ Supported actions are:
62
66
  }
63
67
  ```
64
68
 
65
- The reducer enforces lifecycle transitions and rejects dependency cycles. A graph allows 15 non-deleted tasks by default; updates still apply when full, but new tasks are rejected until space is freed.
69
+ The reducer enforces lifecycle transitions and rejects dependency cycles. A graph allows 15
70
+ non-deleted tasks by default; updates still apply when full, but new tasks are rejected until space
71
+ is freed.
66
72
 
67
73
  ## TUI
68
74
 
69
- Use `/tasks` or `SPC t t` to open the interactive task view. These surfaces require a TUI. The `task` tool remains available in headless sessions.
75
+ Use `/tasks` or `SPC t t` to open the interactive task view. These surfaces require a TUI. The
76
+ `task` tool remains available in headless sessions.
70
77
 
71
78
  ## Storage and cleanup
72
79
 
@@ -76,23 +83,29 @@ The default session-tree store is:
76
83
  ~/.pi/agent/doom-task/<session>/tasks.json
77
84
  ```
78
85
 
79
- Task is authoritative across transcript compaction because the graph is file-backed. This does not mean Task persists Team membership, intercom, or child-process state.
86
+ Task is authoritative across transcript compaction because the graph is file-backed. This does not
87
+ mean Task persists Team membership, intercom, or child-process state.
80
88
 
81
89
  Defaults and overrides:
82
90
 
83
- | Setting | Default |
84
- | --------------------------------- | ----------------------------------- |
85
- | `DOOM_TASK_MAX_TASKS` | 15 non-deleted tasks |
86
- | `DOOM_TASK_STORE_TTL_MS` | 30 days |
87
- | `DOOM_TASK_DELEGATION_TIMEOUT_MS` | 20 minutes |
88
- | `DOOM_TASK_STORE` | Override the store root |
89
- | `DOOM_TASK_COLLAPSE_KEY` | Override the task-view collapse key |
91
+ | Setting | Default | Purpose |
92
+ | --------------------------------- | -------------- | ------------------------------------------ |
93
+ | `DOOM_TASK_MAX_TASKS` | `15` | Maximum non-deleted tasks |
94
+ | `DOOM_TASK_STORE_TTL_MS` | 30 days | Retention for inactive session-tree stores |
95
+ | `DOOM_TASK_DELEGATION_TIMEOUT_MS` | 20 minutes | Delegated-run result timeout |
96
+ | `DOOM_TASK_STORE` | Unset | Override the complete store file path |
97
+ | `DOOM_TASK_COLLAPSE_KEY` | `ctrl+shift+t` | Override the task-view collapse key |
90
98
 
91
- Startup/reconciliation sweeps expired stores and repairs delegation records whose owning process is no longer live.
99
+ Startup reconciliation removes expired stores and repairs delegation records whose owning process
100
+ is no longer live.
92
101
 
93
102
  ## Task and Team together
94
103
 
95
- Task injects Team's delegation service and records its lifecycle events on the task. It also contributes pending assignments through `doom/background-work`. Team discovers the selected agent, applies model/tool policy, owns the run, and returns completion or failure. Losing or replacing Team unloads and rebinds both consumers without leaving a process-global registration. Intercom and Team membership remain Team state; `tasks.json` remains Task state.
104
+ Task uses Team's delegation service and records its lifecycle events on the task. It also contributes
105
+ pending assignments through `doom/background-work`. Team discovers the selected agent, applies
106
+ model and tool policy, owns the run, and returns completion or failure. If Team unloads or is
107
+ replaced, both connections are removed and rebound without leaving a process-global registration.
108
+ Intercom and Team membership remain Team state; `tasks.json` remains Task state.
96
109
 
97
110
  ## Public API
98
111
 
@@ -101,7 +114,8 @@ import { detectCycle, isBlocked, TaskStore, taskExtension } from '@agimon-ai/doo
101
114
  import type { Task, TaskStatus } from '@agimon-ai/doompi-task';
102
115
  ```
103
116
 
104
- Focused exports cover schemas, reducers, storage, invariants, delegation management, tool responses, and TUI selectors.
117
+ Focused exports cover schemas, reducers, storage, invariants, delegation management, tool responses,
118
+ and TUI selectors.
105
119
 
106
120
  ## Development
107
121
 
@@ -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/narration/taskNarration.cjs"),s=require("../store/paths.cjs"),c=require("../store/taskStore.cjs"),l=require("../../tui/taskOverlay2.cjs"),u=require("../node/delegationPlatform.cjs"),d=require("../telemetry/logSinkTelemetry.cjs");let f=require("@earendil-works/pi-tui"),p=require("@agimon-ai/doompi-extension-contracts/delegation"),m=require("@agimon-ai/doompi-extension-contracts/background-work"),h=require("@agimon-ai/doompi-extension-contracts/cordis-host"),g=require("@agimon-ai/doompi-extension-contracts/narration"),_=require("@agimon-ai/doompi-extension-contracts/readiness"),v=require("@agimon-ai/doompi-extension-contracts/ui-hub");const y=`@agimon-ai/doompi-task`;function b(h,b){let x=d.createTaskErrorReporter(),S=d.toFailureReporter(x),C=!0,w=0,T,E,D,O,k,A,j=!1,M,N,P,F=e=>{x.recordWarning(`doom_task.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-task could not record a narration failure: ${String(e)}`)})},I={narrate(e){let t=T,n=(0,g.createNarrationRequest)(e);if(!(!t||!n))try{Promise.resolve(t.request(n)).catch(F)}catch(e){F(e)}}};h.inject([g.DOOM_NARRATION_SERVICE],e=>{let t=(0,g.requireDoomNarrationService)(e);return T=t,()=>{T===t&&(T=void 0)}});let L=async(e,t)=>{t?.throwIfAborted();let n=P;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(),!C||n!==P||!N)throw Error(`doom-task readiness belongs to a stale extension generation`)},R=()=>{k?.(),k=void 0,j=!1,A?.update(),D?.reset(),O?.dispose(),N=void 0};h.effect(()=>async()=>{C=!1,w+=1;let e=P?.operation;P=void 0;try{e&&await Promise.allSettled([e]),R(),D?.dispose(),M?.(),M=void 0,E?.dispose()}finally{await x.shutdown()}},y);let z=new c.TaskStore({report:S,onCommitted:(e,t)=>o.narrateTaskCommit(I,e,t)});E=z;let B=()=>{C&&(O?.update(),A?.update())},V=new n.DelegationManager({store:z,cwd:process.cwd(),platform:u.createNodeDelegationPlatform(),notify:(e,t)=>b.sendMessage(e,t),getSessionId:()=>N,onChange:B,runTimeoutMs:i.getDelegationTimeoutMs(),report:S,onNotifyError:(e,t)=>{x.recordNotificationError(e,t)}});D=V,h.inject([p.DOOM_DELEGATION_SERVICE],e=>{let t=(0,p.readDoomDelegationService)(e);if(t)return V.bind(e,t)}),h.inject([m.DOOM_BACKGROUND_WORK_SERVICE],e=>{let t=(0,m.readDoomBackgroundWorkService)(e);if(!t)return;let r=t.register({provider:n.BACKGROUND_WORK_PROVIDER,listActiveWork:()=>j?V.listActiveWork():[]});return A=r,()=>{r.dispose(),A===r&&(A=void 0)}});let H=new l.TaskOverlay({getTasks:()=>z.snapshot.tasks,delegation:V});O=H,a.registerTaskTool(b,{store:z,delegation:V,maxTasks:i.getMaxTasks(),onChange:B,report:S,waitUntilReady:L}),r.registerTasksCommand(b,z,L),h.inject([v.DOOM_UI_HUB_SERVICE],t=>{let n=(0,v.requireDoomUiHub)(t).registerLeader({source:y,bindings:[{id:`tasks.open`,path:[{key:`t`,label:`tasks`,order:65},{key:`t`,label:`tasks`}],command:{name:e.COMMAND_NAME}}]}),r=()=>n.dispose();return M=r,()=>{r(),M===r&&(M=void 0)}}),b.registerMessageRenderer(n.NOTIFY_CUSTOM_TYPE,(e,t,n)=>{let r=typeof e.content==`string`?e.content:``;return new f.Text(n.fg(`muted`,r),0,0)});let U=i.resolveCollapseKey();U!==`off`&&b.registerShortcut(U,{description:`Collapse or expand the task overlay`,handler:async e=>{!C||!e.hasUI||(await L(e),!(!C||!H.isRegistered())&&H.toggleCollapse())}}),b.on(`session_start`,async(e,n)=>{if(!C)return;let r=++w;N!==void 0&&R();let a=n,o=async e=>{let n=()=>!e?.aborted&&C&&r===w;try{let e=a.sessionManager?.getSessionId();if(!e)throw Error(`doom-task requires a session id`);N=e,j=!a.hasUI,A?.update(),z.configureSession(s.resolveSessionKey(e));let r=await s.removeLegacyStoreDirectoryAsync(z.storePath);if(!n())return;let o=s.hasStorePathOverride()?{removed:[],errors:[]}:await s.sweepStoreFilesAsync(z.storePath,i.getStoreTtlMs());if(!n())return;let c=[...r.errors,...o.errors];for(let e of c)S.warn(t.TASK_EVENT.storeSweepFailed,Error(e));if(c.length>0&&a.hasUI&&a.ui.notify(c.join(`
2
- `),`warning`),a.hasUI&&H.setUICtx(a.ui),await z.readAsync(n),!n()||(k=z.onExternalChange(B),await V.reconcile(n),!n()))return;B()}catch(e){if(!n())return;throw R(),S.error(t.TASK_EVENT.sessionStartFailed,e),e}},c=(0,_.readDoomReadinessCoordinator)(h);if(!c){let e=o();return P={sessionManager:n.sessionManager,operation:e},e}let l=P?.operation,u=(async()=>{l&&await Promise.allSettled([l]),!(!C||r!==w)&&await c.start(y,`${n.sessionManager.getSessionId()}:${r}`,async e=>(await o(e),{value:void 0})).wait()})();P={sessionManager:n.sessionManager,operation:u},Promise.allSettled([u])}),b.on(`session_compact`,B),b.on(`session_tree`,B),b.on(`tool_execution_end`,e=>{C&&e.toolName===`task`&&B()})}async function x(e){let t=await(0,h.connectDoomCordisHost)(e,y),n=t.root.plugin(S,{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 S(e,t){b(e,t.pi)}exports.default=x,exports.taskExtension=x,exports.installTaskRuntime=b;
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`,order:65},{key:`t`,label:`tasks`}],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;
3
3
  //# sourceMappingURL=extension.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.cjs","names":["createTaskErrorReporter","toFailureReporter","createNarrationRequest","DOOM_NARRATION_SERVICE","requireDoomNarrationService","TaskStore","narrateTaskCommit","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 { 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 { 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 = '@agimon-ai/doompi-task';\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 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', order: LEADER_GROUP_ORDER },\n { key: 't', label: 'tasks' },\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","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', order: LEADER_GROUP_ORDER },\n { key: 't', label: 'tasks' },\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 +1 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBA0DgB,mBAAmB,QAAQ,SAAS,IAAI;;iBA4QlC,cAAc,IAAI,eAAe"}
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 +1 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBA0DgB,mBAAmB,QAAQ,SAAS,IAAI;;iBA4QlC,cAAc,IAAI,eAAe"}
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,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{narrateTaskCommit as d}from"../../services/narration/taskNarration.mjs";import{hasStorePathOverride as f,removeLegacyStoreDirectoryAsync as p,resolveSessionKey as m,sweepStoreFilesAsync as h}from"../store/paths.mjs";import{TaskStore as g}from"../store/taskStore.mjs";import{TaskOverlay as _}from"../../tui/taskOverlay2.mjs";import{createNodeDelegationPlatform as v}from"../node/delegationPlatform.mjs";import{createTaskErrorReporter as y,toFailureReporter as b}from"../telemetry/logSinkTelemetry.mjs";import{Text as x}from"@earendil-works/pi-tui";import{DOOM_DELEGATION_SERVICE as S,readDoomDelegationService as C}from"@agimon-ai/doompi-extension-contracts/delegation";import{DOOM_BACKGROUND_WORK_SERVICE as w,readDoomBackgroundWorkService as T}from"@agimon-ai/doompi-extension-contracts/background-work";import{connectDoomCordisHost as E}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_NARRATION_SERVICE as D,createNarrationRequest as ee,requireDoomNarrationService as te}from"@agimon-ai/doompi-extension-contracts/narration";import{readDoomReadinessCoordinator as ne}from"@agimon-ai/doompi-extension-contracts/readiness";import{DOOM_UI_HUB_SERVICE as re,requireDoomUiHub as O}from"@agimon-ai/doompi-extension-contracts/ui-hub";const k=`@agimon-ai/doompi-task`;function A(E,A){let j=y(),M=b(j),N=!0,P=0,F,I,L,R,z,B,V=!1,H,U,W,G=e=>{j.recordWarning(`doom_task.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-task could not record a narration failure: ${String(e)}`)})},K={narrate(e){let t=F,n=ee(e);if(!(!t||!n))try{Promise.resolve(t.request(n)).catch(G)}catch(e){G(e)}}};E.inject([D],e=>{let t=te(e);return F=t,()=>{F===t&&(F=void 0)}});let q=async(e,t)=>{t?.throwIfAborted();let n=W;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(),!N||n!==W||!U)throw Error(`doom-task readiness belongs to a stale extension generation`)},J=()=>{z?.(),z=void 0,V=!1,B?.update(),L?.reset(),R?.dispose(),U=void 0};E.effect(()=>async()=>{N=!1,P+=1;let e=W?.operation;W=void 0;try{e&&await Promise.allSettled([e]),J(),L?.dispose(),H?.(),H=void 0,I?.dispose()}finally{await j.shutdown()}},k);let Y=new g({report:M,onCommitted:(e,t)=>d(K,e,t)});I=Y;let X=()=>{N&&(R?.update(),B?.update())},Z=new r({store:Y,cwd:process.cwd(),platform:v(),notify:(e,t)=>A.sendMessage(e,t),getSessionId:()=>U,onChange:X,runTimeoutMs:o(),report:M,onNotifyError:(e,t)=>{j.recordNotificationError(e,t)}});L=Z,E.inject([S],e=>{let t=C(e);if(t)return Z.bind(e,t)}),E.inject([w],e=>{let t=T(e);if(!t)return;let r=t.register({provider:n,listActiveWork:()=>V?Z.listActiveWork():[]});return B=r,()=>{r.dispose(),B===r&&(B=void 0)}});let Q=new _({getTasks:()=>Y.snapshot.tasks,delegation:Z});R=Q,u(A,{store:Y,delegation:Z,maxTasks:s(),onChange:X,report:M,waitUntilReady:q}),a(A,Y,q),E.inject([re],t=>{let n=O(t).registerLeader({source:k,bindings:[{id:`tasks.open`,path:[{key:`t`,label:`tasks`,order:65},{key:`t`,label:`tasks`}],command:{name:e}}]}),r=()=>n.dispose();return H=r,()=>{r(),H===r&&(H=void 0)}}),A.registerMessageRenderer(i,(e,t,n)=>{let r=typeof e.content==`string`?e.content:``;return new x(n.fg(`muted`,r),0,0)});let $=l();$!==`off`&&A.registerShortcut($,{description:`Collapse or expand the task overlay`,handler:async e=>{!N||!e.hasUI||(await q(e),!(!N||!Q.isRegistered())&&Q.toggleCollapse())}}),A.on(`session_start`,async(e,n)=>{if(!N)return;let r=++P;U!==void 0&&J();let i=n,a=async e=>{let n=()=>!e?.aborted&&N&&r===P;try{let e=i.sessionManager?.getSessionId();if(!e)throw Error(`doom-task requires a session id`);U=e,V=!i.hasUI,B?.update(),Y.configureSession(m(e));let r=await p(Y.storePath);if(!n())return;let a=f()?{removed:[],errors:[]}:await h(Y.storePath,c());if(!n())return;let o=[...r.errors,...a.errors];for(let e of o)M.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()||(z=Y.onExternalChange(X),await Z.reconcile(n),!n()))return;X()}catch(e){if(!n())return;throw J(),M.error(t.sessionStartFailed,e),e}},o=ne(E);if(!o){let e=a();return W={sessionManager:n.sessionManager,operation:e},e}let s=W?.operation,l=(async()=>{s&&await Promise.allSettled([s]),!(!N||r!==P)&&await o.start(k,`${n.sessionManager.getSessionId()}:${r}`,async e=>(await a(e),{value:void 0})).wait()})();W={sessionManager:n.sessionManager,operation:l},Promise.allSettled([l])}),A.on(`session_compact`,X),A.on(`session_tree`,X),A.on(`tool_execution_end`,e=>{N&&e.toolName===`task`&&X()})}async function j(e){let t=await E(e,k),n=t.root.plugin(M,{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 M(e,t){A(e,t.pi)}export{j as default,j as taskExtension,A 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 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`,order:65},{key:`t`,label:`tasks`}],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};
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 { 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 { 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 = '@agimon-ai/doompi-task';\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 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', order: LEADER_GROUP_ORDER },\n { key: 't', label: 'tasks' },\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';\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', order: LEADER_GROUP_ORDER },\n { key: 't', label: 'tasks' },\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,2 +1,2 @@
1
- const e=["Use `task` for work with 3+ steps, when the user gives you a list of tasks, or immediately after receiving new instructions to capture requirements. Skip it for single trivial tasks and purely conversational requests.",`Capture a whole plan in one call: {"action":"upsert","tasks":[{"subject":"Research existing tool"},{"subject":"Write the reducer"},{"subject":"Add tests"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.`,`Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.`,`Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status "failed" or keep it in_progress and add a new task for the blocker in the same upsert.`,`When every non-deleted task is completed, review the full task list once more and call task {"action":"clear"} to close it. Do not leave an all-completed list open.`,`Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.`,`One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {"action":"upsert","tasks":[{"id":3,"status":"completed"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.`,`Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a "ref" and list the ref string in place of an id: [{"ref":"api","subject":"Design the API"},{"subject":"Implement the API","blockedBy":["api"]}]. A ref must be declared before it is used. Cycles are rejected.`,`upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.`,`Before assigning work, call subagent with {"action":"agents"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.`,`Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {"action":"assign","assignments":[{"id":1,"agent":"researcher"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.`,`A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.`,`Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.`,`After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.`,`When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.`,`If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.`,`assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {"action":"cancel","id":3} to stop a running delegation; the task returns to pending.`,`Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.`,`The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.`,`Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.`];exports.DEFAULT_PROMPT_GUIDELINES=e;
1
+ const e=["Use `task` only for complex jobs where persistent coordination materially improves execution, such as work with dependencies, multiple parallel workstreams, substantial delegated work, or a long-running plan that must survive context compaction. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because a request contains several steps. Never create a task list only to restate new instructions.",`Capture a whole plan in one call: {"action":"upsert","tasks":[{"subject":"Research existing tool"},{"subject":"Write the reducer"},{"subject":"Add tests"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.`,`Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.`,`Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status "failed" or keep it in_progress and add a new task for the blocker in the same upsert.`,`When every non-deleted task is completed, review the full task list once more and call task {"action":"clear"} to close it. Do not leave an all-completed list open.`,`Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.`,`One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {"action":"upsert","tasks":[{"id":3,"status":"completed"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.`,`Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a "ref" and list the ref string in place of an id: [{"ref":"api","subject":"Design the API"},{"subject":"Implement the API","blockedBy":["api"]}]. A ref must be declared before it is used. Cycles are rejected.`,`upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.`,`Before assigning work, call subagent with {"action":"agents"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.`,`Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {"action":"assign","assignments":[{"id":1,"agent":"researcher"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.`,`A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.`,`Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.`,`After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.`,`When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.`,`If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.`,`assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {"action":"cancel","id":3} to stop a running delegation; the task returns to pending.`,`Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.`,`The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.`,`Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.`];exports.DEFAULT_PROMPT_GUIDELINES=e;
2
2
  //# sourceMappingURL=promptGuidelines.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"promptGuidelines.cjs","names":[],"sources":["../../../src/commands/task/promptGuidelines.ts"],"sourcesContent":["export const DEFAULT_PROMPT_GUIDELINES: string[] = [\n 'Use `task` for work with 3+ steps, when the user gives you a list of tasks, or immediately after receiving new instructions to capture requirements. Skip it for single trivial tasks and purely conversational requests.',\n 'Capture a whole plan in one call: {\"action\":\"upsert\",\"tasks\":[{\"subject\":\"Research existing tool\"},{\"subject\":\"Write the reducer\"},{\"subject\":\"Add tests\"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.',\n 'Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.',\n 'Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status \"failed\" or keep it in_progress and add a new task for the blocker in the same upsert.',\n 'When every non-deleted task is completed, review the full task list once more and call task {\"action\":\"clear\"} to close it. Do not leave an all-completed list open.',\n \"Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.\",\n 'One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {\"action\":\"upsert\",\"tasks\":[{\"id\":3,\"status\":\"completed\"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.',\n 'Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a \"ref\" and list the ref string in place of an id: [{\"ref\":\"api\",\"subject\":\"Design the API\"},{\"subject\":\"Implement the API\",\"blockedBy\":[\"api\"]}]. A ref must be declared before it is used. Cycles are rejected.',\n 'upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.',\n 'Before assigning work, call subagent with {\"action\":\"agents\"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.',\n 'Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {\"action\":\"assign\",\"assignments\":[{\"id\":1,\"agent\":\"researcher\"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.',\n 'A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.',\n 'Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.',\n 'After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.',\n 'When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.',\n 'If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.',\n 'assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {\"action\":\"cancel\",\"id\":3} to stop a running delegation; the task returns to pending.',\n 'Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.',\n 'The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.',\n \"Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.\",\n];\n"],"mappings":"AAAA,MAAa,EAAsC,CACjD,4NACA,6XACA,ukBACA,mNACA,uKACA,kPACA,qXACA,0fACA,qTACA,sRACA,iuBACA,0MACA,4sBACA,yHACA,qNACA,8LACA,4LACA,+LACA,8KACA,uXACF"}
1
+ {"version":3,"file":"promptGuidelines.cjs","names":[],"sources":["../../../src/commands/task/promptGuidelines.ts"],"sourcesContent":["export const DEFAULT_PROMPT_GUIDELINES: string[] = [\n 'Use `task` only for complex jobs where persistent coordination materially improves execution, such as work with dependencies, multiple parallel workstreams, substantial delegated work, or a long-running plan that must survive context compaction. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because a request contains several steps. Never create a task list only to restate new instructions.',\n 'Capture a whole plan in one call: {\"action\":\"upsert\",\"tasks\":[{\"subject\":\"Research existing tool\"},{\"subject\":\"Write the reducer\"},{\"subject\":\"Add tests\"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.',\n 'Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.',\n 'Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status \"failed\" or keep it in_progress and add a new task for the blocker in the same upsert.',\n 'When every non-deleted task is completed, review the full task list once more and call task {\"action\":\"clear\"} to close it. Do not leave an all-completed list open.',\n \"Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.\",\n 'One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {\"action\":\"upsert\",\"tasks\":[{\"id\":3,\"status\":\"completed\"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.',\n 'Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a \"ref\" and list the ref string in place of an id: [{\"ref\":\"api\",\"subject\":\"Design the API\"},{\"subject\":\"Implement the API\",\"blockedBy\":[\"api\"]}]. A ref must be declared before it is used. Cycles are rejected.',\n 'upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.',\n 'Before assigning work, call subagent with {\"action\":\"agents\"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.',\n 'Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {\"action\":\"assign\",\"assignments\":[{\"id\":1,\"agent\":\"researcher\"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.',\n 'A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.',\n 'Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.',\n 'After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.',\n 'When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.',\n 'If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.',\n 'assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {\"action\":\"cancel\",\"id\":3} to stop a running delegation; the task returns to pending.',\n 'Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.',\n 'The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.',\n \"Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.\",\n];\n"],"mappings":"AAAA,MAAa,EAAsC,CACjD,4bACA,6XACA,ukBACA,mNACA,uKACA,kPACA,qXACA,0fACA,qTACA,sRACA,iuBACA,0MACA,4sBACA,yHACA,qNACA,8LACA,4LACA,+LACA,8KACA,uXACF"}
@@ -1,2 +1,2 @@
1
- const e=["Use `task` for work with 3+ steps, when the user gives you a list of tasks, or immediately after receiving new instructions to capture requirements. Skip it for single trivial tasks and purely conversational requests.",`Capture a whole plan in one call: {"action":"upsert","tasks":[{"subject":"Research existing tool"},{"subject":"Write the reducer"},{"subject":"Add tests"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.`,`Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.`,`Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status "failed" or keep it in_progress and add a new task for the blocker in the same upsert.`,`When every non-deleted task is completed, review the full task list once more and call task {"action":"clear"} to close it. Do not leave an all-completed list open.`,`Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.`,`One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {"action":"upsert","tasks":[{"id":3,"status":"completed"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.`,`Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a "ref" and list the ref string in place of an id: [{"ref":"api","subject":"Design the API"},{"subject":"Implement the API","blockedBy":["api"]}]. A ref must be declared before it is used. Cycles are rejected.`,`upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.`,`Before assigning work, call subagent with {"action":"agents"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.`,`Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {"action":"assign","assignments":[{"id":1,"agent":"researcher"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.`,`A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.`,`Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.`,`After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.`,`When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.`,`If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.`,`assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {"action":"cancel","id":3} to stop a running delegation; the task returns to pending.`,`Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.`,`The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.`,`Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.`];export{e as DEFAULT_PROMPT_GUIDELINES};
1
+ const e=["Use `task` only for complex jobs where persistent coordination materially improves execution, such as work with dependencies, multiple parallel workstreams, substantial delegated work, or a long-running plan that must survive context compaction. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because a request contains several steps. Never create a task list only to restate new instructions.",`Capture a whole plan in one call: {"action":"upsert","tasks":[{"subject":"Research existing tool"},{"subject":"Write the reducer"},{"subject":"Add tests"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.`,`Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.`,`Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status "failed" or keep it in_progress and add a new task for the blocker in the same upsert.`,`When every non-deleted task is completed, review the full task list once more and call task {"action":"clear"} to close it. Do not leave an all-completed list open.`,`Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.`,`One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {"action":"upsert","tasks":[{"id":3,"status":"completed"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.`,`Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a "ref" and list the ref string in place of an id: [{"ref":"api","subject":"Design the API"},{"subject":"Implement the API","blockedBy":["api"]}]. A ref must be declared before it is used. Cycles are rejected.`,`upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.`,`Before assigning work, call subagent with {"action":"agents"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.`,`Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {"action":"assign","assignments":[{"id":1,"agent":"researcher"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.`,`A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.`,`Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.`,`After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.`,`When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.`,`If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.`,`assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {"action":"cancel","id":3} to stop a running delegation; the task returns to pending.`,`Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.`,`The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.`,`Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.`];export{e as DEFAULT_PROMPT_GUIDELINES};
2
2
  //# sourceMappingURL=promptGuidelines.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"promptGuidelines.mjs","names":[],"sources":["../../../src/commands/task/promptGuidelines.ts"],"sourcesContent":["export const DEFAULT_PROMPT_GUIDELINES: string[] = [\n 'Use `task` for work with 3+ steps, when the user gives you a list of tasks, or immediately after receiving new instructions to capture requirements. Skip it for single trivial tasks and purely conversational requests.',\n 'Capture a whole plan in one call: {\"action\":\"upsert\",\"tasks\":[{\"subject\":\"Research existing tool\"},{\"subject\":\"Write the reducer\"},{\"subject\":\"Add tests\"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.',\n 'Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.',\n 'Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status \"failed\" or keep it in_progress and add a new task for the blocker in the same upsert.',\n 'When every non-deleted task is completed, review the full task list once more and call task {\"action\":\"clear\"} to close it. Do not leave an all-completed list open.',\n \"Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.\",\n 'One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {\"action\":\"upsert\",\"tasks\":[{\"id\":3,\"status\":\"completed\"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.',\n 'Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a \"ref\" and list the ref string in place of an id: [{\"ref\":\"api\",\"subject\":\"Design the API\"},{\"subject\":\"Implement the API\",\"blockedBy\":[\"api\"]}]. A ref must be declared before it is used. Cycles are rejected.',\n 'upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.',\n 'Before assigning work, call subagent with {\"action\":\"agents\"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.',\n 'Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {\"action\":\"assign\",\"assignments\":[{\"id\":1,\"agent\":\"researcher\"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.',\n 'A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.',\n 'Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.',\n 'After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.',\n 'When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.',\n 'If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.',\n 'assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {\"action\":\"cancel\",\"id\":3} to stop a running delegation; the task returns to pending.',\n 'Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.',\n 'The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.',\n \"Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.\",\n];\n"],"mappings":"AAAA,MAAa,EAAsC,CACjD,4NACA,6XACA,ukBACA,mNACA,uKACA,kPACA,qXACA,0fACA,qTACA,sRACA,iuBACA,0MACA,4sBACA,yHACA,qNACA,8LACA,4LACA,+LACA,8KACA,uXACF"}
1
+ {"version":3,"file":"promptGuidelines.mjs","names":[],"sources":["../../../src/commands/task/promptGuidelines.ts"],"sourcesContent":["export const DEFAULT_PROMPT_GUIDELINES: string[] = [\n 'Use `task` only for complex jobs where persistent coordination materially improves execution, such as work with dependencies, multiple parallel workstreams, substantial delegated work, or a long-running plan that must survive context compaction. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because a request contains several steps. Never create a task list only to restate new instructions.',\n 'Capture a whole plan in one call: {\"action\":\"upsert\",\"tasks\":[{\"subject\":\"Research existing tool\"},{\"subject\":\"Write the reducer\"},{\"subject\":\"Add tests\"}]}. Batch whenever you are writing the plan down — the initial list, a revised plan, or wiring dependencies across several tasks — because one call keeps ids and blockers consistent and shows the user the whole plan at once.',\n 'Do not batch parent progress. Mark a task in_progress with its own call BEFORE beginning work, and completed with its own call IMMEDIATELY when it is done. upsert accepts many tasks, but a batch of completions sent at the end of a turn is still wrong: upsert batching is for writing a plan down, single calls are for reporting progress, and the user watches the task list to see where you are. Native assignments[] fan-out is separate from progress reporting. Exactly one task should be in_progress for your own work; delegated tasks run in parallel and do not count against that.',\n 'Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors, use status \"failed\" or keep it in_progress and add a new task for the blocker in the same upsert.',\n 'When every non-deleted task is completed, review the full task list once more and call task {\"action\":\"clear\"} to close it. Do not leave an all-completed list open.',\n \"Status is a state machine: pending → in_progress → completed, plus failed (recoverable, can go back to pending or in_progress) and deleted as a tombstone. Pass activeForm (present-continuous, e.g. 'writing tests') when marking in_progress.\",\n 'One call does both: an entry with an id changes that task, an entry without an id creates one. To change status, upsert the task by id, e.g. {\"action\":\"upsert\",\"tasks\":[{\"id\":3,\"status\":\"completed\"}]}. Never omit the id when you meant to change an existing task — an entry with no id creates a new one instead. An entry that carries an id and no other field is rejected.',\n 'Use blockedBy for dependencies (A is blocked by B). On an entry with no id pass blockedBy as the initial set; on an entry with an id use addBlockedBy / removeBlockedBy (additive merge, do not resend the full array). To depend on a task created earlier in the SAME call, give that earlier entry a \"ref\" and list the ref string in place of an id: [{\"ref\":\"api\",\"subject\":\"Design the API\"},{\"subject\":\"Implement the API\",\"blockedBy\":[\"api\"]}]. A ref must be declared before it is used. Cycles are rejected.',\n 'upsert applies each entry independently: the entries that pass are committed and the failures are reported with their array index. A failed entry changed nothing, so resend only the corrected failures — never resend the whole call, because an already-applied entry with no id would create a duplicate task.',\n 'Before assigning work, call subagent with {\"action\":\"agents\"} and match each task to an exact discovered agent name. Prefer a discovered general-purpose write-capable agent such as delegate or worker when no specialist fits. Use a focused inlineAgent only for read-only work.',\n 'Assign every unblocked pending task, including implementation tasks. Use assignments[] for every assign call, including one task: {\"action\":\"assign\",\"assignments\":[{\"id\":1,\"agent\":\"researcher\"}]}. Put all assign-time fields — including model, inlineAgent, instructions, relevantFiles, priorFindings, and context — inside each assignments[] entry; assign has no top-level single-task form. For two or more independent ready tasks, use one native batch instead of repeated calls. Do not wrap repeated task assign calls in multi_tool; assignments[] reduces tool overhead and returns one indexed result. Do not use a direct subagent run for work already represented in the shared task list, so ownership and terminal results stay visible.',\n 'A batched assign applies each entry independently. Successful entries are already running if another entry fails; retry only failed entries after correcting them, and never resend successful entries.',\n 'Hand over what you already know when you assign. Pass relevantFiles with the paths you have actually read or located for that task, and priorFindings with the facts you established: symbol names, call sites, the approach you picked, what you ruled out. A fresh-context child starts with none of your context and will otherwise spend most of its run re-reading the repo. It treats this verified pack as satisfying initial context exploration, reads known paths directly, and widens discovery only for a concrete missing dependency. Send at most a dozen files and only ones you really opened — a guessed path costs the child more than an omitted one — and keep priorFindings to a few lines of facts, not directives.',\n 'After assigning work, continue non-overlapping parent work or end your turn. Do not poll or duplicate delegated scope.',\n 'When a delegated task completes and unblocks another task, repeat agent discovery if needed and assign that newly eligible task promptly. Do not poll in a loop. Completion notifications wake the parent session.',\n 'If a child asks for a decision through intercom, or an assignment fails to start for another concrete issue, respond, rescope, or retry explicitly. Do not silently leave the task pending.',\n 'assign refuses tasks that are blocked, already delegated, or completed. Resolve blockers first. Use {\"action\":\"cancel\",\"id\":3} to stop a running delegation; the task returns to pending.',\n 'Doom Task records delegated lifecycle and results on the shared task. The child does not update task status directly. Read the completion or failure result before deciding what to do next.',\n 'The task list is scoped to this session tree: delegated subagents share it, while unrelated sessions start empty. It persists for later inspection until retention cleanup.',\n \"Subject must be short and imperative (e.g. 'Research existing tool'); description is long-form detail written when you plan the task, and the delegated brief is that description plus the assign-time instructions, relevantFiles and priorFindings. Put durable scope in description and what you learned while exploring in the assign-time pack — do not repeat one in the other.\",\n];\n"],"mappings":"AAAA,MAAa,EAAsC,CACjD,4bACA,6XACA,ukBACA,mNACA,uKACA,kPACA,qXACA,0fACA,qTACA,sRACA,iuBACA,0MACA,4sBACA,yHACA,qNACA,8LACA,4LACA,+LACA,8KACA,uXACF"}
@@ -1,3 +1,3 @@
1
- const e=require("../../schemas/task.cjs"),t=require("../../services/store/reducer.cjs"),n=require("../../types/telemetry.cjs"),r=require("../../tui/format2.cjs");require("../../types/config.cjs");const i=require("./promptGuidelines.cjs"),a=require("./responseEnvelope.cjs"),o=`Manage a shared task list to track multi-step progress, and delegate tasks to subagents`,s=`tool.action`,c=`task.id`,l=new Set([`upsert`,`list`,`get`,`delete`,`clear`]);var u=class extends Error{};function d(e,t){return new u([`Task ${e} failed: ${t}`,``,`Options:`,`- List the task board and inspect the current ids, states, and blockers.`,`- Correct the arguments or state conflict, then retry once.`,`- Ask the user before cancelling work or changing dependencies to recover.`].join(`
2
- `))}function f(t,n){let r=Object.keys(n).filter(r=>n[r]!==void 0&&!e.taskActionAcceptsField(t,r));if(r.length===0)return;let i=t===`upsert`?` ${e.MSG_UPSERT_FIELD_MISPLACED}`:``;throw d(t,`${t} does not accept ${r.join(`, `)}.${i}`)}function p(e){let t=e.assignments;if(!t||t.length===0)throw d(`assign`,`assign requires a non-empty assignments[] array`);return t}function m(e,t){return{agent:e.agent,inlineAgent:e.inlineAgent,instructions:e.instructions,relevantFiles:e.relevantFiles,priorFindings:e.priorFindings,model:e.model,context:e.context,signal:t}}async function h(e,t,r,i){let a=[];for(let[o,l]of t.entries()){let t;try{t=await e.assign(l.id,m(l,r))}catch(e){i?.error(n.TASK_EVENT.toolFailed,e,{[s]:`assign`,[c]:l.id}),t={ok:!1,message:e instanceof Error?e.message:String(e)}}a.push({index:o,id:l.id,agent:l.agent,...t})}return a}async function g(e,n,r,i){let{document:o,value:s}=await e.mutate(e=>{let a=t.applyTaskMutation(e,n,r,void 0,i);return{...t.isCommittingOp(a.op)?{document:a.document}:{},value:a}});if(s.op.kind===`error`)throw d(n,s.op.message);if(s.op.kind===`upsert`&&s.op.applied===0)throw d(n,a.formatUpsertFailureText(s.op));return a.buildToolResult(n,r,o,s.op)}function _(t,m){let{store:_,delegation:v,maxTasks:y=15}=m;t.registerTool({name:e.TOOL_NAME,label:e.TOOL_LABEL,description:`Manage a shared task list for tracking multi-step progress, and delegate tasks to subagents. Actions: upsert (write tasks — a tasks[] array where an entry with an id changes that task and an entry without an id creates one, so one call can capture a whole plan), list, get, delete (tombstone), clear (close/reset the list), assign (hand one or more tasks through assignments[] to named subagents that work in the background), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.`,promptSnippet:o,promptGuidelines:i.DEFAULT_PROMPT_GUIDELINES,parameters:e.TaskParamsSchema,renderShell:`self`,async execute(e,t,r,i,o){let b=t.action,x=t;try{if(await m.waitUntilReady?.(o,r),f(b,x),l.has(b)){let e=await g(_,b,x,y);return m.onChange?.(),e}if(b===`assign`){let e=p(x),t=e[0],n=e.length===1&&t?`Delegating task #${t.id}...`:`Delegating ${e.length} tasks...`;i?.(a.buildTextResult(b,x,_.snapshot,n));let o=await h(v,e,r,m.report);m.onChange?.();let s=a.formatAssignmentResults(o),c=o.flatMap(e=>e.ok?[e.id]:[]);if(c.length===0)throw d(b,s);return a.buildAssignmentResult(x,_.snapshot,s,{assigned:c,failed:o.length-c.length})}i?.(a.buildTextResult(b,x,_.snapshot,`Requesting cancellation for task #${x.id??`unknown`}...`));let e=await v.cancel(x.id??NaN);if(m.onChange?.(),!e.ok)throw d(b,e.message);return a.buildTextResult(b,x,_.snapshot,e.message,void 0)}catch(e){throw m.report?.error(n.TASK_EVENT.toolFailed,e,{[s]:b,...x.id===void 0?{}:{[c]:x.id}}),e instanceof u?e:d(b,e instanceof Error?e.message:String(e))}},renderCall(e,t,n){return r.renderTaskCall(e,t,_.snapshot.tasks)},renderResult(e,t,n,i){return r.renderTaskResult(e,t,n)}})}exports.DEFAULT_PROMPT_GUIDELINES=i.DEFAULT_PROMPT_GUIDELINES,exports.DEFAULT_PROMPT_SNIPPET=o,exports.registerTaskTool=_;
1
+ const e=require("../../schemas/task.cjs"),t=require("../../services/store/reducer.cjs"),n=require("../../types/telemetry.cjs"),r=require("../../tui/format2.cjs");require("../../types/config.cjs");const i=require("./promptGuidelines.cjs"),a=require("./responseEnvelope.cjs"),o=`Track only complex, multi-step jobs that benefit from persistent progress or delegation; skip simple work`,s=`tool.action`,c=`task.id`,l=new Set([`upsert`,`list`,`get`,`delete`,`clear`]);var u=class extends Error{};function d(e,t){return new u([`Task ${e} failed: ${t}`,``,`Options:`,`- List the task board and inspect the current ids, states, and blockers.`,`- Correct the arguments or state conflict, then retry once.`,`- Ask the user before cancelling work or changing dependencies to recover.`].join(`
2
+ `))}function f(t,n){let r=Object.keys(n).filter(r=>n[r]!==void 0&&!e.taskActionAcceptsField(t,r));if(r.length===0)return;let i=t===`upsert`?` ${e.MSG_UPSERT_FIELD_MISPLACED}`:``;throw d(t,`${t} does not accept ${r.join(`, `)}.${i}`)}function p(e){let t=e.assignments;if(!t||t.length===0)throw d(`assign`,`assign requires a non-empty assignments[] array`);return t}function m(e,t){return{agent:e.agent,inlineAgent:e.inlineAgent,instructions:e.instructions,relevantFiles:e.relevantFiles,priorFindings:e.priorFindings,model:e.model,context:e.context,signal:t}}async function h(e,t,r,i){let a=[];for(let[o,l]of t.entries()){let t;try{t=await e.assign(l.id,m(l,r))}catch(e){i?.error(n.TASK_EVENT.toolFailed,e,{[s]:`assign`,[c]:l.id}),t={ok:!1,message:e instanceof Error?e.message:String(e)}}a.push({index:o,id:l.id,agent:l.agent,...t})}return a}async function g(e,n,r,i){let{document:o,value:s}=await e.mutate(e=>{let a=t.applyTaskMutation(e,n,r,void 0,i);return{...t.isCommittingOp(a.op)?{document:a.document}:{},value:a}});if(s.op.kind===`error`)throw d(n,s.op.message);if(s.op.kind===`upsert`&&s.op.applied===0)throw d(n,a.formatUpsertFailureText(s.op));return a.buildToolResult(n,r,o,s.op)}function _(t,m){let{store:_,delegation:v,maxTasks:y=15}=m;t.registerTool({name:e.TOOL_NAME,label:e.TOOL_LABEL,description:`Track complex jobs with a shared task list and delegate tracked work to subagents. Use this only when persistent coordination is useful, such as work with dependencies, parallel workstreams, or a long-running plan. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because the user listed multiple steps. Actions: upsert (write tasks: an entry with an id changes that task and one without an id creates one), list, get, delete (tombstone), clear (close/reset the list), assign (hand tasks through assignments[] to named background subagents), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.`,promptSnippet:o,promptGuidelines:i.DEFAULT_PROMPT_GUIDELINES,parameters:e.TaskParamsSchema,renderShell:`self`,async execute(e,t,r,i,o){let b=t.action,x=t;try{if(await m.waitUntilReady?.(o,r),f(b,x),l.has(b)){let e=await g(_,b,x,y);return m.onChange?.(),e}if(b===`assign`){let e=p(x),t=e[0],n=e.length===1&&t?`Delegating task #${t.id}...`:`Delegating ${e.length} tasks...`;i?.(a.buildTextResult(b,x,_.snapshot,n));let o=await h(v,e,r,m.report);m.onChange?.();let s=a.formatAssignmentResults(o),c=o.flatMap(e=>e.ok?[e.id]:[]);if(c.length===0)throw d(b,s);return a.buildAssignmentResult(x,_.snapshot,s,{assigned:c,failed:o.length-c.length})}i?.(a.buildTextResult(b,x,_.snapshot,`Requesting cancellation for task #${x.id??`unknown`}...`));let e=await v.cancel(x.id??NaN);if(m.onChange?.(),!e.ok)throw d(b,e.message);return a.buildTextResult(b,x,_.snapshot,e.message,void 0)}catch(e){throw m.report?.error(n.TASK_EVENT.toolFailed,e,{[s]:b,...x.id===void 0?{}:{[c]:x.id}}),e instanceof u?e:d(b,e instanceof Error?e.message:String(e))}},renderCall(e,t,n){return r.renderTaskCall(e,t,_.snapshot.tasks)},renderResult(e,t,n,i){return r.renderTaskResult(e,t,n)}})}exports.DEFAULT_PROMPT_GUIDELINES=i.DEFAULT_PROMPT_GUIDELINES,exports.DEFAULT_PROMPT_SNIPPET=o,exports.registerTaskTool=_;
3
3
  //# sourceMappingURL=taskTool.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"taskTool.cjs","names":["taskActionAcceptsField","MSG_UPSERT_FIELD_MISPLACED","TASK_EVENT","applyTaskMutation","isCommittingOp","formatUpsertFailureText","buildToolResult","TOOL_NAME","TOOL_LABEL","DEFAULT_PROMPT_GUIDELINES","TaskParamsSchema","buildTextResult","formatAssignmentResults","buildAssignmentResult","renderTaskCall","renderTaskResult"],"sources":["../../../src/commands/task/taskTool.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n MSG_UPSERT_FIELD_MISPLACED,\n TaskParamsSchema,\n TOOL_LABEL,\n TOOL_NAME,\n taskActionAcceptsField,\n} from '../../schemas/task.ts';\nimport type { AssignOptions, DelegationManager, DelegationOutcome } from '../../services/delegation/manager.ts';\nimport { applyTaskMutation, isCommittingOp, type ReducerAction } from '../../services/store/reducer.ts';\nimport type { TaskStore } from '../../adapters/store/taskStore';\nimport type { TaskAction, TaskAssignment, TaskMutationParams } from '../../services/store/types.ts';\nimport { renderTaskCall, renderTaskResult } from '../../tui/format.ts';\nimport { DEFAULT_MAX_TASKS } from '../../types/config.ts';\nimport { TASK_EVENT, type TaskFailureReporter } from '../../types/telemetry.ts';\nimport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\nimport {\n type AssignmentItemResult,\n buildAssignmentResult,\n buildTextResult,\n buildToolResult,\n formatAssignmentResults,\n formatUpsertFailureText,\n type ToolResult,\n} from './responseEnvelope.ts';\n\nexport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\n\nexport const DEFAULT_PROMPT_SNIPPET =\n 'Manage a shared task list to track multi-step progress, and delegate tasks to subagents';\n\nexport interface TaskToolDependencies {\n store: TaskStore;\n delegation: DelegationManager;\n maxTasks?: number;\n onChange?: () => void;\n report?: TaskFailureReporter;\n waitUntilReady?: (context: ExtensionContext, signal?: AbortSignal) => Promise<void>;\n}\n\nconst ACTION_ATTRIBUTE = 'tool.action';\nconst TASK_ID_ATTRIBUTE = 'task.id';\nconst REDUCER_ACTIONS = new Set<TaskAction>(['upsert', 'list', 'get', 'delete', 'clear']);\n\nclass TaskToolExecutionError extends Error {}\n\nfunction actionableTaskError(action: TaskAction, message: string): TaskToolExecutionError {\n return new TaskToolExecutionError(\n [\n `Task ${action} failed: ${message}`,\n '',\n 'Options:',\n '- List the task board and inspect the current ids, states, and blockers.',\n '- Correct the arguments or state conflict, then retry once.',\n '- Ask the user before cancelling work or changing dependencies to recover.',\n ].join('\\n'),\n );\n}\n\n/**\n * Reject a field that belongs to a different action.\n *\n * The params schema is flat and shared across every action, so this is the only\n * place `{\"action\":\"upsert\",\"id\":3}` can be caught — and it must be, because\n * the entry it carries has no id and would silently create a duplicate task.\n */\nfunction rejectStrayFields(action: TaskAction, params: TaskMutationParams): void {\n const stray = Object.keys(params).filter((key) => params[key] !== undefined && !taskActionAcceptsField(action, key));\n if (stray.length === 0) return;\n const hint = action === 'upsert' ? ` ${MSG_UPSERT_FIELD_MISPLACED}` : '';\n throw actionableTaskError(action, `${action} does not accept ${stray.join(', ')}.${hint}`);\n}\n\n/** assignments[] is the only assign contract, even when it contains one task. */\nfunction resolveAssignments(params: TaskMutationParams): TaskAssignment[] {\n const assignments = params.assignments;\n if (!assignments || assignments.length === 0) {\n throw actionableTaskError('assign', 'assign requires a non-empty assignments[] array');\n }\n return assignments;\n}\n\nfunction assignOptions(request: TaskAssignment, signal: AbortSignal | undefined): AssignOptions {\n return {\n agent: request.agent,\n inlineAgent: request.inlineAgent,\n instructions: request.instructions,\n relevantFiles: request.relevantFiles,\n priorFindings: request.priorFindings,\n model: request.model,\n context: request.context,\n signal,\n };\n}\n\nasync function executeAssignmentBatch(\n delegation: DelegationManager,\n assignments: readonly TaskAssignment[],\n signal: AbortSignal | undefined,\n report: TaskFailureReporter | undefined,\n): Promise<AssignmentItemResult[]> {\n const results: AssignmentItemResult[] = [];\n for (const [index, assignment] of assignments.entries()) {\n let outcome: DelegationOutcome;\n try {\n outcome = await delegation.assign(assignment.id, assignOptions(assignment, signal));\n } catch (error) {\n report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: 'assign',\n [TASK_ID_ATTRIBUTE]: assignment.id,\n });\n outcome = { ok: false, message: error instanceof Error ? error.message : String(error) };\n }\n results.push({ index, id: assignment.id, agent: assignment.agent, ...outcome });\n }\n return results;\n}\n\n/** Run an action through the reducer, persisting only when it produced a change. */\nasync function executeReducerAction(\n store: TaskStore,\n action: ReducerAction,\n params: TaskMutationParams,\n maxTasks: number,\n): Promise<ToolResult> {\n const { document, value } = await store.mutate((current) => {\n const result = applyTaskMutation(current, action, params, undefined, maxTasks);\n return {\n ...(isCommittingOp(result.op) ? { document: result.document } : {}),\n value: result,\n };\n });\n if (value.op.kind === 'error') throw actionableTaskError(action, value.op.message);\n // An upsert that applied nothing is a failed call: it wrote nothing and left\n // `rev` alone, so it must not read to the model as a success. A batch where\n // some entries landed returns normally — that is what partial apply means.\n if (value.op.kind === 'upsert' && value.op.applied === 0) {\n throw actionableTaskError(action, formatUpsertFailureText(value.op));\n }\n // `document` is the committed document, so `details.rev` matches what landed\n // on disk; `value.document` is the pre-write copy and lags by one.\n return buildToolResult(action, params, document, value.op);\n}\n\nexport function registerTaskTool(pi: ExtensionAPI, dependencies: TaskToolDependencies): void {\n const { store, delegation, maxTasks = DEFAULT_MAX_TASKS } = dependencies;\n\n pi.registerTool({\n name: TOOL_NAME,\n label: TOOL_LABEL,\n description:\n 'Manage a shared task list for tracking multi-step progress, and delegate tasks to subagents. Actions: upsert (write tasks — a tasks[] array where an entry with an id changes that task and an entry without an id creates one, so one call can capture a whole plan), list, get, delete (tombstone), clear (close/reset the list), assign (hand one or more tasks through assignments[] to named subagents that work in the background), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.',\n promptSnippet: DEFAULT_PROMPT_SNIPPET,\n promptGuidelines: DEFAULT_PROMPT_GUIDELINES,\n parameters: TaskParamsSchema,\n // Task rows carry their own status colors. Owning the shell prevents Pi's\n // pending/success/error background fill from obscuring those row states.\n renderShell: 'self',\n\n async execute(_toolCallId, params, signal, onUpdate, ctx) {\n const action = params.action as TaskAction;\n const mutationParams = params as TaskMutationParams;\n\n try {\n await dependencies.waitUntilReady?.(ctx, signal);\n rejectStrayFields(action, mutationParams);\n if (REDUCER_ACTIONS.has(action)) {\n const result = await executeReducerAction(store, action as ReducerAction, mutationParams, maxTasks);\n dependencies.onChange?.();\n return result;\n }\n\n if (action === 'assign') {\n const assignments = resolveAssignments(mutationParams);\n const first = assignments[0];\n const progress =\n assignments.length === 1 && first\n ? `Delegating task #${first.id}...`\n : `Delegating ${assignments.length} tasks...`;\n onUpdate?.(buildTextResult(action, mutationParams, store.snapshot, progress));\n const results = await executeAssignmentBatch(delegation, assignments, signal, dependencies.report);\n dependencies.onChange?.();\n const text = formatAssignmentResults(results);\n const assigned = results.flatMap((item) => (item.ok ? [item.id] : []));\n if (assigned.length === 0) throw actionableTaskError(action, text);\n return buildAssignmentResult(mutationParams, store.snapshot, text, {\n assigned,\n failed: results.length - assigned.length,\n });\n }\n\n onUpdate?.(\n buildTextResult(\n action,\n mutationParams,\n store.snapshot,\n `Requesting cancellation for task #${mutationParams.id ?? 'unknown'}...`,\n ),\n );\n const outcome = await delegation.cancel(mutationParams.id ?? Number.NaN);\n dependencies.onChange?.();\n if (!outcome.ok) throw actionableTaskError(action, outcome.message);\n return buildTextResult(action, mutationParams, store.snapshot, outcome.message, undefined);\n } catch (error) {\n // Rethrown so pi still renders the failure to the model; recorded\n // because this is where a store fault becomes visible to the user.\n dependencies.report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: action,\n ...(mutationParams.id === undefined ? {} : { [TASK_ID_ATTRIBUTE]: mutationParams.id }),\n });\n if (error instanceof TaskToolExecutionError) throw error;\n throw actionableTaskError(action, error instanceof Error ? error.message : String(error));\n }\n },\n\n // Render hooks receive no session identity, so they read the store snapshot\n // that the executing session last loaded. That is the foreground session's\n // own view, which is exactly what its transcript should show.\n renderCall(args, theme, _context) {\n return renderTaskCall(args as never, theme, store.snapshot.tasks);\n },\n\n renderResult(result, options, theme, _context) {\n return renderTaskResult(result, options, theme);\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"taskTool.cjs","names":["taskActionAcceptsField","MSG_UPSERT_FIELD_MISPLACED","TASK_EVENT","applyTaskMutation","isCommittingOp","formatUpsertFailureText","buildToolResult","TOOL_NAME","TOOL_LABEL","DEFAULT_PROMPT_GUIDELINES","TaskParamsSchema","buildTextResult","formatAssignmentResults","buildAssignmentResult","renderTaskCall","renderTaskResult"],"sources":["../../../src/commands/task/taskTool.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n MSG_UPSERT_FIELD_MISPLACED,\n TaskParamsSchema,\n TOOL_LABEL,\n TOOL_NAME,\n taskActionAcceptsField,\n} from '../../schemas/task.ts';\nimport type { AssignOptions, DelegationManager, DelegationOutcome } from '../../services/delegation/manager.ts';\nimport { applyTaskMutation, isCommittingOp, type ReducerAction } from '../../services/store/reducer.ts';\nimport type { TaskStore } from '../../adapters/store/taskStore';\nimport type { TaskAction, TaskAssignment, TaskMutationParams } from '../../services/store/types.ts';\nimport { renderTaskCall, renderTaskResult } from '../../tui/format.ts';\nimport { DEFAULT_MAX_TASKS } from '../../types/config.ts';\nimport { TASK_EVENT, type TaskFailureReporter } from '../../types/telemetry.ts';\nimport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\nimport {\n type AssignmentItemResult,\n buildAssignmentResult,\n buildTextResult,\n buildToolResult,\n formatAssignmentResults,\n formatUpsertFailureText,\n type ToolResult,\n} from './responseEnvelope.ts';\n\nexport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\n\nexport const DEFAULT_PROMPT_SNIPPET =\n 'Track only complex, multi-step jobs that benefit from persistent progress or delegation; skip simple work';\n\nexport interface TaskToolDependencies {\n store: TaskStore;\n delegation: DelegationManager;\n maxTasks?: number;\n onChange?: () => void;\n report?: TaskFailureReporter;\n waitUntilReady?: (context: ExtensionContext, signal?: AbortSignal) => Promise<void>;\n}\n\nconst ACTION_ATTRIBUTE = 'tool.action';\nconst TASK_ID_ATTRIBUTE = 'task.id';\nconst REDUCER_ACTIONS = new Set<TaskAction>(['upsert', 'list', 'get', 'delete', 'clear']);\n\nclass TaskToolExecutionError extends Error {}\n\nfunction actionableTaskError(action: TaskAction, message: string): TaskToolExecutionError {\n return new TaskToolExecutionError(\n [\n `Task ${action} failed: ${message}`,\n '',\n 'Options:',\n '- List the task board and inspect the current ids, states, and blockers.',\n '- Correct the arguments or state conflict, then retry once.',\n '- Ask the user before cancelling work or changing dependencies to recover.',\n ].join('\\n'),\n );\n}\n\n/**\n * Reject a field that belongs to a different action.\n *\n * The params schema is flat and shared across every action, so this is the only\n * place `{\"action\":\"upsert\",\"id\":3}` can be caught — and it must be, because\n * the entry it carries has no id and would silently create a duplicate task.\n */\nfunction rejectStrayFields(action: TaskAction, params: TaskMutationParams): void {\n const stray = Object.keys(params).filter((key) => params[key] !== undefined && !taskActionAcceptsField(action, key));\n if (stray.length === 0) return;\n const hint = action === 'upsert' ? ` ${MSG_UPSERT_FIELD_MISPLACED}` : '';\n throw actionableTaskError(action, `${action} does not accept ${stray.join(', ')}.${hint}`);\n}\n\n/** assignments[] is the only assign contract, even when it contains one task. */\nfunction resolveAssignments(params: TaskMutationParams): TaskAssignment[] {\n const assignments = params.assignments;\n if (!assignments || assignments.length === 0) {\n throw actionableTaskError('assign', 'assign requires a non-empty assignments[] array');\n }\n return assignments;\n}\n\nfunction assignOptions(request: TaskAssignment, signal: AbortSignal | undefined): AssignOptions {\n return {\n agent: request.agent,\n inlineAgent: request.inlineAgent,\n instructions: request.instructions,\n relevantFiles: request.relevantFiles,\n priorFindings: request.priorFindings,\n model: request.model,\n context: request.context,\n signal,\n };\n}\n\nasync function executeAssignmentBatch(\n delegation: DelegationManager,\n assignments: readonly TaskAssignment[],\n signal: AbortSignal | undefined,\n report: TaskFailureReporter | undefined,\n): Promise<AssignmentItemResult[]> {\n const results: AssignmentItemResult[] = [];\n for (const [index, assignment] of assignments.entries()) {\n let outcome: DelegationOutcome;\n try {\n outcome = await delegation.assign(assignment.id, assignOptions(assignment, signal));\n } catch (error) {\n report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: 'assign',\n [TASK_ID_ATTRIBUTE]: assignment.id,\n });\n outcome = { ok: false, message: error instanceof Error ? error.message : String(error) };\n }\n results.push({ index, id: assignment.id, agent: assignment.agent, ...outcome });\n }\n return results;\n}\n\n/** Run an action through the reducer, persisting only when it produced a change. */\nasync function executeReducerAction(\n store: TaskStore,\n action: ReducerAction,\n params: TaskMutationParams,\n maxTasks: number,\n): Promise<ToolResult> {\n const { document, value } = await store.mutate((current) => {\n const result = applyTaskMutation(current, action, params, undefined, maxTasks);\n return {\n ...(isCommittingOp(result.op) ? { document: result.document } : {}),\n value: result,\n };\n });\n if (value.op.kind === 'error') throw actionableTaskError(action, value.op.message);\n // An upsert that applied nothing is a failed call: it wrote nothing and left\n // `rev` alone, so it must not read to the model as a success. A batch where\n // some entries landed returns normally — that is what partial apply means.\n if (value.op.kind === 'upsert' && value.op.applied === 0) {\n throw actionableTaskError(action, formatUpsertFailureText(value.op));\n }\n // `document` is the committed document, so `details.rev` matches what landed\n // on disk; `value.document` is the pre-write copy and lags by one.\n return buildToolResult(action, params, document, value.op);\n}\n\nexport function registerTaskTool(pi: ExtensionAPI, dependencies: TaskToolDependencies): void {\n const { store, delegation, maxTasks = DEFAULT_MAX_TASKS } = dependencies;\n\n pi.registerTool({\n name: TOOL_NAME,\n label: TOOL_LABEL,\n description:\n 'Track complex jobs with a shared task list and delegate tracked work to subagents. Use this only when persistent coordination is useful, such as work with dependencies, parallel workstreams, or a long-running plan. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because the user listed multiple steps. Actions: upsert (write tasks: an entry with an id changes that task and one without an id creates one), list, get, delete (tombstone), clear (close/reset the list), assign (hand tasks through assignments[] to named background subagents), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.',\n promptSnippet: DEFAULT_PROMPT_SNIPPET,\n promptGuidelines: DEFAULT_PROMPT_GUIDELINES,\n parameters: TaskParamsSchema,\n // Task rows carry their own status colors. Owning the shell prevents Pi's\n // pending/success/error background fill from obscuring those row states.\n renderShell: 'self',\n\n async execute(_toolCallId, params, signal, onUpdate, ctx) {\n const action = params.action as TaskAction;\n const mutationParams = params as TaskMutationParams;\n\n try {\n await dependencies.waitUntilReady?.(ctx, signal);\n rejectStrayFields(action, mutationParams);\n if (REDUCER_ACTIONS.has(action)) {\n const result = await executeReducerAction(store, action as ReducerAction, mutationParams, maxTasks);\n dependencies.onChange?.();\n return result;\n }\n\n if (action === 'assign') {\n const assignments = resolveAssignments(mutationParams);\n const first = assignments[0];\n const progress =\n assignments.length === 1 && first\n ? `Delegating task #${first.id}...`\n : `Delegating ${assignments.length} tasks...`;\n onUpdate?.(buildTextResult(action, mutationParams, store.snapshot, progress));\n const results = await executeAssignmentBatch(delegation, assignments, signal, dependencies.report);\n dependencies.onChange?.();\n const text = formatAssignmentResults(results);\n const assigned = results.flatMap((item) => (item.ok ? [item.id] : []));\n if (assigned.length === 0) throw actionableTaskError(action, text);\n return buildAssignmentResult(mutationParams, store.snapshot, text, {\n assigned,\n failed: results.length - assigned.length,\n });\n }\n\n onUpdate?.(\n buildTextResult(\n action,\n mutationParams,\n store.snapshot,\n `Requesting cancellation for task #${mutationParams.id ?? 'unknown'}...`,\n ),\n );\n const outcome = await delegation.cancel(mutationParams.id ?? Number.NaN);\n dependencies.onChange?.();\n if (!outcome.ok) throw actionableTaskError(action, outcome.message);\n return buildTextResult(action, mutationParams, store.snapshot, outcome.message, undefined);\n } catch (error) {\n // Rethrown so pi still renders the failure to the model; recorded\n // because this is where a store fault becomes visible to the user.\n dependencies.report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: action,\n ...(mutationParams.id === undefined ? {} : { [TASK_ID_ATTRIBUTE]: mutationParams.id }),\n });\n if (error instanceof TaskToolExecutionError) throw error;\n throw actionableTaskError(action, error instanceof Error ? error.message : String(error));\n }\n },\n\n // Render hooks receive no session identity, so they read the store snapshot\n // that the executing session last loaded. That is the foreground session's\n // own view, which is exactly what its transcript should show.\n renderCall(args, theme, _context) {\n return renderTaskCall(args as never, theme, store.snapshot.tasks);\n },\n\n renderResult(result, options, theme, _context) {\n return renderTaskResult(result, options, theme);\n },\n });\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { DelegationManager } from "../../services/delegation/manager.cjs";
4
4
  import { DEFAULT_PROMPT_GUIDELINES } from "./promptGuidelines.cjs";
5
5
  import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
6
6
  //#region src/commands/task/taskTool.d.ts
7
- declare const DEFAULT_PROMPT_SNIPPET = "Manage a shared task list to track multi-step progress, and delegate tasks to subagents";
7
+ declare const DEFAULT_PROMPT_SNIPPET = "Track only complex, multi-step jobs that benefit from persistent progress or delegation; skip simple work";
8
8
  interface TaskToolDependencies {
9
9
  store: TaskStore;
10
10
  delegation: DelegationManager;
@@ -4,7 +4,7 @@ import { DelegationManager } from "../../services/delegation/manager.mjs";
4
4
  import { DEFAULT_PROMPT_GUIDELINES } from "./promptGuidelines.mjs";
5
5
  import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
6
6
  //#region src/commands/task/taskTool.d.ts
7
- declare const DEFAULT_PROMPT_SNIPPET = "Manage a shared task list to track multi-step progress, and delegate tasks to subagents";
7
+ declare const DEFAULT_PROMPT_SNIPPET = "Track only complex, multi-step jobs that benefit from persistent progress or delegation; skip simple work";
8
8
  interface TaskToolDependencies {
9
9
  store: TaskStore;
10
10
  delegation: DelegationManager;
@@ -1,3 +1,3 @@
1
- import{MSG_UPSERT_FIELD_MISPLACED as e,TOOL_LABEL as t,TOOL_NAME as n,TaskParamsSchema as r,taskActionAcceptsField as i}from"../../schemas/task.mjs";import{applyTaskMutation as a,isCommittingOp as o}from"../../services/store/reducer.mjs";import{TASK_EVENT as s}from"../../types/telemetry.mjs";import{renderTaskCall as c,renderTaskResult as l}from"../../tui/format2.mjs";import"../../types/config.mjs";import{DEFAULT_PROMPT_GUIDELINES as u}from"./promptGuidelines.mjs";import{buildAssignmentResult as d,buildTextResult as f,buildToolResult as p,formatAssignmentResults as m,formatUpsertFailureText as h}from"./responseEnvelope.mjs";const g=`Manage a shared task list to track multi-step progress, and delegate tasks to subagents`,_=`tool.action`,v=`task.id`,y=new Set([`upsert`,`list`,`get`,`delete`,`clear`]);var b=class extends Error{};function x(e,t){return new b([`Task ${e} failed: ${t}`,``,`Options:`,`- List the task board and inspect the current ids, states, and blockers.`,`- Correct the arguments or state conflict, then retry once.`,`- Ask the user before cancelling work or changing dependencies to recover.`].join(`
2
- `))}function S(t,n){let r=Object.keys(n).filter(e=>n[e]!==void 0&&!i(t,e));if(r.length===0)return;let a=t===`upsert`?` ${e}`:``;throw x(t,`${t} does not accept ${r.join(`, `)}.${a}`)}function C(e){let t=e.assignments;if(!t||t.length===0)throw x(`assign`,`assign requires a non-empty assignments[] array`);return t}function w(e,t){return{agent:e.agent,inlineAgent:e.inlineAgent,instructions:e.instructions,relevantFiles:e.relevantFiles,priorFindings:e.priorFindings,model:e.model,context:e.context,signal:t}}async function T(e,t,n,r){let i=[];for(let[a,o]of t.entries()){let t;try{t=await e.assign(o.id,w(o,n))}catch(e){r?.error(s.toolFailed,e,{[_]:`assign`,[v]:o.id}),t={ok:!1,message:e instanceof Error?e.message:String(e)}}i.push({index:a,id:o.id,agent:o.agent,...t})}return i}async function E(e,t,n,r){let{document:i,value:s}=await e.mutate(e=>{let i=a(e,t,n,void 0,r);return{...o(i.op)?{document:i.document}:{},value:i}});if(s.op.kind===`error`)throw x(t,s.op.message);if(s.op.kind===`upsert`&&s.op.applied===0)throw x(t,h(s.op));return p(t,n,i,s.op)}function D(e,i){let{store:a,delegation:o,maxTasks:p=15}=i;e.registerTool({name:n,label:t,description:`Manage a shared task list for tracking multi-step progress, and delegate tasks to subagents. Actions: upsert (write tasks — a tasks[] array where an entry with an id changes that task and an entry without an id creates one, so one call can capture a whole plan), list, get, delete (tombstone), clear (close/reset the list), assign (hand one or more tasks through assignments[] to named subagents that work in the background), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.`,promptSnippet:g,promptGuidelines:u,parameters:r,renderShell:`self`,async execute(e,t,n,r,c){let l=t.action,u=t;try{if(await i.waitUntilReady?.(c,n),S(l,u),y.has(l)){let e=await E(a,l,u,p);return i.onChange?.(),e}if(l===`assign`){let e=C(u),t=e[0],s=e.length===1&&t?`Delegating task #${t.id}...`:`Delegating ${e.length} tasks...`;r?.(f(l,u,a.snapshot,s));let c=await T(o,e,n,i.report);i.onChange?.();let p=m(c),h=c.flatMap(e=>e.ok?[e.id]:[]);if(h.length===0)throw x(l,p);return d(u,a.snapshot,p,{assigned:h,failed:c.length-h.length})}r?.(f(l,u,a.snapshot,`Requesting cancellation for task #${u.id??`unknown`}...`));let e=await o.cancel(u.id??NaN);if(i.onChange?.(),!e.ok)throw x(l,e.message);return f(l,u,a.snapshot,e.message,void 0)}catch(e){throw i.report?.error(s.toolFailed,e,{[_]:l,...u.id===void 0?{}:{[v]:u.id}}),e instanceof b?e:x(l,e instanceof Error?e.message:String(e))}},renderCall(e,t,n){return c(e,t,a.snapshot.tasks)},renderResult(e,t,n,r){return l(e,t,n)}})}export{u as DEFAULT_PROMPT_GUIDELINES,g as DEFAULT_PROMPT_SNIPPET,D as registerTaskTool};
1
+ import{MSG_UPSERT_FIELD_MISPLACED as e,TOOL_LABEL as t,TOOL_NAME as n,TaskParamsSchema as r,taskActionAcceptsField as i}from"../../schemas/task.mjs";import{applyTaskMutation as a,isCommittingOp as o}from"../../services/store/reducer.mjs";import{TASK_EVENT as s}from"../../types/telemetry.mjs";import{renderTaskCall as c,renderTaskResult as l}from"../../tui/format2.mjs";import"../../types/config.mjs";import{DEFAULT_PROMPT_GUIDELINES as u}from"./promptGuidelines.mjs";import{buildAssignmentResult as d,buildTextResult as f,buildToolResult as p,formatAssignmentResults as m,formatUpsertFailureText as h}from"./responseEnvelope.mjs";const g=`Track only complex, multi-step jobs that benefit from persistent progress or delegation; skip simple work`,_=`tool.action`,v=`task.id`,y=new Set([`upsert`,`list`,`get`,`delete`,`clear`]);var b=class extends Error{};function x(e,t){return new b([`Task ${e} failed: ${t}`,``,`Options:`,`- List the task board and inspect the current ids, states, and blockers.`,`- Correct the arguments or state conflict, then retry once.`,`- Ask the user before cancelling work or changing dependencies to recover.`].join(`
2
+ `))}function S(t,n){let r=Object.keys(n).filter(e=>n[e]!==void 0&&!i(t,e));if(r.length===0)return;let a=t===`upsert`?` ${e}`:``;throw x(t,`${t} does not accept ${r.join(`, `)}.${a}`)}function C(e){let t=e.assignments;if(!t||t.length===0)throw x(`assign`,`assign requires a non-empty assignments[] array`);return t}function w(e,t){return{agent:e.agent,inlineAgent:e.inlineAgent,instructions:e.instructions,relevantFiles:e.relevantFiles,priorFindings:e.priorFindings,model:e.model,context:e.context,signal:t}}async function T(e,t,n,r){let i=[];for(let[a,o]of t.entries()){let t;try{t=await e.assign(o.id,w(o,n))}catch(e){r?.error(s.toolFailed,e,{[_]:`assign`,[v]:o.id}),t={ok:!1,message:e instanceof Error?e.message:String(e)}}i.push({index:a,id:o.id,agent:o.agent,...t})}return i}async function E(e,t,n,r){let{document:i,value:s}=await e.mutate(e=>{let i=a(e,t,n,void 0,r);return{...o(i.op)?{document:i.document}:{},value:i}});if(s.op.kind===`error`)throw x(t,s.op.message);if(s.op.kind===`upsert`&&s.op.applied===0)throw x(t,h(s.op));return p(t,n,i,s.op)}function D(e,i){let{store:a,delegation:o,maxTasks:p=15}=i;e.registerTool({name:n,label:t,description:`Track complex jobs with a shared task list and delegate tracked work to subagents. Use this only when persistent coordination is useful, such as work with dependencies, parallel workstreams, or a long-running plan. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because the user listed multiple steps. Actions: upsert (write tasks: an entry with an id changes that task and one without an id creates one), list, get, delete (tombstone), clear (close/reset the list), assign (hand tasks through assignments[] to named background subagents), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.`,promptSnippet:g,promptGuidelines:u,parameters:r,renderShell:`self`,async execute(e,t,n,r,c){let l=t.action,u=t;try{if(await i.waitUntilReady?.(c,n),S(l,u),y.has(l)){let e=await E(a,l,u,p);return i.onChange?.(),e}if(l===`assign`){let e=C(u),t=e[0],s=e.length===1&&t?`Delegating task #${t.id}...`:`Delegating ${e.length} tasks...`;r?.(f(l,u,a.snapshot,s));let c=await T(o,e,n,i.report);i.onChange?.();let p=m(c),h=c.flatMap(e=>e.ok?[e.id]:[]);if(h.length===0)throw x(l,p);return d(u,a.snapshot,p,{assigned:h,failed:c.length-h.length})}r?.(f(l,u,a.snapshot,`Requesting cancellation for task #${u.id??`unknown`}...`));let e=await o.cancel(u.id??NaN);if(i.onChange?.(),!e.ok)throw x(l,e.message);return f(l,u,a.snapshot,e.message,void 0)}catch(e){throw i.report?.error(s.toolFailed,e,{[_]:l,...u.id===void 0?{}:{[v]:u.id}}),e instanceof b?e:x(l,e instanceof Error?e.message:String(e))}},renderCall(e,t,n){return c(e,t,a.snapshot.tasks)},renderResult(e,t,n,r){return l(e,t,n)}})}export{u as DEFAULT_PROMPT_GUIDELINES,g as DEFAULT_PROMPT_SNIPPET,D as registerTaskTool};
3
3
  //# sourceMappingURL=taskTool.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"taskTool.mjs","names":[],"sources":["../../../src/commands/task/taskTool.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n MSG_UPSERT_FIELD_MISPLACED,\n TaskParamsSchema,\n TOOL_LABEL,\n TOOL_NAME,\n taskActionAcceptsField,\n} from '../../schemas/task.ts';\nimport type { AssignOptions, DelegationManager, DelegationOutcome } from '../../services/delegation/manager.ts';\nimport { applyTaskMutation, isCommittingOp, type ReducerAction } from '../../services/store/reducer.ts';\nimport type { TaskStore } from '../../adapters/store/taskStore';\nimport type { TaskAction, TaskAssignment, TaskMutationParams } from '../../services/store/types.ts';\nimport { renderTaskCall, renderTaskResult } from '../../tui/format.ts';\nimport { DEFAULT_MAX_TASKS } from '../../types/config.ts';\nimport { TASK_EVENT, type TaskFailureReporter } from '../../types/telemetry.ts';\nimport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\nimport {\n type AssignmentItemResult,\n buildAssignmentResult,\n buildTextResult,\n buildToolResult,\n formatAssignmentResults,\n formatUpsertFailureText,\n type ToolResult,\n} from './responseEnvelope.ts';\n\nexport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\n\nexport const DEFAULT_PROMPT_SNIPPET =\n 'Manage a shared task list to track multi-step progress, and delegate tasks to subagents';\n\nexport interface TaskToolDependencies {\n store: TaskStore;\n delegation: DelegationManager;\n maxTasks?: number;\n onChange?: () => void;\n report?: TaskFailureReporter;\n waitUntilReady?: (context: ExtensionContext, signal?: AbortSignal) => Promise<void>;\n}\n\nconst ACTION_ATTRIBUTE = 'tool.action';\nconst TASK_ID_ATTRIBUTE = 'task.id';\nconst REDUCER_ACTIONS = new Set<TaskAction>(['upsert', 'list', 'get', 'delete', 'clear']);\n\nclass TaskToolExecutionError extends Error {}\n\nfunction actionableTaskError(action: TaskAction, message: string): TaskToolExecutionError {\n return new TaskToolExecutionError(\n [\n `Task ${action} failed: ${message}`,\n '',\n 'Options:',\n '- List the task board and inspect the current ids, states, and blockers.',\n '- Correct the arguments or state conflict, then retry once.',\n '- Ask the user before cancelling work or changing dependencies to recover.',\n ].join('\\n'),\n );\n}\n\n/**\n * Reject a field that belongs to a different action.\n *\n * The params schema is flat and shared across every action, so this is the only\n * place `{\"action\":\"upsert\",\"id\":3}` can be caught — and it must be, because\n * the entry it carries has no id and would silently create a duplicate task.\n */\nfunction rejectStrayFields(action: TaskAction, params: TaskMutationParams): void {\n const stray = Object.keys(params).filter((key) => params[key] !== undefined && !taskActionAcceptsField(action, key));\n if (stray.length === 0) return;\n const hint = action === 'upsert' ? ` ${MSG_UPSERT_FIELD_MISPLACED}` : '';\n throw actionableTaskError(action, `${action} does not accept ${stray.join(', ')}.${hint}`);\n}\n\n/** assignments[] is the only assign contract, even when it contains one task. */\nfunction resolveAssignments(params: TaskMutationParams): TaskAssignment[] {\n const assignments = params.assignments;\n if (!assignments || assignments.length === 0) {\n throw actionableTaskError('assign', 'assign requires a non-empty assignments[] array');\n }\n return assignments;\n}\n\nfunction assignOptions(request: TaskAssignment, signal: AbortSignal | undefined): AssignOptions {\n return {\n agent: request.agent,\n inlineAgent: request.inlineAgent,\n instructions: request.instructions,\n relevantFiles: request.relevantFiles,\n priorFindings: request.priorFindings,\n model: request.model,\n context: request.context,\n signal,\n };\n}\n\nasync function executeAssignmentBatch(\n delegation: DelegationManager,\n assignments: readonly TaskAssignment[],\n signal: AbortSignal | undefined,\n report: TaskFailureReporter | undefined,\n): Promise<AssignmentItemResult[]> {\n const results: AssignmentItemResult[] = [];\n for (const [index, assignment] of assignments.entries()) {\n let outcome: DelegationOutcome;\n try {\n outcome = await delegation.assign(assignment.id, assignOptions(assignment, signal));\n } catch (error) {\n report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: 'assign',\n [TASK_ID_ATTRIBUTE]: assignment.id,\n });\n outcome = { ok: false, message: error instanceof Error ? error.message : String(error) };\n }\n results.push({ index, id: assignment.id, agent: assignment.agent, ...outcome });\n }\n return results;\n}\n\n/** Run an action through the reducer, persisting only when it produced a change. */\nasync function executeReducerAction(\n store: TaskStore,\n action: ReducerAction,\n params: TaskMutationParams,\n maxTasks: number,\n): Promise<ToolResult> {\n const { document, value } = await store.mutate((current) => {\n const result = applyTaskMutation(current, action, params, undefined, maxTasks);\n return {\n ...(isCommittingOp(result.op) ? { document: result.document } : {}),\n value: result,\n };\n });\n if (value.op.kind === 'error') throw actionableTaskError(action, value.op.message);\n // An upsert that applied nothing is a failed call: it wrote nothing and left\n // `rev` alone, so it must not read to the model as a success. A batch where\n // some entries landed returns normally — that is what partial apply means.\n if (value.op.kind === 'upsert' && value.op.applied === 0) {\n throw actionableTaskError(action, formatUpsertFailureText(value.op));\n }\n // `document` is the committed document, so `details.rev` matches what landed\n // on disk; `value.document` is the pre-write copy and lags by one.\n return buildToolResult(action, params, document, value.op);\n}\n\nexport function registerTaskTool(pi: ExtensionAPI, dependencies: TaskToolDependencies): void {\n const { store, delegation, maxTasks = DEFAULT_MAX_TASKS } = dependencies;\n\n pi.registerTool({\n name: TOOL_NAME,\n label: TOOL_LABEL,\n description:\n 'Manage a shared task list for tracking multi-step progress, and delegate tasks to subagents. Actions: upsert (write tasks — a tasks[] array where an entry with an id changes that task and an entry without an id creates one, so one call can capture a whole plan), list, get, delete (tombstone), clear (close/reset the list), assign (hand one or more tasks through assignments[] to named subagents that work in the background), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.',\n promptSnippet: DEFAULT_PROMPT_SNIPPET,\n promptGuidelines: DEFAULT_PROMPT_GUIDELINES,\n parameters: TaskParamsSchema,\n // Task rows carry their own status colors. Owning the shell prevents Pi's\n // pending/success/error background fill from obscuring those row states.\n renderShell: 'self',\n\n async execute(_toolCallId, params, signal, onUpdate, ctx) {\n const action = params.action as TaskAction;\n const mutationParams = params as TaskMutationParams;\n\n try {\n await dependencies.waitUntilReady?.(ctx, signal);\n rejectStrayFields(action, mutationParams);\n if (REDUCER_ACTIONS.has(action)) {\n const result = await executeReducerAction(store, action as ReducerAction, mutationParams, maxTasks);\n dependencies.onChange?.();\n return result;\n }\n\n if (action === 'assign') {\n const assignments = resolveAssignments(mutationParams);\n const first = assignments[0];\n const progress =\n assignments.length === 1 && first\n ? `Delegating task #${first.id}...`\n : `Delegating ${assignments.length} tasks...`;\n onUpdate?.(buildTextResult(action, mutationParams, store.snapshot, progress));\n const results = await executeAssignmentBatch(delegation, assignments, signal, dependencies.report);\n dependencies.onChange?.();\n const text = formatAssignmentResults(results);\n const assigned = results.flatMap((item) => (item.ok ? [item.id] : []));\n if (assigned.length === 0) throw actionableTaskError(action, text);\n return buildAssignmentResult(mutationParams, store.snapshot, text, {\n assigned,\n failed: results.length - assigned.length,\n });\n }\n\n onUpdate?.(\n buildTextResult(\n action,\n mutationParams,\n store.snapshot,\n `Requesting cancellation for task #${mutationParams.id ?? 'unknown'}...`,\n ),\n );\n const outcome = await delegation.cancel(mutationParams.id ?? Number.NaN);\n dependencies.onChange?.();\n if (!outcome.ok) throw actionableTaskError(action, outcome.message);\n return buildTextResult(action, mutationParams, store.snapshot, outcome.message, undefined);\n } catch (error) {\n // Rethrown so pi still renders the failure to the model; recorded\n // because this is where a store fault becomes visible to the user.\n dependencies.report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: action,\n ...(mutationParams.id === undefined ? {} : { [TASK_ID_ATTRIBUTE]: mutationParams.id }),\n });\n if (error instanceof TaskToolExecutionError) throw error;\n throw actionableTaskError(action, error instanceof Error ? error.message : String(error));\n }\n },\n\n // Render hooks receive no session identity, so they read the store snapshot\n // that the executing session last loaded. That is the foreground session's\n // own view, which is exactly what its transcript should show.\n renderCall(args, theme, _context) {\n return renderTaskCall(args as never, theme, store.snapshot.tasks);\n },\n\n renderResult(result, options, theme, _context) {\n return renderTaskResult(result, options, theme);\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"taskTool.mjs","names":[],"sources":["../../../src/commands/task/taskTool.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n MSG_UPSERT_FIELD_MISPLACED,\n TaskParamsSchema,\n TOOL_LABEL,\n TOOL_NAME,\n taskActionAcceptsField,\n} from '../../schemas/task.ts';\nimport type { AssignOptions, DelegationManager, DelegationOutcome } from '../../services/delegation/manager.ts';\nimport { applyTaskMutation, isCommittingOp, type ReducerAction } from '../../services/store/reducer.ts';\nimport type { TaskStore } from '../../adapters/store/taskStore';\nimport type { TaskAction, TaskAssignment, TaskMutationParams } from '../../services/store/types.ts';\nimport { renderTaskCall, renderTaskResult } from '../../tui/format.ts';\nimport { DEFAULT_MAX_TASKS } from '../../types/config.ts';\nimport { TASK_EVENT, type TaskFailureReporter } from '../../types/telemetry.ts';\nimport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\nimport {\n type AssignmentItemResult,\n buildAssignmentResult,\n buildTextResult,\n buildToolResult,\n formatAssignmentResults,\n formatUpsertFailureText,\n type ToolResult,\n} from './responseEnvelope.ts';\n\nexport { DEFAULT_PROMPT_GUIDELINES } from './promptGuidelines.ts';\n\nexport const DEFAULT_PROMPT_SNIPPET =\n 'Track only complex, multi-step jobs that benefit from persistent progress or delegation; skip simple work';\n\nexport interface TaskToolDependencies {\n store: TaskStore;\n delegation: DelegationManager;\n maxTasks?: number;\n onChange?: () => void;\n report?: TaskFailureReporter;\n waitUntilReady?: (context: ExtensionContext, signal?: AbortSignal) => Promise<void>;\n}\n\nconst ACTION_ATTRIBUTE = 'tool.action';\nconst TASK_ID_ATTRIBUTE = 'task.id';\nconst REDUCER_ACTIONS = new Set<TaskAction>(['upsert', 'list', 'get', 'delete', 'clear']);\n\nclass TaskToolExecutionError extends Error {}\n\nfunction actionableTaskError(action: TaskAction, message: string): TaskToolExecutionError {\n return new TaskToolExecutionError(\n [\n `Task ${action} failed: ${message}`,\n '',\n 'Options:',\n '- List the task board and inspect the current ids, states, and blockers.',\n '- Correct the arguments or state conflict, then retry once.',\n '- Ask the user before cancelling work or changing dependencies to recover.',\n ].join('\\n'),\n );\n}\n\n/**\n * Reject a field that belongs to a different action.\n *\n * The params schema is flat and shared across every action, so this is the only\n * place `{\"action\":\"upsert\",\"id\":3}` can be caught — and it must be, because\n * the entry it carries has no id and would silently create a duplicate task.\n */\nfunction rejectStrayFields(action: TaskAction, params: TaskMutationParams): void {\n const stray = Object.keys(params).filter((key) => params[key] !== undefined && !taskActionAcceptsField(action, key));\n if (stray.length === 0) return;\n const hint = action === 'upsert' ? ` ${MSG_UPSERT_FIELD_MISPLACED}` : '';\n throw actionableTaskError(action, `${action} does not accept ${stray.join(', ')}.${hint}`);\n}\n\n/** assignments[] is the only assign contract, even when it contains one task. */\nfunction resolveAssignments(params: TaskMutationParams): TaskAssignment[] {\n const assignments = params.assignments;\n if (!assignments || assignments.length === 0) {\n throw actionableTaskError('assign', 'assign requires a non-empty assignments[] array');\n }\n return assignments;\n}\n\nfunction assignOptions(request: TaskAssignment, signal: AbortSignal | undefined): AssignOptions {\n return {\n agent: request.agent,\n inlineAgent: request.inlineAgent,\n instructions: request.instructions,\n relevantFiles: request.relevantFiles,\n priorFindings: request.priorFindings,\n model: request.model,\n context: request.context,\n signal,\n };\n}\n\nasync function executeAssignmentBatch(\n delegation: DelegationManager,\n assignments: readonly TaskAssignment[],\n signal: AbortSignal | undefined,\n report: TaskFailureReporter | undefined,\n): Promise<AssignmentItemResult[]> {\n const results: AssignmentItemResult[] = [];\n for (const [index, assignment] of assignments.entries()) {\n let outcome: DelegationOutcome;\n try {\n outcome = await delegation.assign(assignment.id, assignOptions(assignment, signal));\n } catch (error) {\n report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: 'assign',\n [TASK_ID_ATTRIBUTE]: assignment.id,\n });\n outcome = { ok: false, message: error instanceof Error ? error.message : String(error) };\n }\n results.push({ index, id: assignment.id, agent: assignment.agent, ...outcome });\n }\n return results;\n}\n\n/** Run an action through the reducer, persisting only when it produced a change. */\nasync function executeReducerAction(\n store: TaskStore,\n action: ReducerAction,\n params: TaskMutationParams,\n maxTasks: number,\n): Promise<ToolResult> {\n const { document, value } = await store.mutate((current) => {\n const result = applyTaskMutation(current, action, params, undefined, maxTasks);\n return {\n ...(isCommittingOp(result.op) ? { document: result.document } : {}),\n value: result,\n };\n });\n if (value.op.kind === 'error') throw actionableTaskError(action, value.op.message);\n // An upsert that applied nothing is a failed call: it wrote nothing and left\n // `rev` alone, so it must not read to the model as a success. A batch where\n // some entries landed returns normally — that is what partial apply means.\n if (value.op.kind === 'upsert' && value.op.applied === 0) {\n throw actionableTaskError(action, formatUpsertFailureText(value.op));\n }\n // `document` is the committed document, so `details.rev` matches what landed\n // on disk; `value.document` is the pre-write copy and lags by one.\n return buildToolResult(action, params, document, value.op);\n}\n\nexport function registerTaskTool(pi: ExtensionAPI, dependencies: TaskToolDependencies): void {\n const { store, delegation, maxTasks = DEFAULT_MAX_TASKS } = dependencies;\n\n pi.registerTool({\n name: TOOL_NAME,\n label: TOOL_LABEL,\n description:\n 'Track complex jobs with a shared task list and delegate tracked work to subagents. Use this only when persistent coordination is useful, such as work with dependencies, parallel workstreams, or a long-running plan. Do not use it for simple requests, routine edits, straightforward command sequences, or merely because the user listed multiple steps. Actions: upsert (write tasks: an entry with an id changes that task and one without an id creates one), list, get, delete (tombstone), clear (close/reset the list), assign (hand tasks through assignments[] to named background subagents), cancel (stop a delegated run). upsert and assign apply entries independently: successes remain committed or delegated and failures are reported by array index. Batch a plan and independent assignments, but report progress one task at a time. Status: pending → in_progress → completed, plus failed and a deleted tombstone. The list is shared within the current session tree, including delegated subagents, while unrelated sessions start empty. Session stores persist until retention cleanup.',\n promptSnippet: DEFAULT_PROMPT_SNIPPET,\n promptGuidelines: DEFAULT_PROMPT_GUIDELINES,\n parameters: TaskParamsSchema,\n // Task rows carry their own status colors. Owning the shell prevents Pi's\n // pending/success/error background fill from obscuring those row states.\n renderShell: 'self',\n\n async execute(_toolCallId, params, signal, onUpdate, ctx) {\n const action = params.action as TaskAction;\n const mutationParams = params as TaskMutationParams;\n\n try {\n await dependencies.waitUntilReady?.(ctx, signal);\n rejectStrayFields(action, mutationParams);\n if (REDUCER_ACTIONS.has(action)) {\n const result = await executeReducerAction(store, action as ReducerAction, mutationParams, maxTasks);\n dependencies.onChange?.();\n return result;\n }\n\n if (action === 'assign') {\n const assignments = resolveAssignments(mutationParams);\n const first = assignments[0];\n const progress =\n assignments.length === 1 && first\n ? `Delegating task #${first.id}...`\n : `Delegating ${assignments.length} tasks...`;\n onUpdate?.(buildTextResult(action, mutationParams, store.snapshot, progress));\n const results = await executeAssignmentBatch(delegation, assignments, signal, dependencies.report);\n dependencies.onChange?.();\n const text = formatAssignmentResults(results);\n const assigned = results.flatMap((item) => (item.ok ? [item.id] : []));\n if (assigned.length === 0) throw actionableTaskError(action, text);\n return buildAssignmentResult(mutationParams, store.snapshot, text, {\n assigned,\n failed: results.length - assigned.length,\n });\n }\n\n onUpdate?.(\n buildTextResult(\n action,\n mutationParams,\n store.snapshot,\n `Requesting cancellation for task #${mutationParams.id ?? 'unknown'}...`,\n ),\n );\n const outcome = await delegation.cancel(mutationParams.id ?? Number.NaN);\n dependencies.onChange?.();\n if (!outcome.ok) throw actionableTaskError(action, outcome.message);\n return buildTextResult(action, mutationParams, store.snapshot, outcome.message, undefined);\n } catch (error) {\n // Rethrown so pi still renders the failure to the model; recorded\n // because this is where a store fault becomes visible to the user.\n dependencies.report?.error(TASK_EVENT.toolFailed, error, {\n [ACTION_ATTRIBUTE]: action,\n ...(mutationParams.id === undefined ? {} : { [TASK_ID_ATTRIBUTE]: mutationParams.id }),\n });\n if (error instanceof TaskToolExecutionError) throw error;\n throw actionableTaskError(action, error instanceof Error ? error.message : String(error));\n }\n },\n\n // Render hooks receive no session identity, so they read the store snapshot\n // that the executing session last loaded. That is the foreground session's\n // own view, which is exactly what its transcript should show.\n renderCall(args, theme, _context) {\n return renderTaskCall(args as never, theme, store.snapshot.tasks);\n },\n\n renderResult(result, options, theme, _context) {\n return renderTaskResult(result, options, theme);\n },\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ const e=`@agimon-ai/doompi-task`,t=`active-tasks`,n=`Active tasks`;function r(e){return e.filter(e=>e.status===`pending`||e.status===`in_progress`||e.status===`failed`).map(e=>({id:e.id,status:e.status,subject:e.subject,...e.activeForm?{activeForm:e.activeForm}:{},...e.owner?{owner:e.owner}:{},...e.blockedBy?.length?{blockedBy:e.blockedBy}:{},...e.delegation?{delegation:{agent:e.delegation.agent,state:e.delegation.state,...e.delegation.result?.error?{error:e.delegation.result.error}:{}}}:{}}))}function i(e){return e.length===0?`(no active tasks)`:e.map(e=>{let t=[e.activeForm?`active: ${e.activeForm}`:void 0,e.owner?`owner: ${e.owner}`:void 0,e.blockedBy?.length?`blocked by: ${e.blockedBy.join(`, `)}`:void 0,e.delegation?`delegation: ${e.delegation.agent} (${e.delegation.state})`:void 0,e.delegation?.error?`error: ${e.delegation.error}`:void 0].filter(e=>!!e);return`- #${e.id} [${e.status}] ${e.subject}${t.length>0?` | ${t.join(` | `)}`:``}`}).join(`
2
+ `)}function a(a){return Object.freeze({source:e,id:t,label:n,order:100,snapshot:()=>i(r(a()))})}exports.TASK_CONTEXT_CONTRIBUTION_ID=t,exports.TASK_CONTEXT_CONTRIBUTION_LABEL=n,exports.TASK_CONTEXT_CONTRIBUTION_SOURCE=e,exports.createTaskContextContribution=a;
3
+ //# sourceMappingURL=contextContribution.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextContribution.cjs","names":[],"sources":["../../src/services/contextContribution.ts"],"sourcesContent":["import type { DoomContextContribution } from '@agimon-ai/doompi-extension-contracts/context-contributions';\nimport type { Task } from './store/types.ts';\n\nexport const TASK_CONTEXT_CONTRIBUTION_SOURCE = '@agimon-ai/doompi-task';\nexport const TASK_CONTEXT_CONTRIBUTION_ID = 'active-tasks';\nexport const TASK_CONTEXT_CONTRIBUTION_LABEL = 'Active tasks';\nexport const TASK_CONTEXT_CONTRIBUTION_ORDER = 100;\n\ninterface TaskContextSnapshot {\n readonly id: number;\n readonly status: Task['status'];\n readonly subject: string;\n readonly activeForm?: string;\n readonly owner?: string;\n readonly blockedBy?: readonly number[];\n readonly delegation?: {\n readonly agent: string;\n readonly state: NonNullable<Task['delegation']>['state'];\n readonly error?: string;\n };\n}\n\nfunction activeTaskSnapshots(tasks: readonly Task[]): TaskContextSnapshot[] {\n return tasks\n .filter((task) => task.status === 'pending' || task.status === 'in_progress' || task.status === 'failed')\n .map((task) => ({\n id: task.id,\n status: task.status,\n subject: task.subject,\n ...(task.activeForm ? { activeForm: task.activeForm } : {}),\n ...(task.owner ? { owner: task.owner } : {}),\n ...(task.blockedBy?.length ? { blockedBy: task.blockedBy } : {}),\n ...(task.delegation\n ? {\n delegation: {\n agent: task.delegation.agent,\n state: task.delegation.state,\n ...(task.delegation.result?.error ? { error: task.delegation.result.error } : {}),\n },\n }\n : {}),\n }));\n}\n\nfunction formatTaskSnapshots(tasks: readonly TaskContextSnapshot[]): string {\n if (tasks.length === 0) return '(no active tasks)';\n return tasks\n .map((task) => {\n const details = [\n task.activeForm ? `active: ${task.activeForm}` : undefined,\n task.owner ? `owner: ${task.owner}` : undefined,\n task.blockedBy?.length ? `blocked by: ${task.blockedBy.join(', ')}` : undefined,\n task.delegation ? `delegation: ${task.delegation.agent} (${task.delegation.state})` : undefined,\n task.delegation?.error ? `error: ${task.delegation.error}` : undefined,\n ].filter((value): value is string => Boolean(value));\n return `- #${task.id} [${task.status}] ${task.subject}${details.length > 0 ? ` | ${details.join(' | ')}` : ''}`;\n })\n .join('\\n');\n}\n\n/** Builds Task's bounded, synchronous contribution from its in-memory store snapshot. */\nexport function createTaskContextContribution(readTasks: () => readonly Task[]): DoomContextContribution {\n return Object.freeze({\n source: TASK_CONTEXT_CONTRIBUTION_SOURCE,\n id: TASK_CONTEXT_CONTRIBUTION_ID,\n label: TASK_CONTEXT_CONTRIBUTION_LABEL,\n order: TASK_CONTEXT_CONTRIBUTION_ORDER,\n snapshot: () => formatTaskSnapshots(activeTaskSnapshots(readTasks())),\n });\n}\n"],"mappings":"AAGA,MAAa,EAAmC,yBACnC,EAA+B,eAC/B,EAAkC,eAiB/C,SAAS,EAAoB,EAA+C,CAC1E,OAAO,EACJ,OAAQ,GAAS,EAAK,SAAW,WAAa,EAAK,SAAW,eAAiB,EAAK,SAAW,QAAQ,CAAC,CACxG,IAAK,IAAU,CACd,GAAI,EAAK,GACT,OAAQ,EAAK,OACb,QAAS,EAAK,QACd,GAAI,EAAK,WAAa,CAAE,WAAY,EAAK,UAAW,EAAI,CAAC,EACzD,GAAI,EAAK,MAAQ,CAAE,MAAO,EAAK,KAAM,EAAI,CAAC,EAC1C,GAAI,EAAK,WAAW,OAAS,CAAE,UAAW,EAAK,SAAU,EAAI,CAAC,EAC9D,GAAI,EAAK,WACL,CACE,WAAY,CACV,MAAO,EAAK,WAAW,MACvB,MAAO,EAAK,WAAW,MACvB,GAAI,EAAK,WAAW,QAAQ,MAAQ,CAAE,MAAO,EAAK,WAAW,OAAO,KAAM,EAAI,CAAC,CACjF,CACF,EACA,CAAC,CACP,EAAE,CACN,CAEA,SAAS,EAAoB,EAA+C,CAE1E,OADI,EAAM,SAAW,EAAU,oBACxB,EACJ,IAAK,GAAS,CACb,IAAM,EAAU,CACd,EAAK,WAAa,WAAW,EAAK,aAAe,IAAA,GACjD,EAAK,MAAQ,UAAU,EAAK,QAAU,IAAA,GACtC,EAAK,WAAW,OAAS,eAAe,EAAK,UAAU,KAAK,IAAI,IAAM,IAAA,GACtE,EAAK,WAAa,eAAe,EAAK,WAAW,MAAM,IAAI,EAAK,WAAW,MAAM,GAAK,IAAA,GACtF,EAAK,YAAY,MAAQ,UAAU,EAAK,WAAW,QAAU,IAAA,EAC/D,CAAC,CAAC,OAAQ,GAA2B,EAAQ,CAAM,EACnD,MAAO,MAAM,EAAK,GAAG,IAAI,EAAK,OAAO,IAAI,EAAK,UAAU,EAAQ,OAAS,EAAI,MAAM,EAAQ,KAAK,KAAK,IAAM,IAC7G,CAAC,CAAC,CACD,KAAK;CAAI,CACd,CAGA,SAAgB,EAA8B,EAA2D,CACvG,OAAO,OAAO,OAAO,CACnB,OAAQ,EACR,GAAI,EACJ,MAAO,EACP,MAAA,IACA,aAAgB,EAAoB,EAAoB,EAAU,CAAC,CAAC,CACtE,CAAC,CACH"}
@@ -0,0 +1,3 @@
1
+ const e=`@agimon-ai/doompi-task`,t=`active-tasks`,n=`Active tasks`;function r(e){return e.filter(e=>e.status===`pending`||e.status===`in_progress`||e.status===`failed`).map(e=>({id:e.id,status:e.status,subject:e.subject,...e.activeForm?{activeForm:e.activeForm}:{},...e.owner?{owner:e.owner}:{},...e.blockedBy?.length?{blockedBy:e.blockedBy}:{},...e.delegation?{delegation:{agent:e.delegation.agent,state:e.delegation.state,...e.delegation.result?.error?{error:e.delegation.result.error}:{}}}:{}}))}function i(e){return e.length===0?`(no active tasks)`:e.map(e=>{let t=[e.activeForm?`active: ${e.activeForm}`:void 0,e.owner?`owner: ${e.owner}`:void 0,e.blockedBy?.length?`blocked by: ${e.blockedBy.join(`, `)}`:void 0,e.delegation?`delegation: ${e.delegation.agent} (${e.delegation.state})`:void 0,e.delegation?.error?`error: ${e.delegation.error}`:void 0].filter(e=>!!e);return`- #${e.id} [${e.status}] ${e.subject}${t.length>0?` | ${t.join(` | `)}`:``}`}).join(`
2
+ `)}function a(a){return Object.freeze({source:e,id:t,label:n,order:100,snapshot:()=>i(r(a()))})}export{t as TASK_CONTEXT_CONTRIBUTION_ID,n as TASK_CONTEXT_CONTRIBUTION_LABEL,e as TASK_CONTEXT_CONTRIBUTION_SOURCE,a as createTaskContextContribution};
3
+ //# sourceMappingURL=contextContribution.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextContribution.mjs","names":[],"sources":["../../src/services/contextContribution.ts"],"sourcesContent":["import type { DoomContextContribution } from '@agimon-ai/doompi-extension-contracts/context-contributions';\nimport type { Task } from './store/types.ts';\n\nexport const TASK_CONTEXT_CONTRIBUTION_SOURCE = '@agimon-ai/doompi-task';\nexport const TASK_CONTEXT_CONTRIBUTION_ID = 'active-tasks';\nexport const TASK_CONTEXT_CONTRIBUTION_LABEL = 'Active tasks';\nexport const TASK_CONTEXT_CONTRIBUTION_ORDER = 100;\n\ninterface TaskContextSnapshot {\n readonly id: number;\n readonly status: Task['status'];\n readonly subject: string;\n readonly activeForm?: string;\n readonly owner?: string;\n readonly blockedBy?: readonly number[];\n readonly delegation?: {\n readonly agent: string;\n readonly state: NonNullable<Task['delegation']>['state'];\n readonly error?: string;\n };\n}\n\nfunction activeTaskSnapshots(tasks: readonly Task[]): TaskContextSnapshot[] {\n return tasks\n .filter((task) => task.status === 'pending' || task.status === 'in_progress' || task.status === 'failed')\n .map((task) => ({\n id: task.id,\n status: task.status,\n subject: task.subject,\n ...(task.activeForm ? { activeForm: task.activeForm } : {}),\n ...(task.owner ? { owner: task.owner } : {}),\n ...(task.blockedBy?.length ? { blockedBy: task.blockedBy } : {}),\n ...(task.delegation\n ? {\n delegation: {\n agent: task.delegation.agent,\n state: task.delegation.state,\n ...(task.delegation.result?.error ? { error: task.delegation.result.error } : {}),\n },\n }\n : {}),\n }));\n}\n\nfunction formatTaskSnapshots(tasks: readonly TaskContextSnapshot[]): string {\n if (tasks.length === 0) return '(no active tasks)';\n return tasks\n .map((task) => {\n const details = [\n task.activeForm ? `active: ${task.activeForm}` : undefined,\n task.owner ? `owner: ${task.owner}` : undefined,\n task.blockedBy?.length ? `blocked by: ${task.blockedBy.join(', ')}` : undefined,\n task.delegation ? `delegation: ${task.delegation.agent} (${task.delegation.state})` : undefined,\n task.delegation?.error ? `error: ${task.delegation.error}` : undefined,\n ].filter((value): value is string => Boolean(value));\n return `- #${task.id} [${task.status}] ${task.subject}${details.length > 0 ? ` | ${details.join(' | ')}` : ''}`;\n })\n .join('\\n');\n}\n\n/** Builds Task's bounded, synchronous contribution from its in-memory store snapshot. */\nexport function createTaskContextContribution(readTasks: () => readonly Task[]): DoomContextContribution {\n return Object.freeze({\n source: TASK_CONTEXT_CONTRIBUTION_SOURCE,\n id: TASK_CONTEXT_CONTRIBUTION_ID,\n label: TASK_CONTEXT_CONTRIBUTION_LABEL,\n order: TASK_CONTEXT_CONTRIBUTION_ORDER,\n snapshot: () => formatTaskSnapshots(activeTaskSnapshots(readTasks())),\n });\n}\n"],"mappings":"AAGA,MAAa,EAAmC,yBACnC,EAA+B,eAC/B,EAAkC,eAiB/C,SAAS,EAAoB,EAA+C,CAC1E,OAAO,EACJ,OAAQ,GAAS,EAAK,SAAW,WAAa,EAAK,SAAW,eAAiB,EAAK,SAAW,QAAQ,CAAC,CACxG,IAAK,IAAU,CACd,GAAI,EAAK,GACT,OAAQ,EAAK,OACb,QAAS,EAAK,QACd,GAAI,EAAK,WAAa,CAAE,WAAY,EAAK,UAAW,EAAI,CAAC,EACzD,GAAI,EAAK,MAAQ,CAAE,MAAO,EAAK,KAAM,EAAI,CAAC,EAC1C,GAAI,EAAK,WAAW,OAAS,CAAE,UAAW,EAAK,SAAU,EAAI,CAAC,EAC9D,GAAI,EAAK,WACL,CACE,WAAY,CACV,MAAO,EAAK,WAAW,MACvB,MAAO,EAAK,WAAW,MACvB,GAAI,EAAK,WAAW,QAAQ,MAAQ,CAAE,MAAO,EAAK,WAAW,OAAO,KAAM,EAAI,CAAC,CACjF,CACF,EACA,CAAC,CACP,EAAE,CACN,CAEA,SAAS,EAAoB,EAA+C,CAE1E,OADI,EAAM,SAAW,EAAU,oBACxB,EACJ,IAAK,GAAS,CACb,IAAM,EAAU,CACd,EAAK,WAAa,WAAW,EAAK,aAAe,IAAA,GACjD,EAAK,MAAQ,UAAU,EAAK,QAAU,IAAA,GACtC,EAAK,WAAW,OAAS,eAAe,EAAK,UAAU,KAAK,IAAI,IAAM,IAAA,GACtE,EAAK,WAAa,eAAe,EAAK,WAAW,MAAM,IAAI,EAAK,WAAW,MAAM,GAAK,IAAA,GACtF,EAAK,YAAY,MAAQ,UAAU,EAAK,WAAW,QAAU,IAAA,EAC/D,CAAC,CAAC,OAAQ,GAA2B,EAAQ,CAAM,EACnD,MAAO,MAAM,EAAK,GAAG,IAAI,EAAK,OAAO,IAAI,EAAK,UAAU,EAAQ,OAAS,EAAI,MAAM,EAAQ,KAAK,KAAK,IAAM,IAC7G,CAAC,CAAC,CACD,KAAK;CAAI,CACd,CAGA,SAAgB,EAA8B,EAA2D,CACvG,OAAO,OAAO,OAAO,CACnB,OAAQ,EACR,GAAI,EACJ,MAAO,EACP,MAAA,IACA,aAAgB,EAAoB,EAAoB,EAAU,CAAC,CAAC,CACtE,CAAC,CACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-task",
3
- "version": "0.0.1-alpha.28",
3
+ "version": "0.0.1-alpha.29",
4
4
  "description": "Persistent, dependency-aware task graphs and subagent delegation for Pi coding sessions.",
5
5
  "keywords": [
6
6
  "ai",
@@ -150,9 +150,9 @@
150
150
  "dependencies": {
151
151
  "@deepseek-ai/cordis": "4.0.1",
152
152
  "typebox": "1.3.16",
153
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.28",
154
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.28",
155
- "@agimon-ai/doompi-ui": "0.0.1-alpha.28"
153
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.29",
154
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.29",
155
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.29"
156
156
  },
157
157
  "devDependencies": {
158
158
  "@earendil-works/pi-coding-agent": "0.84.2",