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