@agimon-ai/doompi-notification 0.0.1-alpha.35 → 0.0.1-alpha.41
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 +1 -1
- package/dist/adapters/pi/extension.cjs.map +1 -1
- package/dist/adapters/pi/extension.d.cts +3 -3
- package/dist/adapters/pi/extension.d.cts.map +1 -1
- package/dist/adapters/pi/extension.d.mts +3 -3
- package/dist/adapters/pi/extension.d.mts.map +1 -1
- package/dist/adapters/pi/extension.mjs +1 -1
- package/dist/adapters/pi/extension.mjs.map +1 -1
- package/dist/adapters/shellTitleController.cjs +1 -1
- package/dist/adapters/shellTitleController.cjs.map +1 -1
- package/dist/adapters/shellTitleController.d.cts +2 -3
- package/dist/adapters/shellTitleController.d.cts.map +1 -1
- package/dist/adapters/shellTitleController.d.mts +2 -3
- package/dist/adapters/shellTitleController.d.mts.map +1 -1
- package/dist/adapters/shellTitleController.mjs +1 -1
- package/dist/adapters/shellTitleController.mjs.map +1 -1
- package/dist/adapters/systemNotification.d.cts +1 -2
- package/dist/adapters/systemNotification.d.cts.map +1 -1
- package/dist/adapters/systemNotification.d.mts +1 -2
- package/dist/adapters/systemNotification.d.mts.map +1 -1
- package/dist/services/notificationPolicy.d.cts +6 -7
- package/dist/services/notificationPolicy.d.cts.map +1 -1
- package/dist/services/notificationPolicy.d.mts +6 -7
- package/dist/services/notificationPolicy.d.mts.map +1 -1
- package/dist/services/notificationText.d.cts +6 -7
- package/dist/services/notificationText.d.cts.map +1 -1
- package/dist/services/notificationText.d.mts +6 -7
- package/dist/services/notificationText.d.mts.map +1 -1
- package/dist/types/notifications.d.cts +5 -6
- package/dist/types/notifications.d.cts.map +1 -1
- package/dist/types/notifications.d.mts +5 -6
- package/dist/types/notifications.d.mts.map +1 -1
- package/package.json +10 -10
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../services/notificationPolicy.cjs"),t=require("../../services/notificationText.cjs"),n=require("../notificationRouter.cjs"),r=require("../shellTitleController.cjs");let i=require("@agimon-ai/doompi-extension-contracts/ask-user"),a=require("@agimon-ai/doompi-extension-contracts/child-process"),o=require("@agimon-ai/doompi-extension-contracts/cordis-host"),s=require("@agimon-ai/doompi-extension-contracts/notification");const c=`@agimon-ai/doompi-notification`,l=Symbol.for(`@agimon-ai/doompi-notification/original-ui-method`);function u(e,t){let n=e[t],r=new Set;for(;typeof n==`function`&&!r.has(n);){r.add(n);let e=Reflect.get(n,l);if(typeof e!=`function`)break;n=e}return n}function d(e,t){Reflect.defineProperty(e,l,{configurable:!1,enumerable:!1,value:t,writable:!1})}function f(e,t,n,r,i){let a=u(e,`confirm`),o=(t,i,o)=>(n()&&r(`${t}: ${i}`),a.call(e,t,i,o));d(o,a),e.confirm=o;let s=u(e,`select`),c=(t,i,a)=>(n()&&r(t),s.call(e,t,i,a));d(c,s),e.select=c;let l=u(e,`input`),f=(t,i,a)=>(n()&&r(t),l.call(e,t,i,a));d(f,l),e.input=f;let p=u(e,`editor`),m=(t,i)=>(n()&&r(t),p.call(e,t,i));d(m,p),e.editor=m;let h=u(e,`notify`),g=(n,r)=>{t()?i(n,r):h.call(e,n,r)};return d(g,h),e.notify=g,()=>{e.confirm===o&&(e.confirm=a),e.select===c&&(e.select=s),e.input===f&&(e.input=l),e.editor===m&&(e.editor=p),e.notify===g&&(e.notify=h)}}function p(a,{generation:l,pi:u,options:d}){let p,m=n.createDoomNotificationRouter({generation:l,pi:u,context:()=>p});a.provide(s.DOOM_NOTIFICATION_SERVICE,m),a.inject([o.DOOM_CORDIS_SESSION_SERVICE],e=>{let t=e.get(o.DOOM_CORDIS_SESSION_SERVICE).context;return p=t,()=>{p===t&&(p=void 0)}}),a.effect(function*(){let n=d.titleController??r.createWorkerTitleController(),o=new Map,s=!0,c=!1,l=!1,h,g,_=e=>t.shellTabTitle({cwd:e,sessionName:u.getSessionName(),prompt:h}),v=t=>{if(
|
|
1
|
+
const e=require("../../services/notificationPolicy.cjs"),t=require("../../services/notificationText.cjs"),n=require("../notificationRouter.cjs"),r=require("../shellTitleController.cjs");let i=require("@agimon-ai/doompi-extension-contracts/ask-user"),a=require("@agimon-ai/doompi-extension-contracts/child-process"),o=require("@agimon-ai/doompi-extension-contracts/cordis-host"),s=require("@agimon-ai/doompi-extension-contracts/notification");const c=`@agimon-ai/doompi-notification`,l=Symbol.for(`@agimon-ai/doompi-notification/original-ui-method`);function u(e,t){let n=e[t],r=new Set;for(;typeof n==`function`&&!r.has(n);){r.add(n);let e=Reflect.get(n,l);if(typeof e!=`function`)break;n=e}return n}function d(e,t){Reflect.defineProperty(e,l,{configurable:!1,enumerable:!1,value:t,writable:!1})}function f(e,t,n,r,i){let a=u(e,`confirm`),o=(t,i,o)=>(n()&&r(`${t}: ${i}`),a.call(e,t,i,o));d(o,a),e.confirm=o;let s=u(e,`select`),c=(t,i,a)=>(n()&&r(t),s.call(e,t,i,a));d(c,s),e.select=c;let l=u(e,`input`),f=(t,i,a)=>(n()&&r(t),l.call(e,t,i,a));d(f,l),e.input=f;let p=u(e,`editor`),m=(t,i)=>(n()&&r(t),p.call(e,t,i));d(m,p),e.editor=m;let h=u(e,`notify`),g=(n,r)=>{t()?i(n,r):h.call(e,n,r)};return d(g,h),e.notify=g,()=>{e.confirm===o&&(e.confirm=a),e.select===c&&(e.select=s),e.input===f&&(e.input=l),e.editor===m&&(e.editor=p),e.notify===g&&(e.notify=h)}}function p(a,{generation:l,pi:u,options:d}){let p,m=n.createDoomNotificationRouter({generation:l,pi:u,context:()=>p});a.provide(s.DOOM_NOTIFICATION_SERVICE,m),a.inject([o.DOOM_CORDIS_SESSION_SERVICE],e=>{let t=e.get(o.DOOM_CORDIS_SESSION_SERVICE).context;return p=t,()=>{p===t&&(p=void 0)}}),a.effect(function*(){let n=d.titleController??r.createWorkerTitleController(),o=new Map,s=!0,c=!1,l=!1,h,g,_=e=>t.shellTabTitle({cwd:e,sessionName:u.getSessionName(),prompt:h}),v=t=>{if(s&&e.supportsShellTitle(t))return g={cwd:t.cwd,write:e=>t.ui.setTitle(e)},g},y=e=>{s&&m.request(t.attentionNotification(e))};yield a.on(i.DOOM_ASK_USER_PROMPT_EVENT,t=>{y(e.askUserPromptBody(t.questions))}),yield a.on(i.DOOM_ASK_USER_BLOCKED_EVENT,e=>{l=e.active}),yield()=>{g&&n.dispose(_(g.cwd),g.write),g=void 0},yield()=>{for(let e of o.values())e();o.clear()},yield()=>{s=!1,c=!1,l=!1},u.on(`input`,(e,r)=>{if(!s||h||e.source===`extension`)return;p=r,h=t.promptTitle(e.text);let i=h?v(r):void 0;i&&n.set(_(i.cwd),i.write)}),u.on(`session_start`,(t,n)=>{s&&!o.has(n.ui)&&(p=n,o.set(n.ui,f(n.ui,()=>s,()=>s&&e.warrantsAttentionNotification({agentRunning:c,askUserBlocked:l}),y,(e,t)=>void m.request({body:e,...t?{level:t}:{}}))))}),u.on(`agent_start`,(e,t)=>{if(!s)return;p=t,c=!0;let r=v(t);r&&n.start(_(r.cwd),r.write)}),u.on(`agent_settled`,async(r,i)=>{if(!s)return;p=i,c=!1,l=!1;let a=v(i);a&&n.stop(_(a.cwd),a.write),e.warrantsSettledNotification(i.hasPendingMessages())&&await m.request(t.settledNotification({cwd:i.cwd,sessionName:u.getSessionName()}))})},c)}async function m(e,t={}){if((t.environment??process.env)[a.SUBAGENT_CHILD_ENV])return;let n=await(0,o.connectDoomCordisHost)(e,c,{environment:t.environment??process.env}),r=n.root.plugin(p,{generation:`${n.runtime.generation}:notification`,pi:e,options:t});try{await r}catch(e){try{await r.dispose()}finally{await n.dispose()}throw e}let i;e.on(`session_shutdown`,()=>i??=(async()=>{try{await r.dispose()}finally{await n.dispose()}})())}exports.default=m,exports.notificationExtension=m;
|
|
2
2
|
//# sourceMappingURL=extension.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.cjs","names":["createDoomNotificationRouter","DOOM_NOTIFICATION_SERVICE","DOOM_CORDIS_SESSION_SERVICE","createWorkerTitleController","shellTabTitle","supportsShellTitle","attentionNotification","DOOM_ASK_USER_PROMPT_EVENT","askUserPromptBody","DOOM_ASK_USER_BLOCKED_EVENT","promptTitle","warrantsAttentionNotification","warrantsSettledNotification","settledNotification","SUBAGENT_CHILD_ENV","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_ASK_USER_BLOCKED_EVENT,\n DOOM_ASK_USER_PROMPT_EVENT,\n} from '@agimon-ai/doompi-extension-contracts/ask-user';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_NOTIFICATION_SERVICE,\n type DoomNotificationLevel,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext } from '@earendil-works/pi-coding-agent';\nimport {\n askUserPromptBody,\n supportsShellTitle,\n warrantsAttentionNotification,\n warrantsSettledNotification,\n} from '../../services/notificationPolicy.ts';\nimport {\n attentionNotification,\n promptTitle,\n settledNotification,\n shellTabTitle,\n} from '../../services/notificationText.ts';\nimport type { ShellTitleController, WriteTitle } from '../../types/notifications.ts';\nimport { createDoomNotificationRouter } from '../notificationRouter.ts';\nimport { createWorkerTitleController } from '../shellTitleController.ts';\n\nconst AGENT_SETTLED_EVENT = 'agent_settled';\nconst AGENT_START_EVENT = 'agent_start';\nconst EXTENSION_INPUT_SOURCE = 'extension';\nconst INPUT_EVENT = 'input';\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-notification';\nconst SESSION_START_EVENT = 'session_start';\n\nexport interface NotificationExtensionOptions {\n /** Replaces the worker-thread animator, which a host without worker threads cannot start. */\n titleController?: ShellTitleController;\n environment?: NodeJS.ProcessEnv;\n}\n\n/** Where the tab title is written, remembered so shutdown can restore it without an event. */\ninterface TitleSurface {\n cwd: string;\n write: WriteTitle;\n}\n\ntype WrappedUiMethod = 'confirm' | 'editor' | 'input' | 'notify' | 'select';\n\nconst ORIGINAL_UI_METHOD = Symbol.for('@agimon-ai/doompi-notification/original-ui-method');\n\n/** Capture the true host method even when another bundle copy currently owns the UI. */\nfunction captureUiMethod<Method extends WrappedUiMethod>(\n ui: ExtensionUIContext,\n method: Method,\n): ExtensionUIContext[Method] {\n let captured: unknown = ui[method];\n const seen = new Set<unknown>();\n while (typeof captured === 'function' && !seen.has(captured)) {\n seen.add(captured);\n const original = Reflect.get(captured, ORIGINAL_UI_METHOD) as unknown;\n if (typeof original !== 'function') break;\n captured = original;\n }\n return captured as ExtensionUIContext[Method];\n}\n\nfunction tagUiWrapper(wrapper: object, original: object): void {\n Reflect.defineProperty(wrapper, ORIGINAL_UI_METHOD, {\n configurable: false,\n enumerable: false,\n value: original,\n writable: false,\n });\n}\n\n/**\n * Routes UI notices and announces dialogs the agent opens on its own initiative.\n * Dialog wrappers preserve the original behavior. The notice wrapper replaces\n * host delivery while this plugin owns the UI and restores its exact identity.\n */\nfunction wrapUiNotifications(\n ui: ExtensionUIContext,\n isActive: () => boolean,\n shouldNotifyAttention: () => boolean,\n notifyAttention: (body: string) => void,\n notify: (body: string, level?: DoomNotificationLevel) => void,\n): () => void {\n const originalConfirm = captureUiMethod(ui, 'confirm');\n const wrappedConfirm: ExtensionUIContext['confirm'] = (title, message, options) => {\n if (shouldNotifyAttention()) notifyAttention(`${title}: ${message}`);\n return originalConfirm.call(ui, title, message, options);\n };\n tagUiWrapper(wrappedConfirm, originalConfirm);\n ui.confirm = wrappedConfirm;\n\n const originalSelect = captureUiMethod(ui, 'select');\n const wrappedSelect: ExtensionUIContext['select'] = (title, options, dialogOptions) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalSelect.call(ui, title, options, dialogOptions);\n };\n tagUiWrapper(wrappedSelect, originalSelect);\n ui.select = wrappedSelect;\n\n const originalInput = captureUiMethod(ui, 'input');\n const wrappedInput: ExtensionUIContext['input'] = (title, placeholder, options) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalInput.call(ui, title, placeholder, options);\n };\n tagUiWrapper(wrappedInput, originalInput);\n ui.input = wrappedInput;\n\n const originalEditor = captureUiMethod(ui, 'editor');\n const wrappedEditor: ExtensionUIContext['editor'] = (title, prefill) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalEditor.call(ui, title, prefill);\n };\n tagUiWrapper(wrappedEditor, originalEditor);\n ui.editor = wrappedEditor;\n\n const originalNotify = captureUiMethod(ui, 'notify');\n const wrappedNotify: ExtensionUIContext['notify'] = (message, level) => {\n if (isActive()) notify(message, level);\n else originalNotify.call(ui, message, level);\n };\n tagUiWrapper(wrappedNotify, originalNotify);\n ui.notify = wrappedNotify;\n\n return () => {\n if (ui.confirm === wrappedConfirm) ui.confirm = originalConfirm;\n if (ui.select === wrappedSelect) ui.select = originalSelect;\n if (ui.input === wrappedInput) ui.input = originalInput;\n if (ui.editor === wrappedEditor) ui.editor = originalEditor;\n if (ui.notify === wrappedNotify) ui.notify = originalNotify;\n };\n}\n\ninterface NotificationPluginConfig {\n readonly generation: string;\n readonly pi: ExtensionAPI;\n readonly options: NotificationExtensionOptions;\n}\n\nfunction notificationPlugin(cordis: Context, { generation, pi, options }: NotificationPluginConfig): void {\n let activeContext: ExtensionContext | undefined;\n const router = createDoomNotificationRouter({ generation, pi, context: () => activeContext });\n cordis.provide(DOOM_NOTIFICATION_SERVICE, router);\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n\n cordis.effect(function* () {\n const titles = options.titleController ?? createWorkerTitleController();\n const wrappedUis = new Map<ExtensionUIContext, () => void>();\n let active = true;\n let agentRunning = false;\n let askUserBlocked = false;\n let firstPrompt: string | undefined;\n let surface: TitleSurface | undefined;\n\n const idleTitle = (cwd: string): string =>\n shellTabTitle({ cwd, sessionName: pi.getSessionName(), prompt: firstPrompt });\n const titleSurface = (context: ExtensionContext): TitleSurface | undefined => {\n if (!active || !supportsShellTitle(context)) return undefined;\n surface = { cwd: context.cwd, write: (title) => context.ui.setTitle(title) };\n return surface;\n };\n const notifyAttention = (body: string): void => {\n if (active) void router.request(attentionNotification(body));\n };\n\n yield cordis.on(DOOM_ASK_USER_PROMPT_EVENT, (prompt) => {\n notifyAttention(askUserPromptBody(prompt.questions));\n });\n yield cordis.on(DOOM_ASK_USER_BLOCKED_EVENT, (payload) => {\n askUserBlocked = payload.active;\n });\n\n yield () => {\n if (surface) titles.dispose(idleTitle(surface.cwd), surface.write);\n surface = undefined;\n };\n yield () => {\n for (const restore of wrappedUis.values()) restore();\n wrappedUis.clear();\n };\n yield () => {\n active = false;\n agentRunning = false;\n askUserBlocked = false;\n };\n\n pi.on(INPUT_EVENT, (event, context) => {\n if (!active || firstPrompt || event.source === EXTENSION_INPUT_SOURCE) return;\n activeContext = context;\n firstPrompt = promptTitle(event.text);\n const target = firstPrompt ? titleSurface(context) : undefined;\n if (target) titles.set(idleTitle(target.cwd), target.write);\n });\n\n pi.on(SESSION_START_EVENT, (_event, context) => {\n if (!active || wrappedUis.has(context.ui)) return;\n activeContext = context;\n wrappedUis.set(\n context.ui,\n wrapUiNotifications(\n context.ui,\n () => active,\n () => active && warrantsAttentionNotification({ agentRunning, askUserBlocked }),\n notifyAttention,\n (body, level) => void router.request({ body, ...(level ? { level } : {}) }),\n ),\n );\n });\n\n pi.on(AGENT_START_EVENT, (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = true;\n const target = titleSurface(context);\n if (target) titles.start(idleTitle(target.cwd), target.write);\n });\n\n pi.on(AGENT_SETTLED_EVENT, async (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = false;\n askUserBlocked = false;\n const target = titleSurface(context);\n if (target) titles.stop(idleTitle(target.cwd), target.write);\n if (!warrantsSettledNotification(context.hasPendingMessages())) return;\n await router.request(settledNotification({ cwd: context.cwd, sessionName: pi.getSessionName() }));\n });\n }, PACKAGE_SOURCE);\n}\n\n/** The package's single standard Pi factory. */\nexport async function notificationExtension(\n pi: ExtensionAPI,\n options: NotificationExtensionOptions = {},\n): Promise<void> {\n if ((options.environment ?? process.env)[SUBAGENT_CHILD_ENV]) return;\n\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE, {\n environment: options.environment ?? process.env,\n });\n const fiber = connection.root.plugin(notificationPlugin, {\n generation: `${connection.runtime.generation}:notification`,\n pi,\n options,\n });\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\nexport default notificationExtension;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["createDoomNotificationRouter","DOOM_NOTIFICATION_SERVICE","DOOM_CORDIS_SESSION_SERVICE","createWorkerTitleController","shellTabTitle","supportsShellTitle","attentionNotification","DOOM_ASK_USER_PROMPT_EVENT","askUserPromptBody","DOOM_ASK_USER_BLOCKED_EVENT","promptTitle","warrantsAttentionNotification","warrantsSettledNotification","settledNotification","SUBAGENT_CHILD_ENV","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_ASK_USER_BLOCKED_EVENT,\n DOOM_ASK_USER_PROMPT_EVENT,\n} from '@agimon-ai/doompi-extension-contracts/ask-user';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_NOTIFICATION_SERVICE,\n type DoomNotificationLevel,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext } from '@earendil-works/pi-coding-agent';\nimport {\n askUserPromptBody,\n supportsShellTitle,\n warrantsAttentionNotification,\n warrantsSettledNotification,\n} from '../../services/notificationPolicy.ts';\nimport {\n attentionNotification,\n promptTitle,\n settledNotification,\n shellTabTitle,\n} from '../../services/notificationText.ts';\nimport type { ShellTitleController, WriteTitle } from '../../types/notifications.ts';\nimport { createDoomNotificationRouter } from '../notificationRouter.ts';\nimport { createWorkerTitleController } from '../shellTitleController.ts';\n\nconst AGENT_SETTLED_EVENT = 'agent_settled';\nconst AGENT_START_EVENT = 'agent_start';\nconst EXTENSION_INPUT_SOURCE = 'extension';\nconst INPUT_EVENT = 'input';\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-notification';\nconst SESSION_START_EVENT = 'session_start';\n\nexport interface NotificationExtensionOptions {\n /** Replaces the worker-thread animator, which a host without worker threads cannot start. */\n titleController?: ShellTitleController;\n environment?: NodeJS.ProcessEnv;\n}\n\n/** Where the tab title is written, remembered so shutdown can restore it without an event. */\ninterface TitleSurface {\n cwd: string;\n write: WriteTitle;\n}\n\ntype WrappedUiMethod = 'confirm' | 'editor' | 'input' | 'notify' | 'select';\n\nconst ORIGINAL_UI_METHOD = Symbol.for('@agimon-ai/doompi-notification/original-ui-method');\n\n/** Capture the true host method even when another bundle copy currently owns the UI. */\nfunction captureUiMethod<Method extends WrappedUiMethod>(\n ui: ExtensionUIContext,\n method: Method,\n): ExtensionUIContext[Method] {\n let captured: unknown = ui[method];\n const seen = new Set<unknown>();\n while (typeof captured === 'function' && !seen.has(captured)) {\n seen.add(captured);\n const original = Reflect.get(captured, ORIGINAL_UI_METHOD) as unknown;\n if (typeof original !== 'function') break;\n captured = original;\n }\n return captured as ExtensionUIContext[Method];\n}\n\nfunction tagUiWrapper(wrapper: object, original: object): void {\n Reflect.defineProperty(wrapper, ORIGINAL_UI_METHOD, {\n configurable: false,\n enumerable: false,\n value: original,\n writable: false,\n });\n}\n\n/**\n * Routes UI notices and announces dialogs the agent opens on its own initiative.\n * Dialog wrappers preserve the original behavior. The notice wrapper replaces\n * host delivery while this plugin owns the UI and restores its exact identity.\n */\nfunction wrapUiNotifications(\n ui: ExtensionUIContext,\n isActive: () => boolean,\n shouldNotifyAttention: () => boolean,\n notifyAttention: (body: string) => void,\n notify: (body: string, level?: DoomNotificationLevel) => void,\n): () => void {\n const originalConfirm = captureUiMethod(ui, 'confirm');\n const wrappedConfirm: ExtensionUIContext['confirm'] = (title, message, options) => {\n if (shouldNotifyAttention()) notifyAttention(`${title}: ${message}`);\n return originalConfirm.call(ui, title, message, options);\n };\n tagUiWrapper(wrappedConfirm, originalConfirm);\n ui.confirm = wrappedConfirm;\n\n const originalSelect = captureUiMethod(ui, 'select');\n const wrappedSelect: ExtensionUIContext['select'] = (title, options, dialogOptions) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalSelect.call(ui, title, options, dialogOptions);\n };\n tagUiWrapper(wrappedSelect, originalSelect);\n ui.select = wrappedSelect;\n\n const originalInput = captureUiMethod(ui, 'input');\n const wrappedInput: ExtensionUIContext['input'] = (title, placeholder, options) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalInput.call(ui, title, placeholder, options);\n };\n tagUiWrapper(wrappedInput, originalInput);\n ui.input = wrappedInput;\n\n const originalEditor = captureUiMethod(ui, 'editor');\n const wrappedEditor: ExtensionUIContext['editor'] = (title, prefill) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalEditor.call(ui, title, prefill);\n };\n tagUiWrapper(wrappedEditor, originalEditor);\n ui.editor = wrappedEditor;\n\n const originalNotify = captureUiMethod(ui, 'notify');\n const wrappedNotify: ExtensionUIContext['notify'] = (message, level) => {\n if (isActive()) notify(message, level);\n else originalNotify.call(ui, message, level);\n };\n tagUiWrapper(wrappedNotify, originalNotify);\n ui.notify = wrappedNotify;\n\n return () => {\n if (ui.confirm === wrappedConfirm) ui.confirm = originalConfirm;\n if (ui.select === wrappedSelect) ui.select = originalSelect;\n if (ui.input === wrappedInput) ui.input = originalInput;\n if (ui.editor === wrappedEditor) ui.editor = originalEditor;\n if (ui.notify === wrappedNotify) ui.notify = originalNotify;\n };\n}\n\ninterface NotificationPluginConfig {\n readonly generation: string;\n readonly pi: ExtensionAPI;\n readonly options: NotificationExtensionOptions;\n}\n\nfunction notificationPlugin(cordis: Context, { generation, pi, options }: NotificationPluginConfig): void {\n let activeContext: ExtensionContext | undefined;\n const router = createDoomNotificationRouter({ generation, pi, context: () => activeContext });\n cordis.provide(DOOM_NOTIFICATION_SERVICE, router);\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n\n cordis.effect(function* () {\n const titles = options.titleController ?? createWorkerTitleController();\n const wrappedUis = new Map<ExtensionUIContext, () => void>();\n let active = true;\n let agentRunning = false;\n let askUserBlocked = false;\n let firstPrompt: string | undefined;\n let surface: TitleSurface | undefined;\n\n const idleTitle = (cwd: string): string =>\n shellTabTitle({ cwd, sessionName: pi.getSessionName(), prompt: firstPrompt });\n const titleSurface = (context: ExtensionContext): TitleSurface | undefined => {\n if (!active || !supportsShellTitle(context)) return undefined;\n surface = { cwd: context.cwd, write: (title) => context.ui.setTitle(title) };\n return surface;\n };\n const notifyAttention = (body: string): void => {\n if (active) void router.request(attentionNotification(body));\n };\n\n yield cordis.on(DOOM_ASK_USER_PROMPT_EVENT, (prompt) => {\n notifyAttention(askUserPromptBody(prompt.questions));\n });\n yield cordis.on(DOOM_ASK_USER_BLOCKED_EVENT, (payload) => {\n askUserBlocked = payload.active;\n });\n\n yield () => {\n if (surface) titles.dispose(idleTitle(surface.cwd), surface.write);\n surface = undefined;\n };\n yield () => {\n for (const restore of wrappedUis.values()) restore();\n wrappedUis.clear();\n };\n yield () => {\n active = false;\n agentRunning = false;\n askUserBlocked = false;\n };\n\n pi.on(INPUT_EVENT, (event, context) => {\n if (!active || firstPrompt || event.source === EXTENSION_INPUT_SOURCE) return;\n activeContext = context;\n firstPrompt = promptTitle(event.text);\n const target = firstPrompt ? titleSurface(context) : undefined;\n if (target) titles.set(idleTitle(target.cwd), target.write);\n });\n\n pi.on(SESSION_START_EVENT, (_event, context) => {\n if (!active || wrappedUis.has(context.ui)) return;\n activeContext = context;\n wrappedUis.set(\n context.ui,\n wrapUiNotifications(\n context.ui,\n () => active,\n () => active && warrantsAttentionNotification({ agentRunning, askUserBlocked }),\n notifyAttention,\n (body, level) => void router.request({ body, ...(level ? { level } : {}) }),\n ),\n );\n });\n\n pi.on(AGENT_START_EVENT, (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = true;\n const target = titleSurface(context);\n if (target) titles.start(idleTitle(target.cwd), target.write);\n });\n\n pi.on(AGENT_SETTLED_EVENT, async (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = false;\n askUserBlocked = false;\n const target = titleSurface(context);\n if (target) titles.stop(idleTitle(target.cwd), target.write);\n if (!warrantsSettledNotification(context.hasPendingMessages())) return;\n await router.request(settledNotification({ cwd: context.cwd, sessionName: pi.getSessionName() }));\n });\n }, PACKAGE_SOURCE);\n}\n\n/** The package's single standard Pi factory. */\nexport async function notificationExtension(\n pi: ExtensionAPI,\n options: NotificationExtensionOptions = {},\n): Promise<void> {\n if ((options.environment ?? process.env)[SUBAGENT_CHILD_ENV]) return;\n\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE, {\n environment: options.environment ?? process.env,\n });\n const fiber = connection.root.plugin(notificationPlugin, {\n generation: `${connection.runtime.generation}:notification`,\n pi,\n options,\n });\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\nexport default notificationExtension;\n"],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ShellTitleController } from "../../types/notifications.cjs";
|
|
2
2
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
//#region src/adapters/pi/extension.d.ts
|
|
4
|
-
interface NotificationExtensionOptions {
|
|
4
|
+
export interface NotificationExtensionOptions {
|
|
5
5
|
/** Replaces the worker-thread animator, which a host without worker threads cannot start. */
|
|
6
6
|
titleController?: ShellTitleController;
|
|
7
7
|
environment?: NodeJS.ProcessEnv;
|
|
8
8
|
}
|
|
9
9
|
/** The package's single standard Pi factory. */
|
|
10
|
-
declare function notificationExtension(pi: ExtensionAPI, options?: NotificationExtensionOptions): Promise<void>;
|
|
10
|
+
export declare function notificationExtension(pi: ExtensionAPI, options?: NotificationExtensionOptions): Promise<void>;
|
|
11
11
|
//#endregion
|
|
12
|
-
export {
|
|
12
|
+
export { notificationExtension as default };
|
|
13
13
|
//# sourceMappingURL=extension.d.cts.map
|
|
@@ -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":";;;iBAuCiB;;EAEf,kBAAkB;EAClB,cAAc,OAAO;;;wBA4MD,sBACpB,IAAI,cACJ,UAAS,+BACR"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ShellTitleController } from "../../types/notifications.mjs";
|
|
2
2
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
//#region src/adapters/pi/extension.d.ts
|
|
4
|
-
interface NotificationExtensionOptions {
|
|
4
|
+
export interface NotificationExtensionOptions {
|
|
5
5
|
/** Replaces the worker-thread animator, which a host without worker threads cannot start. */
|
|
6
6
|
titleController?: ShellTitleController;
|
|
7
7
|
environment?: NodeJS.ProcessEnv;
|
|
8
8
|
}
|
|
9
9
|
/** The package's single standard Pi factory. */
|
|
10
|
-
declare function notificationExtension(pi: ExtensionAPI, options?: NotificationExtensionOptions): Promise<void>;
|
|
10
|
+
export declare function notificationExtension(pi: ExtensionAPI, options?: NotificationExtensionOptions): Promise<void>;
|
|
11
11
|
//#endregion
|
|
12
|
-
export {
|
|
12
|
+
export { notificationExtension as default };
|
|
13
13
|
//# sourceMappingURL=extension.d.mts.map
|
|
@@ -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":";;;iBAuCiB;;EAEf,kBAAkB;EAClB,cAAc,OAAO;;;wBA4MD,sBACpB,IAAI,cACJ,UAAS,+BACR"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{askUserPromptBody as e,supportsShellTitle as t,warrantsAttentionNotification as n,warrantsSettledNotification as r}from"../../services/notificationPolicy.mjs";import{attentionNotification as i,promptTitle as a,settledNotification as o,shellTabTitle as s}from"../../services/notificationText.mjs";import{createDoomNotificationRouter as c}from"../notificationRouter.mjs";import{createWorkerTitleController as l}from"../shellTitleController.mjs";import{DOOM_ASK_USER_BLOCKED_EVENT as u,DOOM_ASK_USER_PROMPT_EVENT as d}from"@agimon-ai/doompi-extension-contracts/ask-user";import{SUBAGENT_CHILD_ENV as f}from"@agimon-ai/doompi-extension-contracts/child-process";import{DOOM_CORDIS_SESSION_SERVICE as p,connectDoomCordisHost as m}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_NOTIFICATION_SERVICE as h}from"@agimon-ai/doompi-extension-contracts/notification";const g=`@agimon-ai/doompi-notification`,_=Symbol.for(`@agimon-ai/doompi-notification/original-ui-method`);function v(e,t){let n=e[t],r=new Set;for(;typeof n==`function`&&!r.has(n);){r.add(n);let e=Reflect.get(n,_);if(typeof e!=`function`)break;n=e}return n}function y(e,t){Reflect.defineProperty(e,_,{configurable:!1,enumerable:!1,value:t,writable:!1})}function b(e,t,n,r,i){let a=v(e,`confirm`),o=(t,i,o)=>(n()&&r(`${t}: ${i}`),a.call(e,t,i,o));y(o,a),e.confirm=o;let s=v(e,`select`),c=(t,i,a)=>(n()&&r(t),s.call(e,t,i,a));y(c,s),e.select=c;let l=v(e,`input`),u=(t,i,a)=>(n()&&r(t),l.call(e,t,i,a));y(u,l),e.input=u;let d=v(e,`editor`),f=(t,i)=>(n()&&r(t),d.call(e,t,i));y(f,d),e.editor=f;let p=v(e,`notify`),m=(n,r)=>{t()?i(n,r):p.call(e,n,r)};return y(m,p),e.notify=m,()=>{e.confirm===o&&(e.confirm=a),e.select===c&&(e.select=s),e.input===u&&(e.input=l),e.editor===f&&(e.editor=d),e.notify===m&&(e.notify=p)}}function x(f,{generation:m,pi:_,options:v}){let y,x=c({generation:m,pi:_,context:()=>y});f.provide(h,x),f.inject([p],e=>{let t=e.get(p).context;return y=t,()=>{y===t&&(y=void 0)}}),f.effect(function*(){let c=v.titleController??l(),p=new Map,m=!0,h=!1,g=!1,S,C,w=e=>s({cwd:e,sessionName:_.getSessionName(),prompt:S}),T=e=>{if(
|
|
1
|
+
import{askUserPromptBody as e,supportsShellTitle as t,warrantsAttentionNotification as n,warrantsSettledNotification as r}from"../../services/notificationPolicy.mjs";import{attentionNotification as i,promptTitle as a,settledNotification as o,shellTabTitle as s}from"../../services/notificationText.mjs";import{createDoomNotificationRouter as c}from"../notificationRouter.mjs";import{createWorkerTitleController as l}from"../shellTitleController.mjs";import{DOOM_ASK_USER_BLOCKED_EVENT as u,DOOM_ASK_USER_PROMPT_EVENT as d}from"@agimon-ai/doompi-extension-contracts/ask-user";import{SUBAGENT_CHILD_ENV as f}from"@agimon-ai/doompi-extension-contracts/child-process";import{DOOM_CORDIS_SESSION_SERVICE as p,connectDoomCordisHost as m}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_NOTIFICATION_SERVICE as h}from"@agimon-ai/doompi-extension-contracts/notification";const g=`@agimon-ai/doompi-notification`,_=Symbol.for(`@agimon-ai/doompi-notification/original-ui-method`);function v(e,t){let n=e[t],r=new Set;for(;typeof n==`function`&&!r.has(n);){r.add(n);let e=Reflect.get(n,_);if(typeof e!=`function`)break;n=e}return n}function y(e,t){Reflect.defineProperty(e,_,{configurable:!1,enumerable:!1,value:t,writable:!1})}function b(e,t,n,r,i){let a=v(e,`confirm`),o=(t,i,o)=>(n()&&r(`${t}: ${i}`),a.call(e,t,i,o));y(o,a),e.confirm=o;let s=v(e,`select`),c=(t,i,a)=>(n()&&r(t),s.call(e,t,i,a));y(c,s),e.select=c;let l=v(e,`input`),u=(t,i,a)=>(n()&&r(t),l.call(e,t,i,a));y(u,l),e.input=u;let d=v(e,`editor`),f=(t,i)=>(n()&&r(t),d.call(e,t,i));y(f,d),e.editor=f;let p=v(e,`notify`),m=(n,r)=>{t()?i(n,r):p.call(e,n,r)};return y(m,p),e.notify=m,()=>{e.confirm===o&&(e.confirm=a),e.select===c&&(e.select=s),e.input===u&&(e.input=l),e.editor===f&&(e.editor=d),e.notify===m&&(e.notify=p)}}function x(f,{generation:m,pi:_,options:v}){let y,x=c({generation:m,pi:_,context:()=>y});f.provide(h,x),f.inject([p],e=>{let t=e.get(p).context;return y=t,()=>{y===t&&(y=void 0)}}),f.effect(function*(){let c=v.titleController??l(),p=new Map,m=!0,h=!1,g=!1,S,C,w=e=>s({cwd:e,sessionName:_.getSessionName(),prompt:S}),T=e=>{if(m&&t(e))return C={cwd:e.cwd,write:t=>e.ui.setTitle(t)},C},E=e=>{m&&x.request(i(e))};yield f.on(d,t=>{E(e(t.questions))}),yield f.on(u,e=>{g=e.active}),yield()=>{C&&c.dispose(w(C.cwd),C.write),C=void 0},yield()=>{for(let e of p.values())e();p.clear()},yield()=>{m=!1,h=!1,g=!1},_.on(`input`,(e,t)=>{if(!m||S||e.source===`extension`)return;y=t,S=a(e.text);let n=S?T(t):void 0;n&&c.set(w(n.cwd),n.write)}),_.on(`session_start`,(e,t)=>{m&&!p.has(t.ui)&&(y=t,p.set(t.ui,b(t.ui,()=>m,()=>m&&n({agentRunning:h,askUserBlocked:g}),E,(e,t)=>void x.request({body:e,...t?{level:t}:{}}))))}),_.on(`agent_start`,(e,t)=>{if(!m)return;y=t,h=!0;let n=T(t);n&&c.start(w(n.cwd),n.write)}),_.on(`agent_settled`,async(e,t)=>{if(!m)return;y=t,h=!1,g=!1;let n=T(t);n&&c.stop(w(n.cwd),n.write),r(t.hasPendingMessages())&&await x.request(o({cwd:t.cwd,sessionName:_.getSessionName()}))})},g)}async function S(e,t={}){if((t.environment??process.env)[f])return;let n=await m(e,g,{environment:t.environment??process.env}),r=n.root.plugin(x,{generation:`${n.runtime.generation}:notification`,pi:e,options:t});try{await r}catch(e){try{await r.dispose()}finally{await n.dispose()}throw e}let i;e.on(`session_shutdown`,()=>i??=(async()=>{try{await r.dispose()}finally{await n.dispose()}})())}export{S as default,S as notificationExtension};
|
|
2
2
|
//# sourceMappingURL=extension.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_ASK_USER_BLOCKED_EVENT,\n DOOM_ASK_USER_PROMPT_EVENT,\n} from '@agimon-ai/doompi-extension-contracts/ask-user';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_NOTIFICATION_SERVICE,\n type DoomNotificationLevel,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext } from '@earendil-works/pi-coding-agent';\nimport {\n askUserPromptBody,\n supportsShellTitle,\n warrantsAttentionNotification,\n warrantsSettledNotification,\n} from '../../services/notificationPolicy.ts';\nimport {\n attentionNotification,\n promptTitle,\n settledNotification,\n shellTabTitle,\n} from '../../services/notificationText.ts';\nimport type { ShellTitleController, WriteTitle } from '../../types/notifications.ts';\nimport { createDoomNotificationRouter } from '../notificationRouter.ts';\nimport { createWorkerTitleController } from '../shellTitleController.ts';\n\nconst AGENT_SETTLED_EVENT = 'agent_settled';\nconst AGENT_START_EVENT = 'agent_start';\nconst EXTENSION_INPUT_SOURCE = 'extension';\nconst INPUT_EVENT = 'input';\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-notification';\nconst SESSION_START_EVENT = 'session_start';\n\nexport interface NotificationExtensionOptions {\n /** Replaces the worker-thread animator, which a host without worker threads cannot start. */\n titleController?: ShellTitleController;\n environment?: NodeJS.ProcessEnv;\n}\n\n/** Where the tab title is written, remembered so shutdown can restore it without an event. */\ninterface TitleSurface {\n cwd: string;\n write: WriteTitle;\n}\n\ntype WrappedUiMethod = 'confirm' | 'editor' | 'input' | 'notify' | 'select';\n\nconst ORIGINAL_UI_METHOD = Symbol.for('@agimon-ai/doompi-notification/original-ui-method');\n\n/** Capture the true host method even when another bundle copy currently owns the UI. */\nfunction captureUiMethod<Method extends WrappedUiMethod>(\n ui: ExtensionUIContext,\n method: Method,\n): ExtensionUIContext[Method] {\n let captured: unknown = ui[method];\n const seen = new Set<unknown>();\n while (typeof captured === 'function' && !seen.has(captured)) {\n seen.add(captured);\n const original = Reflect.get(captured, ORIGINAL_UI_METHOD) as unknown;\n if (typeof original !== 'function') break;\n captured = original;\n }\n return captured as ExtensionUIContext[Method];\n}\n\nfunction tagUiWrapper(wrapper: object, original: object): void {\n Reflect.defineProperty(wrapper, ORIGINAL_UI_METHOD, {\n configurable: false,\n enumerable: false,\n value: original,\n writable: false,\n });\n}\n\n/**\n * Routes UI notices and announces dialogs the agent opens on its own initiative.\n * Dialog wrappers preserve the original behavior. The notice wrapper replaces\n * host delivery while this plugin owns the UI and restores its exact identity.\n */\nfunction wrapUiNotifications(\n ui: ExtensionUIContext,\n isActive: () => boolean,\n shouldNotifyAttention: () => boolean,\n notifyAttention: (body: string) => void,\n notify: (body: string, level?: DoomNotificationLevel) => void,\n): () => void {\n const originalConfirm = captureUiMethod(ui, 'confirm');\n const wrappedConfirm: ExtensionUIContext['confirm'] = (title, message, options) => {\n if (shouldNotifyAttention()) notifyAttention(`${title}: ${message}`);\n return originalConfirm.call(ui, title, message, options);\n };\n tagUiWrapper(wrappedConfirm, originalConfirm);\n ui.confirm = wrappedConfirm;\n\n const originalSelect = captureUiMethod(ui, 'select');\n const wrappedSelect: ExtensionUIContext['select'] = (title, options, dialogOptions) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalSelect.call(ui, title, options, dialogOptions);\n };\n tagUiWrapper(wrappedSelect, originalSelect);\n ui.select = wrappedSelect;\n\n const originalInput = captureUiMethod(ui, 'input');\n const wrappedInput: ExtensionUIContext['input'] = (title, placeholder, options) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalInput.call(ui, title, placeholder, options);\n };\n tagUiWrapper(wrappedInput, originalInput);\n ui.input = wrappedInput;\n\n const originalEditor = captureUiMethod(ui, 'editor');\n const wrappedEditor: ExtensionUIContext['editor'] = (title, prefill) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalEditor.call(ui, title, prefill);\n };\n tagUiWrapper(wrappedEditor, originalEditor);\n ui.editor = wrappedEditor;\n\n const originalNotify = captureUiMethod(ui, 'notify');\n const wrappedNotify: ExtensionUIContext['notify'] = (message, level) => {\n if (isActive()) notify(message, level);\n else originalNotify.call(ui, message, level);\n };\n tagUiWrapper(wrappedNotify, originalNotify);\n ui.notify = wrappedNotify;\n\n return () => {\n if (ui.confirm === wrappedConfirm) ui.confirm = originalConfirm;\n if (ui.select === wrappedSelect) ui.select = originalSelect;\n if (ui.input === wrappedInput) ui.input = originalInput;\n if (ui.editor === wrappedEditor) ui.editor = originalEditor;\n if (ui.notify === wrappedNotify) ui.notify = originalNotify;\n };\n}\n\ninterface NotificationPluginConfig {\n readonly generation: string;\n readonly pi: ExtensionAPI;\n readonly options: NotificationExtensionOptions;\n}\n\nfunction notificationPlugin(cordis: Context, { generation, pi, options }: NotificationPluginConfig): void {\n let activeContext: ExtensionContext | undefined;\n const router = createDoomNotificationRouter({ generation, pi, context: () => activeContext });\n cordis.provide(DOOM_NOTIFICATION_SERVICE, router);\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n\n cordis.effect(function* () {\n const titles = options.titleController ?? createWorkerTitleController();\n const wrappedUis = new Map<ExtensionUIContext, () => void>();\n let active = true;\n let agentRunning = false;\n let askUserBlocked = false;\n let firstPrompt: string | undefined;\n let surface: TitleSurface | undefined;\n\n const idleTitle = (cwd: string): string =>\n shellTabTitle({ cwd, sessionName: pi.getSessionName(), prompt: firstPrompt });\n const titleSurface = (context: ExtensionContext): TitleSurface | undefined => {\n if (!active || !supportsShellTitle(context)) return undefined;\n surface = { cwd: context.cwd, write: (title) => context.ui.setTitle(title) };\n return surface;\n };\n const notifyAttention = (body: string): void => {\n if (active) void router.request(attentionNotification(body));\n };\n\n yield cordis.on(DOOM_ASK_USER_PROMPT_EVENT, (prompt) => {\n notifyAttention(askUserPromptBody(prompt.questions));\n });\n yield cordis.on(DOOM_ASK_USER_BLOCKED_EVENT, (payload) => {\n askUserBlocked = payload.active;\n });\n\n yield () => {\n if (surface) titles.dispose(idleTitle(surface.cwd), surface.write);\n surface = undefined;\n };\n yield () => {\n for (const restore of wrappedUis.values()) restore();\n wrappedUis.clear();\n };\n yield () => {\n active = false;\n agentRunning = false;\n askUserBlocked = false;\n };\n\n pi.on(INPUT_EVENT, (event, context) => {\n if (!active || firstPrompt || event.source === EXTENSION_INPUT_SOURCE) return;\n activeContext = context;\n firstPrompt = promptTitle(event.text);\n const target = firstPrompt ? titleSurface(context) : undefined;\n if (target) titles.set(idleTitle(target.cwd), target.write);\n });\n\n pi.on(SESSION_START_EVENT, (_event, context) => {\n if (!active || wrappedUis.has(context.ui)) return;\n activeContext = context;\n wrappedUis.set(\n context.ui,\n wrapUiNotifications(\n context.ui,\n () => active,\n () => active && warrantsAttentionNotification({ agentRunning, askUserBlocked }),\n notifyAttention,\n (body, level) => void router.request({ body, ...(level ? { level } : {}) }),\n ),\n );\n });\n\n pi.on(AGENT_START_EVENT, (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = true;\n const target = titleSurface(context);\n if (target) titles.start(idleTitle(target.cwd), target.write);\n });\n\n pi.on(AGENT_SETTLED_EVENT, async (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = false;\n askUserBlocked = false;\n const target = titleSurface(context);\n if (target) titles.stop(idleTitle(target.cwd), target.write);\n if (!warrantsSettledNotification(context.hasPendingMessages())) return;\n await router.request(settledNotification({ cwd: context.cwd, sessionName: pi.getSessionName() }));\n });\n }, PACKAGE_SOURCE);\n}\n\n/** The package's single standard Pi factory. */\nexport async function notificationExtension(\n pi: ExtensionAPI,\n options: NotificationExtensionOptions = {},\n): Promise<void> {\n if ((options.environment ?? process.env)[SUBAGENT_CHILD_ENV]) return;\n\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE, {\n environment: options.environment ?? process.env,\n });\n const fiber = connection.root.plugin(notificationPlugin, {\n generation: `${connection.runtime.generation}:notification`,\n pi,\n options,\n });\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\nexport default notificationExtension;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import {\n DOOM_ASK_USER_BLOCKED_EVENT,\n DOOM_ASK_USER_PROMPT_EVENT,\n} from '@agimon-ai/doompi-extension-contracts/ask-user';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_NOTIFICATION_SERVICE,\n type DoomNotificationLevel,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext } from '@earendil-works/pi-coding-agent';\nimport {\n askUserPromptBody,\n supportsShellTitle,\n warrantsAttentionNotification,\n warrantsSettledNotification,\n} from '../../services/notificationPolicy.ts';\nimport {\n attentionNotification,\n promptTitle,\n settledNotification,\n shellTabTitle,\n} from '../../services/notificationText.ts';\nimport type { ShellTitleController, WriteTitle } from '../../types/notifications.ts';\nimport { createDoomNotificationRouter } from '../notificationRouter.ts';\nimport { createWorkerTitleController } from '../shellTitleController.ts';\n\nconst AGENT_SETTLED_EVENT = 'agent_settled';\nconst AGENT_START_EVENT = 'agent_start';\nconst EXTENSION_INPUT_SOURCE = 'extension';\nconst INPUT_EVENT = 'input';\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-notification';\nconst SESSION_START_EVENT = 'session_start';\n\nexport interface NotificationExtensionOptions {\n /** Replaces the worker-thread animator, which a host without worker threads cannot start. */\n titleController?: ShellTitleController;\n environment?: NodeJS.ProcessEnv;\n}\n\n/** Where the tab title is written, remembered so shutdown can restore it without an event. */\ninterface TitleSurface {\n cwd: string;\n write: WriteTitle;\n}\n\ntype WrappedUiMethod = 'confirm' | 'editor' | 'input' | 'notify' | 'select';\n\nconst ORIGINAL_UI_METHOD = Symbol.for('@agimon-ai/doompi-notification/original-ui-method');\n\n/** Capture the true host method even when another bundle copy currently owns the UI. */\nfunction captureUiMethod<Method extends WrappedUiMethod>(\n ui: ExtensionUIContext,\n method: Method,\n): ExtensionUIContext[Method] {\n let captured: unknown = ui[method];\n const seen = new Set<unknown>();\n while (typeof captured === 'function' && !seen.has(captured)) {\n seen.add(captured);\n const original = Reflect.get(captured, ORIGINAL_UI_METHOD) as unknown;\n if (typeof original !== 'function') break;\n captured = original;\n }\n return captured as ExtensionUIContext[Method];\n}\n\nfunction tagUiWrapper(wrapper: object, original: object): void {\n Reflect.defineProperty(wrapper, ORIGINAL_UI_METHOD, {\n configurable: false,\n enumerable: false,\n value: original,\n writable: false,\n });\n}\n\n/**\n * Routes UI notices and announces dialogs the agent opens on its own initiative.\n * Dialog wrappers preserve the original behavior. The notice wrapper replaces\n * host delivery while this plugin owns the UI and restores its exact identity.\n */\nfunction wrapUiNotifications(\n ui: ExtensionUIContext,\n isActive: () => boolean,\n shouldNotifyAttention: () => boolean,\n notifyAttention: (body: string) => void,\n notify: (body: string, level?: DoomNotificationLevel) => void,\n): () => void {\n const originalConfirm = captureUiMethod(ui, 'confirm');\n const wrappedConfirm: ExtensionUIContext['confirm'] = (title, message, options) => {\n if (shouldNotifyAttention()) notifyAttention(`${title}: ${message}`);\n return originalConfirm.call(ui, title, message, options);\n };\n tagUiWrapper(wrappedConfirm, originalConfirm);\n ui.confirm = wrappedConfirm;\n\n const originalSelect = captureUiMethod(ui, 'select');\n const wrappedSelect: ExtensionUIContext['select'] = (title, options, dialogOptions) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalSelect.call(ui, title, options, dialogOptions);\n };\n tagUiWrapper(wrappedSelect, originalSelect);\n ui.select = wrappedSelect;\n\n const originalInput = captureUiMethod(ui, 'input');\n const wrappedInput: ExtensionUIContext['input'] = (title, placeholder, options) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalInput.call(ui, title, placeholder, options);\n };\n tagUiWrapper(wrappedInput, originalInput);\n ui.input = wrappedInput;\n\n const originalEditor = captureUiMethod(ui, 'editor');\n const wrappedEditor: ExtensionUIContext['editor'] = (title, prefill) => {\n if (shouldNotifyAttention()) notifyAttention(title);\n return originalEditor.call(ui, title, prefill);\n };\n tagUiWrapper(wrappedEditor, originalEditor);\n ui.editor = wrappedEditor;\n\n const originalNotify = captureUiMethod(ui, 'notify');\n const wrappedNotify: ExtensionUIContext['notify'] = (message, level) => {\n if (isActive()) notify(message, level);\n else originalNotify.call(ui, message, level);\n };\n tagUiWrapper(wrappedNotify, originalNotify);\n ui.notify = wrappedNotify;\n\n return () => {\n if (ui.confirm === wrappedConfirm) ui.confirm = originalConfirm;\n if (ui.select === wrappedSelect) ui.select = originalSelect;\n if (ui.input === wrappedInput) ui.input = originalInput;\n if (ui.editor === wrappedEditor) ui.editor = originalEditor;\n if (ui.notify === wrappedNotify) ui.notify = originalNotify;\n };\n}\n\ninterface NotificationPluginConfig {\n readonly generation: string;\n readonly pi: ExtensionAPI;\n readonly options: NotificationExtensionOptions;\n}\n\nfunction notificationPlugin(cordis: Context, { generation, pi, options }: NotificationPluginConfig): void {\n let activeContext: ExtensionContext | undefined;\n const router = createDoomNotificationRouter({ generation, pi, context: () => activeContext });\n cordis.provide(DOOM_NOTIFICATION_SERVICE, router);\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n activeContext = context;\n return () => {\n if (activeContext === context) activeContext = undefined;\n };\n });\n\n cordis.effect(function* () {\n const titles = options.titleController ?? createWorkerTitleController();\n const wrappedUis = new Map<ExtensionUIContext, () => void>();\n let active = true;\n let agentRunning = false;\n let askUserBlocked = false;\n let firstPrompt: string | undefined;\n let surface: TitleSurface | undefined;\n\n const idleTitle = (cwd: string): string =>\n shellTabTitle({ cwd, sessionName: pi.getSessionName(), prompt: firstPrompt });\n const titleSurface = (context: ExtensionContext): TitleSurface | undefined => {\n if (!active || !supportsShellTitle(context)) return undefined;\n surface = { cwd: context.cwd, write: (title) => context.ui.setTitle(title) };\n return surface;\n };\n const notifyAttention = (body: string): void => {\n if (active) void router.request(attentionNotification(body));\n };\n\n yield cordis.on(DOOM_ASK_USER_PROMPT_EVENT, (prompt) => {\n notifyAttention(askUserPromptBody(prompt.questions));\n });\n yield cordis.on(DOOM_ASK_USER_BLOCKED_EVENT, (payload) => {\n askUserBlocked = payload.active;\n });\n\n yield () => {\n if (surface) titles.dispose(idleTitle(surface.cwd), surface.write);\n surface = undefined;\n };\n yield () => {\n for (const restore of wrappedUis.values()) restore();\n wrappedUis.clear();\n };\n yield () => {\n active = false;\n agentRunning = false;\n askUserBlocked = false;\n };\n\n pi.on(INPUT_EVENT, (event, context) => {\n if (!active || firstPrompt || event.source === EXTENSION_INPUT_SOURCE) return;\n activeContext = context;\n firstPrompt = promptTitle(event.text);\n const target = firstPrompt ? titleSurface(context) : undefined;\n if (target) titles.set(idleTitle(target.cwd), target.write);\n });\n\n pi.on(SESSION_START_EVENT, (_event, context) => {\n if (!active || wrappedUis.has(context.ui)) return;\n activeContext = context;\n wrappedUis.set(\n context.ui,\n wrapUiNotifications(\n context.ui,\n () => active,\n () => active && warrantsAttentionNotification({ agentRunning, askUserBlocked }),\n notifyAttention,\n (body, level) => void router.request({ body, ...(level ? { level } : {}) }),\n ),\n );\n });\n\n pi.on(AGENT_START_EVENT, (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = true;\n const target = titleSurface(context);\n if (target) titles.start(idleTitle(target.cwd), target.write);\n });\n\n pi.on(AGENT_SETTLED_EVENT, async (_event, context) => {\n if (!active) return;\n activeContext = context;\n agentRunning = false;\n askUserBlocked = false;\n const target = titleSurface(context);\n if (target) titles.stop(idleTitle(target.cwd), target.write);\n if (!warrantsSettledNotification(context.hasPendingMessages())) return;\n await router.request(settledNotification({ cwd: context.cwd, sessionName: pi.getSessionName() }));\n });\n }, PACKAGE_SOURCE);\n}\n\n/** The package's single standard Pi factory. */\nexport async function notificationExtension(\n pi: ExtensionAPI,\n options: NotificationExtensionOptions = {},\n): Promise<void> {\n if ((options.environment ?? process.env)[SUBAGENT_CHILD_ENV]) return;\n\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE, {\n environment: options.environment ?? process.env,\n });\n const fiber = connection.root.plugin(notificationPlugin, {\n generation: `${connection.runtime.generation}:notification`,\n pi,\n options,\n });\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\nexport default notificationExtension;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
let e=require("node:worker_threads");const t=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`];function n(){let e=0,n,r=()=>{n&&clearInterval(n),n=void 0,e=0};return{set(e,t){r(),t(e)},start(i,a){r();let o=()=>{a(`${t[e%t.length]} ${i}`),e+=1};o(),n=setInterval(o,80)},stop(e,t){r(),t(e)},dispose(e,t){r(),t(e)}}}function r(){let r=n(),i,a,o,s=()=>{o=void 0
|
|
1
|
+
let e=require("node:worker_threads");const t=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`];function n(){let e=0,n,r=()=>{n&&clearInterval(n),n=void 0,e=0};return{set(e,t){r(),t(e)},start(i,a){r();let o=()=>{a(`${t[e%t.length]} ${i}`),e+=1};o(),n=setInterval(o,80)},stop(e,t){r(),t(e)},dispose(e,t){r(),t(e)}}}function r(){let r=n(),i,a,o,s=()=>{o=void 0,i&&a&&r[i.action](i.title,a)};try{let n=new e.Worker(`
|
|
2
2
|
const { writeSync } = require('node:fs');
|
|
3
3
|
const { parentPort, workerData } = require('node:worker_threads');
|
|
4
4
|
let frameIndex = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellTitleController.cjs","names":["Worker"],"sources":["../../src/adapters/shellTitleController.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { ShellTitleCommand, ShellTitleController, WriteTitle } from '../types/notifications.ts';\n\nconst BRAILLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\nconst TITLE_FRAME_INTERVAL_MS = 80;\n/**\n * The animator runs off the main thread so a busy agent turn cannot stall the\n * spinner. It is an inline eval source rather than a shipped file because the\n * package is bundled per entry, and a separate worker module would have to be\n * located on disk at runtime.\n */\nconst TITLE_WORKER_SOURCE = `\nconst { writeSync } = require('node:fs');\nconst { parentPort, workerData } = require('node:worker_threads');\nlet frameIndex = 0;\nlet timer;\nconst writeTitle = (title) => writeSync(1, '\\\\u001b]0;' + title + '\\\\u0007');\nconst stop = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n};\nparentPort.on('message', (command) => {\n stop();\n if (command.action === 'start') {\n const render = () => {\n writeTitle(workerData.frames[frameIndex % workerData.frames.length] + ' ' + command.title);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, workerData.intervalMs);\n return;\n }\n writeTitle(command.title);\n if (command.action === 'dispose') parentPort.close();\n});\n`;\n\n/** Animates on the main thread, for hosts where a worker could not be started. */\nexport function createMainThreadTitleController(): ShellTitleController {\n let frameIndex = 0;\n let timer: ReturnType<typeof setInterval> | undefined;\n const stopAnimation = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n };\n\n return {\n set(title, write) {\n stopAnimation();\n write(title);\n },\n start(title, write) {\n stopAnimation();\n const render = () => {\n write(`${BRAILLE_FRAMES[frameIndex % BRAILLE_FRAMES.length]} ${title}`);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, TITLE_FRAME_INTERVAL_MS);\n },\n stop(title, write) {\n stopAnimation();\n write(title);\n },\n dispose(title, write) {\n stopAnimation();\n write(title);\n },\n };\n}\n\n/**\n * Animates from a worker thread, replaying the last command on the main thread\n * if the worker never started, failed, or exited under us. The worker is\n * unreferenced so a pending frame never holds the process open.\n */\nexport function createWorkerTitleController(): ShellTitleController {\n const fallback = createMainThreadTitleController();\n let lastCommand: ShellTitleCommand | undefined;\n let lastWrite: WriteTitle | undefined;\n let worker: Worker | undefined;\n\n const fallBack = () => {\n worker = undefined;\n if (!lastCommand || !lastWrite) return;\n fallback[lastCommand.action](lastCommand.title, lastWrite);\n };\n\n try {\n const titleWorker = new Worker(TITLE_WORKER_SOURCE, {\n eval: true,\n workerData: { frames: BRAILLE_FRAMES, intervalMs: TITLE_FRAME_INTERVAL_MS },\n });\n titleWorker.unref();\n titleWorker.once('error', fallBack);\n titleWorker.once('exit', (code) => {\n if (code !== 0 && worker === titleWorker) fallBack();\n });\n worker = titleWorker;\n } catch {\n // Workers are unavailable on this host, which the main-thread animator covers.\n worker = undefined;\n }\n\n const dispatch = (command: ShellTitleCommand, write: WriteTitle) => {\n lastCommand = command;\n lastWrite = write;\n if (!worker) {\n fallback[command.action](command.title, write);\n return;\n }\n try {\n worker.postMessage(command);\n } catch {\n // The worker closed between the last frame and this command.\n fallBack();\n }\n };\n\n return {\n set: (title, write) => dispatch({ action: 'set', title }, write),\n start: (title, write) => dispatch({ action: 'start', title }, write),\n stop: (title, write) => dispatch({ action: 'stop', title }, write),\n dispose: (title, write) => dispatch({ action: 'dispose', title }, write),\n };\n}\n"],"mappings":"qCAGA,MAAM,EAAiB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,GAAG,EAoCxE,SAAgB,GAAwD,CACtE,IAAI,EAAa,EACb,EACE,MAAsB,CACtB,GAAO,cAAc,CAAK,EAC9B,EAAQ,IAAA,GACR,EAAa,CACf,EAEA,MAAO,CACL,IAAI,EAAO,EAAO,CAChB,EAAc,EACd,EAAM,CAAK,CACb,EACA,MAAM,EAAO,EAAO,CAClB,EAAc,EACd,IAAM,MAAe,CACnB,EAAM,GAAG,EAAe,EAAa,EAAe,QAAQ,GAAG,GAAO,EACtE,GAAc,CAChB,EACA,EAAO,EACP,EAAQ,YAAY,EAAQ,EAAuB,CACrD,EACA,KAAK,EAAO,EAAO,CACjB,EAAc,EACd,EAAM,CAAK,CACb,EACA,QAAQ,EAAO,EAAO,CACpB,EAAc,EACd,EAAM,CAAK,CACb,CACF,CACF,CAOA,SAAgB,GAAoD,CAClE,IAAM,EAAW,EAAgC,EAC7C,EACA,EACA,EAEE,MAAiB,CACrB,EAAS,IAAA,
|
|
1
|
+
{"version":3,"file":"shellTitleController.cjs","names":["Worker"],"sources":["../../src/adapters/shellTitleController.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { ShellTitleCommand, ShellTitleController, WriteTitle } from '../types/notifications.ts';\n\nconst BRAILLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\nconst TITLE_FRAME_INTERVAL_MS = 80;\n/**\n * The animator runs off the main thread so a busy agent turn cannot stall the\n * spinner. It is an inline eval source rather than a shipped file because the\n * package is bundled per entry, and a separate worker module would have to be\n * located on disk at runtime.\n */\nconst TITLE_WORKER_SOURCE = `\nconst { writeSync } = require('node:fs');\nconst { parentPort, workerData } = require('node:worker_threads');\nlet frameIndex = 0;\nlet timer;\nconst writeTitle = (title) => writeSync(1, '\\\\u001b]0;' + title + '\\\\u0007');\nconst stop = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n};\nparentPort.on('message', (command) => {\n stop();\n if (command.action === 'start') {\n const render = () => {\n writeTitle(workerData.frames[frameIndex % workerData.frames.length] + ' ' + command.title);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, workerData.intervalMs);\n return;\n }\n writeTitle(command.title);\n if (command.action === 'dispose') parentPort.close();\n});\n`;\n\n/** Animates on the main thread, for hosts where a worker could not be started. */\nexport function createMainThreadTitleController(): ShellTitleController {\n let frameIndex = 0;\n let timer: ReturnType<typeof setInterval> | undefined;\n const stopAnimation = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n };\n\n return {\n set(title, write) {\n stopAnimation();\n write(title);\n },\n start(title, write) {\n stopAnimation();\n const render = () => {\n write(`${BRAILLE_FRAMES[frameIndex % BRAILLE_FRAMES.length]} ${title}`);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, TITLE_FRAME_INTERVAL_MS);\n },\n stop(title, write) {\n stopAnimation();\n write(title);\n },\n dispose(title, write) {\n stopAnimation();\n write(title);\n },\n };\n}\n\n/**\n * Animates from a worker thread, replaying the last command on the main thread\n * if the worker never started, failed, or exited under us. The worker is\n * unreferenced so a pending frame never holds the process open.\n */\nexport function createWorkerTitleController(): ShellTitleController {\n const fallback = createMainThreadTitleController();\n let lastCommand: ShellTitleCommand | undefined;\n let lastWrite: WriteTitle | undefined;\n let worker: Worker | undefined;\n\n const fallBack = () => {\n worker = undefined;\n if (!lastCommand || !lastWrite) return;\n fallback[lastCommand.action](lastCommand.title, lastWrite);\n };\n\n try {\n const titleWorker = new Worker(TITLE_WORKER_SOURCE, {\n eval: true,\n workerData: { frames: BRAILLE_FRAMES, intervalMs: TITLE_FRAME_INTERVAL_MS },\n });\n titleWorker.unref();\n titleWorker.once('error', fallBack);\n titleWorker.once('exit', (code) => {\n if (code !== 0 && worker === titleWorker) fallBack();\n });\n worker = titleWorker;\n } catch {\n // Workers are unavailable on this host, which the main-thread animator covers.\n worker = undefined;\n }\n\n const dispatch = (command: ShellTitleCommand, write: WriteTitle) => {\n lastCommand = command;\n lastWrite = write;\n if (!worker) {\n fallback[command.action](command.title, write);\n return;\n }\n try {\n worker.postMessage(command);\n } catch {\n // The worker closed between the last frame and this command.\n fallBack();\n }\n };\n\n return {\n set: (title, write) => dispatch({ action: 'set', title }, write),\n start: (title, write) => dispatch({ action: 'start', title }, write),\n stop: (title, write) => dispatch({ action: 'stop', title }, write),\n dispose: (title, write) => dispatch({ action: 'dispose', title }, write),\n };\n}\n"],"mappings":"qCAGA,MAAM,EAAiB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,GAAG,EAoCxE,SAAgB,GAAwD,CACtE,IAAI,EAAa,EACb,EACE,MAAsB,CACtB,GAAO,cAAc,CAAK,EAC9B,EAAQ,IAAA,GACR,EAAa,CACf,EAEA,MAAO,CACL,IAAI,EAAO,EAAO,CAChB,EAAc,EACd,EAAM,CAAK,CACb,EACA,MAAM,EAAO,EAAO,CAClB,EAAc,EACd,IAAM,MAAe,CACnB,EAAM,GAAG,EAAe,EAAa,EAAe,QAAQ,GAAG,GAAO,EACtE,GAAc,CAChB,EACA,EAAO,EACP,EAAQ,YAAY,EAAQ,EAAuB,CACrD,EACA,KAAK,EAAO,EAAO,CACjB,EAAc,EACd,EAAM,CAAK,CACb,EACA,QAAQ,EAAO,EAAO,CACpB,EAAc,EACd,EAAM,CAAK,CACb,CACF,CACF,CAOA,SAAgB,GAAoD,CAClE,IAAM,EAAW,EAAgC,EAC7C,EACA,EACA,EAEE,MAAiB,CACrB,EAAS,IAAA,GACJ,GAAgB,GACrB,EAAS,EAAY,OAAO,CAAC,EAAY,MAAO,CAAS,CAC3D,EAEA,GAAI,CACF,IAAM,EAAc,IAAIA,EAAAA,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;EAAqB,CAClD,KAAM,GACN,WAAY,CAAE,OAAQ,EAAgB,WAAY,EAAwB,CAC5E,CAAC,EACD,EAAY,MAAM,EAClB,EAAY,KAAK,QAAS,CAAQ,EAClC,EAAY,KAAK,OAAS,GAAS,CAC7B,IAAS,GAAK,IAAW,GAAa,EAAS,CACrD,CAAC,EACD,EAAS,CACX,MAAQ,CAEN,EAAS,IAAA,EACX,CAEA,IAAM,GAAY,EAA4B,IAAsB,CAGlE,GAFA,EAAc,EACd,EAAY,EACR,CAAC,EAAQ,CACX,EAAS,EAAQ,OAAO,CAAC,EAAQ,MAAO,CAAK,EAC7C,MACF,CACA,GAAI,CACF,EAAO,YAAY,CAAO,CAC5B,MAAQ,CAEN,EAAS,CACX,CACF,EAEA,MAAO,CACL,KAAM,EAAO,IAAU,EAAS,CAAE,OAAQ,MAAO,OAAM,EAAG,CAAK,EAC/D,OAAQ,EAAO,IAAU,EAAS,CAAE,OAAQ,QAAS,OAAM,EAAG,CAAK,EACnE,MAAO,EAAO,IAAU,EAAS,CAAE,OAAQ,OAAQ,OAAM,EAAG,CAAK,EACjE,SAAU,EAAO,IAAU,EAAS,CAAE,OAAQ,UAAW,OAAM,EAAG,CAAK,CACzE,CACF"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { ShellTitleController } from "../types/notifications.cjs";
|
|
2
2
|
//#region src/adapters/shellTitleController.d.ts
|
|
3
3
|
/** Animates on the main thread, for hosts where a worker could not be started. */
|
|
4
|
-
declare function createMainThreadTitleController(): ShellTitleController;
|
|
4
|
+
export declare function createMainThreadTitleController(): ShellTitleController;
|
|
5
5
|
/**
|
|
6
6
|
* Animates from a worker thread, replaying the last command on the main thread
|
|
7
7
|
* if the worker never started, failed, or exited under us. The worker is
|
|
8
8
|
* unreferenced so a pending frame never holds the process open.
|
|
9
9
|
*/
|
|
10
|
-
declare function createWorkerTitleController(): ShellTitleController;
|
|
10
|
+
export declare function createWorkerTitleController(): ShellTitleController;
|
|
11
11
|
//#endregion
|
|
12
|
-
export { createMainThreadTitleController, createWorkerTitleController };
|
|
13
12
|
//# sourceMappingURL=shellTitleController.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellTitleController.d.cts","names":[],"sources":["../../src/adapters/shellTitleController.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"shellTitleController.d.cts","names":[],"sources":["../../src/adapters/shellTitleController.ts"],"mappings":";;;wBAuCgB,mCAAmC;;;;;;wBAuCnC,+BAA+B"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { ShellTitleController } from "../types/notifications.mjs";
|
|
2
2
|
//#region src/adapters/shellTitleController.d.ts
|
|
3
3
|
/** Animates on the main thread, for hosts where a worker could not be started. */
|
|
4
|
-
declare function createMainThreadTitleController(): ShellTitleController;
|
|
4
|
+
export declare function createMainThreadTitleController(): ShellTitleController;
|
|
5
5
|
/**
|
|
6
6
|
* Animates from a worker thread, replaying the last command on the main thread
|
|
7
7
|
* if the worker never started, failed, or exited under us. The worker is
|
|
8
8
|
* unreferenced so a pending frame never holds the process open.
|
|
9
9
|
*/
|
|
10
|
-
declare function createWorkerTitleController(): ShellTitleController;
|
|
10
|
+
export declare function createWorkerTitleController(): ShellTitleController;
|
|
11
11
|
//#endregion
|
|
12
|
-
export { createMainThreadTitleController, createWorkerTitleController };
|
|
13
12
|
//# sourceMappingURL=shellTitleController.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellTitleController.d.mts","names":[],"sources":["../../src/adapters/shellTitleController.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"shellTitleController.d.mts","names":[],"sources":["../../src/adapters/shellTitleController.ts"],"mappings":";;;wBAuCgB,mCAAmC;;;;;;wBAuCnC,+BAA+B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Worker as e}from"node:worker_threads";const t=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`];function n(){let e=0,n,r=()=>{n&&clearInterval(n),n=void 0,e=0};return{set(e,t){r(),t(e)},start(i,a){r();let o=()=>{a(`${t[e%t.length]} ${i}`),e+=1};o(),n=setInterval(o,80)},stop(e,t){r(),t(e)},dispose(e,t){r(),t(e)}}}function r(){let r=n(),i,a,o,s=()=>{o=void 0
|
|
1
|
+
import{Worker as e}from"node:worker_threads";const t=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`];function n(){let e=0,n,r=()=>{n&&clearInterval(n),n=void 0,e=0};return{set(e,t){r(),t(e)},start(i,a){r();let o=()=>{a(`${t[e%t.length]} ${i}`),e+=1};o(),n=setInterval(o,80)},stop(e,t){r(),t(e)},dispose(e,t){r(),t(e)}}}function r(){let r=n(),i,a,o,s=()=>{o=void 0,i&&a&&r[i.action](i.title,a)};try{let n=new e(`
|
|
2
2
|
const { writeSync } = require('node:fs');
|
|
3
3
|
const { parentPort, workerData } = require('node:worker_threads');
|
|
4
4
|
let frameIndex = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellTitleController.mjs","names":[],"sources":["../../src/adapters/shellTitleController.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { ShellTitleCommand, ShellTitleController, WriteTitle } from '../types/notifications.ts';\n\nconst BRAILLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\nconst TITLE_FRAME_INTERVAL_MS = 80;\n/**\n * The animator runs off the main thread so a busy agent turn cannot stall the\n * spinner. It is an inline eval source rather than a shipped file because the\n * package is bundled per entry, and a separate worker module would have to be\n * located on disk at runtime.\n */\nconst TITLE_WORKER_SOURCE = `\nconst { writeSync } = require('node:fs');\nconst { parentPort, workerData } = require('node:worker_threads');\nlet frameIndex = 0;\nlet timer;\nconst writeTitle = (title) => writeSync(1, '\\\\u001b]0;' + title + '\\\\u0007');\nconst stop = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n};\nparentPort.on('message', (command) => {\n stop();\n if (command.action === 'start') {\n const render = () => {\n writeTitle(workerData.frames[frameIndex % workerData.frames.length] + ' ' + command.title);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, workerData.intervalMs);\n return;\n }\n writeTitle(command.title);\n if (command.action === 'dispose') parentPort.close();\n});\n`;\n\n/** Animates on the main thread, for hosts where a worker could not be started. */\nexport function createMainThreadTitleController(): ShellTitleController {\n let frameIndex = 0;\n let timer: ReturnType<typeof setInterval> | undefined;\n const stopAnimation = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n };\n\n return {\n set(title, write) {\n stopAnimation();\n write(title);\n },\n start(title, write) {\n stopAnimation();\n const render = () => {\n write(`${BRAILLE_FRAMES[frameIndex % BRAILLE_FRAMES.length]} ${title}`);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, TITLE_FRAME_INTERVAL_MS);\n },\n stop(title, write) {\n stopAnimation();\n write(title);\n },\n dispose(title, write) {\n stopAnimation();\n write(title);\n },\n };\n}\n\n/**\n * Animates from a worker thread, replaying the last command on the main thread\n * if the worker never started, failed, or exited under us. The worker is\n * unreferenced so a pending frame never holds the process open.\n */\nexport function createWorkerTitleController(): ShellTitleController {\n const fallback = createMainThreadTitleController();\n let lastCommand: ShellTitleCommand | undefined;\n let lastWrite: WriteTitle | undefined;\n let worker: Worker | undefined;\n\n const fallBack = () => {\n worker = undefined;\n if (!lastCommand || !lastWrite) return;\n fallback[lastCommand.action](lastCommand.title, lastWrite);\n };\n\n try {\n const titleWorker = new Worker(TITLE_WORKER_SOURCE, {\n eval: true,\n workerData: { frames: BRAILLE_FRAMES, intervalMs: TITLE_FRAME_INTERVAL_MS },\n });\n titleWorker.unref();\n titleWorker.once('error', fallBack);\n titleWorker.once('exit', (code) => {\n if (code !== 0 && worker === titleWorker) fallBack();\n });\n worker = titleWorker;\n } catch {\n // Workers are unavailable on this host, which the main-thread animator covers.\n worker = undefined;\n }\n\n const dispatch = (command: ShellTitleCommand, write: WriteTitle) => {\n lastCommand = command;\n lastWrite = write;\n if (!worker) {\n fallback[command.action](command.title, write);\n return;\n }\n try {\n worker.postMessage(command);\n } catch {\n // The worker closed between the last frame and this command.\n fallBack();\n }\n };\n\n return {\n set: (title, write) => dispatch({ action: 'set', title }, write),\n start: (title, write) => dispatch({ action: 'start', title }, write),\n stop: (title, write) => dispatch({ action: 'stop', title }, write),\n dispose: (title, write) => dispatch({ action: 'dispose', title }, write),\n };\n}\n"],"mappings":"6CAGA,MAAM,EAAiB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,GAAG,EAoCxE,SAAgB,GAAwD,CACtE,IAAI,EAAa,EACb,EACE,MAAsB,CACtB,GAAO,cAAc,CAAK,EAC9B,EAAQ,IAAA,GACR,EAAa,CACf,EAEA,MAAO,CACL,IAAI,EAAO,EAAO,CAChB,EAAc,EACd,EAAM,CAAK,CACb,EACA,MAAM,EAAO,EAAO,CAClB,EAAc,EACd,IAAM,MAAe,CACnB,EAAM,GAAG,EAAe,EAAa,EAAe,QAAQ,GAAG,GAAO,EACtE,GAAc,CAChB,EACA,EAAO,EACP,EAAQ,YAAY,EAAQ,EAAuB,CACrD,EACA,KAAK,EAAO,EAAO,CACjB,EAAc,EACd,EAAM,CAAK,CACb,EACA,QAAQ,EAAO,EAAO,CACpB,EAAc,EACd,EAAM,CAAK,CACb,CACF,CACF,CAOA,SAAgB,GAAoD,CAClE,IAAM,EAAW,EAAgC,EAC7C,EACA,EACA,EAEE,MAAiB,CACrB,EAAS,IAAA,
|
|
1
|
+
{"version":3,"file":"shellTitleController.mjs","names":[],"sources":["../../src/adapters/shellTitleController.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { ShellTitleCommand, ShellTitleController, WriteTitle } from '../types/notifications.ts';\n\nconst BRAILLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\nconst TITLE_FRAME_INTERVAL_MS = 80;\n/**\n * The animator runs off the main thread so a busy agent turn cannot stall the\n * spinner. It is an inline eval source rather than a shipped file because the\n * package is bundled per entry, and a separate worker module would have to be\n * located on disk at runtime.\n */\nconst TITLE_WORKER_SOURCE = `\nconst { writeSync } = require('node:fs');\nconst { parentPort, workerData } = require('node:worker_threads');\nlet frameIndex = 0;\nlet timer;\nconst writeTitle = (title) => writeSync(1, '\\\\u001b]0;' + title + '\\\\u0007');\nconst stop = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n};\nparentPort.on('message', (command) => {\n stop();\n if (command.action === 'start') {\n const render = () => {\n writeTitle(workerData.frames[frameIndex % workerData.frames.length] + ' ' + command.title);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, workerData.intervalMs);\n return;\n }\n writeTitle(command.title);\n if (command.action === 'dispose') parentPort.close();\n});\n`;\n\n/** Animates on the main thread, for hosts where a worker could not be started. */\nexport function createMainThreadTitleController(): ShellTitleController {\n let frameIndex = 0;\n let timer: ReturnType<typeof setInterval> | undefined;\n const stopAnimation = () => {\n if (timer) clearInterval(timer);\n timer = undefined;\n frameIndex = 0;\n };\n\n return {\n set(title, write) {\n stopAnimation();\n write(title);\n },\n start(title, write) {\n stopAnimation();\n const render = () => {\n write(`${BRAILLE_FRAMES[frameIndex % BRAILLE_FRAMES.length]} ${title}`);\n frameIndex += 1;\n };\n render();\n timer = setInterval(render, TITLE_FRAME_INTERVAL_MS);\n },\n stop(title, write) {\n stopAnimation();\n write(title);\n },\n dispose(title, write) {\n stopAnimation();\n write(title);\n },\n };\n}\n\n/**\n * Animates from a worker thread, replaying the last command on the main thread\n * if the worker never started, failed, or exited under us. The worker is\n * unreferenced so a pending frame never holds the process open.\n */\nexport function createWorkerTitleController(): ShellTitleController {\n const fallback = createMainThreadTitleController();\n let lastCommand: ShellTitleCommand | undefined;\n let lastWrite: WriteTitle | undefined;\n let worker: Worker | undefined;\n\n const fallBack = () => {\n worker = undefined;\n if (!lastCommand || !lastWrite) return;\n fallback[lastCommand.action](lastCommand.title, lastWrite);\n };\n\n try {\n const titleWorker = new Worker(TITLE_WORKER_SOURCE, {\n eval: true,\n workerData: { frames: BRAILLE_FRAMES, intervalMs: TITLE_FRAME_INTERVAL_MS },\n });\n titleWorker.unref();\n titleWorker.once('error', fallBack);\n titleWorker.once('exit', (code) => {\n if (code !== 0 && worker === titleWorker) fallBack();\n });\n worker = titleWorker;\n } catch {\n // Workers are unavailable on this host, which the main-thread animator covers.\n worker = undefined;\n }\n\n const dispatch = (command: ShellTitleCommand, write: WriteTitle) => {\n lastCommand = command;\n lastWrite = write;\n if (!worker) {\n fallback[command.action](command.title, write);\n return;\n }\n try {\n worker.postMessage(command);\n } catch {\n // The worker closed between the last frame and this command.\n fallBack();\n }\n };\n\n return {\n set: (title, write) => dispatch({ action: 'set', title }, write),\n start: (title, write) => dispatch({ action: 'start', title }, write),\n stop: (title, write) => dispatch({ action: 'stop', title }, write),\n dispose: (title, write) => dispatch({ action: 'dispose', title }, write),\n };\n}\n"],"mappings":"6CAGA,MAAM,EAAiB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,GAAG,EAoCxE,SAAgB,GAAwD,CACtE,IAAI,EAAa,EACb,EACE,MAAsB,CACtB,GAAO,cAAc,CAAK,EAC9B,EAAQ,IAAA,GACR,EAAa,CACf,EAEA,MAAO,CACL,IAAI,EAAO,EAAO,CAChB,EAAc,EACd,EAAM,CAAK,CACb,EACA,MAAM,EAAO,EAAO,CAClB,EAAc,EACd,IAAM,MAAe,CACnB,EAAM,GAAG,EAAe,EAAa,EAAe,QAAQ,GAAG,GAAO,EACtE,GAAc,CAChB,EACA,EAAO,EACP,EAAQ,YAAY,EAAQ,EAAuB,CACrD,EACA,KAAK,EAAO,EAAO,CACjB,EAAc,EACd,EAAM,CAAK,CACb,EACA,QAAQ,EAAO,EAAO,CACpB,EAAc,EACd,EAAM,CAAK,CACb,CACF,CACF,CAOA,SAAgB,GAAoD,CAClE,IAAM,EAAW,EAAgC,EAC7C,EACA,EACA,EAEE,MAAiB,CACrB,EAAS,IAAA,GACJ,GAAgB,GACrB,EAAS,EAAY,OAAO,CAAC,EAAY,MAAO,CAAS,CAC3D,EAEA,GAAI,CACF,IAAM,EAAc,IAAI,EAAO;;;;;;;;;;;;;;;;;;;;;;;;;EAAqB,CAClD,KAAM,GACN,WAAY,CAAE,OAAQ,EAAgB,WAAY,EAAwB,CAC5E,CAAC,EACD,EAAY,MAAM,EAClB,EAAY,KAAK,QAAS,CAAQ,EAClC,EAAY,KAAK,OAAS,GAAS,CAC7B,IAAS,GAAK,IAAW,GAAa,EAAS,CACrD,CAAC,EACD,EAAS,CACX,MAAQ,CAEN,EAAS,IAAA,EACX,CAEA,IAAM,GAAY,EAA4B,IAAsB,CAGlE,GAFA,EAAc,EACd,EAAY,EACR,CAAC,EAAQ,CACX,EAAS,EAAQ,OAAO,CAAC,EAAQ,MAAO,CAAK,EAC7C,MACF,CACA,GAAI,CACF,EAAO,YAAY,CAAO,CAC5B,MAAQ,CAEN,EAAS,CACX,CACF,EAEA,MAAO,CACL,KAAM,EAAO,IAAU,EAAS,CAAE,OAAQ,MAAO,OAAM,EAAG,CAAK,EAC/D,OAAQ,EAAO,IAAU,EAAS,CAAE,OAAQ,QAAS,OAAM,EAAG,CAAK,EACnE,MAAO,EAAO,IAAU,EAAS,CAAE,OAAQ,OAAQ,OAAM,EAAG,CAAK,EACjE,SAAU,EAAO,IAAU,EAAS,CAAE,OAAQ,UAAW,OAAM,EAAG,CAAK,CACzE,CACF"}
|
|
@@ -8,7 +8,6 @@ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
8
8
|
* session actually lives in. Only macOS has a second option worth attempting,
|
|
9
9
|
* and a host with neither stays silent rather than failing the turn.
|
|
10
10
|
*/
|
|
11
|
-
declare function sendSystemNotification(pi: ExtensionAPI, notification: DesktopNotification, platform?: string): Promise<void>;
|
|
11
|
+
export declare function sendSystemNotification(pi: ExtensionAPI, notification: DesktopNotification, platform?: string): Promise<void>;
|
|
12
12
|
//#endregion
|
|
13
|
-
export { sendSystemNotification };
|
|
14
13
|
//# sourceMappingURL=systemNotification.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"systemNotification.d.cts","names":[],"sources":["../../src/adapters/systemNotification.ts"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"systemNotification.d.cts","names":[],"sources":["../../src/adapters/systemNotification.ts"],"mappings":";;;;;;;;;;wBA4BsB,uBACpB,IAAI,cACJ,cAAc,qBACd,oBACC"}
|
|
@@ -8,7 +8,6 @@ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
8
8
|
* session actually lives in. Only macOS has a second option worth attempting,
|
|
9
9
|
* and a host with neither stays silent rather than failing the turn.
|
|
10
10
|
*/
|
|
11
|
-
declare function sendSystemNotification(pi: ExtensionAPI, notification: DesktopNotification, platform?: string): Promise<void>;
|
|
11
|
+
export declare function sendSystemNotification(pi: ExtensionAPI, notification: DesktopNotification, platform?: string): Promise<void>;
|
|
12
12
|
//#endregion
|
|
13
|
-
export { sendSystemNotification };
|
|
14
13
|
//# sourceMappingURL=systemNotification.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"systemNotification.d.mts","names":[],"sources":["../../src/adapters/systemNotification.ts"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"systemNotification.d.mts","names":[],"sources":["../../src/adapters/systemNotification.ts"],"mappings":";;;;;;;;;;wBA4BsB,uBACpB,IAAI,cACJ,cAAc,qBACd,oBACC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
//#region src/services/notificationPolicy.d.ts
|
|
2
|
-
interface AttentionState {
|
|
2
|
+
export interface AttentionState {
|
|
3
3
|
agentRunning: boolean;
|
|
4
4
|
askUserBlocked: boolean;
|
|
5
5
|
}
|
|
6
|
-
interface ShellSurface {
|
|
6
|
+
export interface ShellSurface {
|
|
7
7
|
hasUI: boolean;
|
|
8
8
|
mode?: string;
|
|
9
9
|
}
|
|
@@ -14,15 +14,14 @@ interface ShellSurface {
|
|
|
14
14
|
* it. An ask-user prompt is announced by its own event, so the dialog it then
|
|
15
15
|
* opens must stay silent or the same question notifies twice.
|
|
16
16
|
*/
|
|
17
|
-
declare function warrantsAttentionNotification({ agentRunning, askUserBlocked }: AttentionState): boolean;
|
|
17
|
+
export declare function warrantsAttentionNotification({ agentRunning, askUserBlocked }: AttentionState): boolean;
|
|
18
18
|
/** A queued follow-up means the agent keeps working, so the run has not stopped for the user. */
|
|
19
|
-
declare function warrantsSettledNotification(hasPendingMessages: boolean): boolean;
|
|
19
|
+
export declare function warrantsSettledNotification(hasPendingMessages: boolean): boolean;
|
|
20
20
|
/** Terminal title escapes only mean something on an attached terminal; RPC output stays clean. */
|
|
21
|
-
declare function supportsShellTitle({ hasUI, mode }: ShellSurface): boolean;
|
|
21
|
+
export declare function supportsShellTitle({ hasUI, mode }: ShellSurface): boolean;
|
|
22
22
|
/** The question an ask-user prompt is blocking on, or a stand-in when it asked none. */
|
|
23
|
-
declare function askUserPromptBody(questions: readonly {
|
|
23
|
+
export declare function askUserPromptBody(questions: readonly {
|
|
24
24
|
question: string;
|
|
25
25
|
}[]): string;
|
|
26
26
|
//#endregion
|
|
27
|
-
export { AttentionState, ShellSurface, askUserPromptBody, supportsShellTitle, warrantsAttentionNotification, warrantsSettledNotification };
|
|
28
27
|
//# sourceMappingURL=notificationPolicy.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notificationPolicy.d.cts","names":[],"sources":["../../src/services/notificationPolicy.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"notificationPolicy.d.cts","names":[],"sources":["../../src/services/notificationPolicy.ts"],"mappings":";iBAGiB;EACf;EACA;;iBAGe;EACf;EACA;;;;;;;;;wBAUc,gCAAgC,cAAc,kBAAkB;;wBAKhE,4BAA4B;;wBAK5B,qBAAqB,OAAO,QAAQ;;wBAKpC,kBAAkB;EAAsB"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
//#region src/services/notificationPolicy.d.ts
|
|
2
|
-
interface AttentionState {
|
|
2
|
+
export interface AttentionState {
|
|
3
3
|
agentRunning: boolean;
|
|
4
4
|
askUserBlocked: boolean;
|
|
5
5
|
}
|
|
6
|
-
interface ShellSurface {
|
|
6
|
+
export interface ShellSurface {
|
|
7
7
|
hasUI: boolean;
|
|
8
8
|
mode?: string;
|
|
9
9
|
}
|
|
@@ -14,15 +14,14 @@ interface ShellSurface {
|
|
|
14
14
|
* it. An ask-user prompt is announced by its own event, so the dialog it then
|
|
15
15
|
* opens must stay silent or the same question notifies twice.
|
|
16
16
|
*/
|
|
17
|
-
declare function warrantsAttentionNotification({ agentRunning, askUserBlocked }: AttentionState): boolean;
|
|
17
|
+
export declare function warrantsAttentionNotification({ agentRunning, askUserBlocked }: AttentionState): boolean;
|
|
18
18
|
/** A queued follow-up means the agent keeps working, so the run has not stopped for the user. */
|
|
19
|
-
declare function warrantsSettledNotification(hasPendingMessages: boolean): boolean;
|
|
19
|
+
export declare function warrantsSettledNotification(hasPendingMessages: boolean): boolean;
|
|
20
20
|
/** Terminal title escapes only mean something on an attached terminal; RPC output stays clean. */
|
|
21
|
-
declare function supportsShellTitle({ hasUI, mode }: ShellSurface): boolean;
|
|
21
|
+
export declare function supportsShellTitle({ hasUI, mode }: ShellSurface): boolean;
|
|
22
22
|
/** The question an ask-user prompt is blocking on, or a stand-in when it asked none. */
|
|
23
|
-
declare function askUserPromptBody(questions: readonly {
|
|
23
|
+
export declare function askUserPromptBody(questions: readonly {
|
|
24
24
|
question: string;
|
|
25
25
|
}[]): string;
|
|
26
26
|
//#endregion
|
|
27
|
-
export { AttentionState, ShellSurface, askUserPromptBody, supportsShellTitle, warrantsAttentionNotification, warrantsSettledNotification };
|
|
28
27
|
//# sourceMappingURL=notificationPolicy.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notificationPolicy.d.mts","names":[],"sources":["../../src/services/notificationPolicy.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"notificationPolicy.d.mts","names":[],"sources":["../../src/services/notificationPolicy.ts"],"mappings":";iBAGiB;EACf;EACA;;iBAGe;EACf;EACA;;;;;;;;;wBAUc,gCAAgC,cAAc,kBAAkB;;wBAKhE,4BAA4B;;wBAK5B,qBAAqB,OAAO,QAAQ;;wBAKpC,kBAAkB;EAAsB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DesktopNotification } from "../types/notifications.cjs";
|
|
2
2
|
//#region src/services/notificationText.d.ts
|
|
3
3
|
/** The first user prompt, shortened to fit a shell tab. A blank prompt names nothing. */
|
|
4
|
-
declare function promptTitle(prompt: string): string | undefined;
|
|
5
|
-
interface ShellTabTitleInput {
|
|
4
|
+
export declare function promptTitle(prompt: string): string | undefined;
|
|
5
|
+
export interface ShellTabTitleInput {
|
|
6
6
|
cwd: string;
|
|
7
7
|
sessionName?: string;
|
|
8
8
|
prompt?: string;
|
|
@@ -13,16 +13,15 @@ interface ShellTabTitleInput {
|
|
|
13
13
|
* The session name wins over the first prompt because the user chose it. With
|
|
14
14
|
* neither, the repository alone still tells two tabs apart.
|
|
15
15
|
*/
|
|
16
|
-
declare function shellTabTitle({ cwd, sessionName, prompt }: ShellTabTitleInput): string;
|
|
16
|
+
export declare function shellTabTitle({ cwd, sessionName, prompt }: ShellTabTitleInput): string;
|
|
17
17
|
/** Notification bodies are one line in a system panel, so they collapse and truncate. */
|
|
18
|
-
declare function notificationBody(body: string): string;
|
|
18
|
+
export declare function notificationBody(body: string): string;
|
|
19
19
|
/** Sent while the agent holds the turn and something is blocking on the user. */
|
|
20
|
-
declare function attentionNotification(body: string): DesktopNotification;
|
|
20
|
+
export declare function attentionNotification(body: string): DesktopNotification;
|
|
21
21
|
/** Sent once a run has stopped, subtitled with whichever name the user recognises. */
|
|
22
|
-
declare function settledNotification({ cwd, sessionName }: {
|
|
22
|
+
export declare function settledNotification({ cwd, sessionName }: {
|
|
23
23
|
cwd: string;
|
|
24
24
|
sessionName?: string;
|
|
25
25
|
}): DesktopNotification;
|
|
26
26
|
//#endregion
|
|
27
|
-
export { ShellTabTitleInput, attentionNotification, notificationBody, promptTitle, settledNotification, shellTabTitle };
|
|
28
27
|
//# sourceMappingURL=notificationText.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notificationText.d.cts","names":[],"sources":["../../src/services/notificationText.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"notificationText.d.cts","names":[],"sources":["../../src/services/notificationText.ts"],"mappings":";;;wBAsBgB,YAAY;iBAKX;EACf;EACA;EACA;;;;;;;;wBASc,gBAAgB,KAAK,aAAa,UAAU;;wBAS5C,iBAAiB;;wBAKjB,sBAAsB,eAAe;;wBAKrC,sBAAsB,KAAK;EAAiB;EAAa;IAAyB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DesktopNotification } from "../types/notifications.mjs";
|
|
2
2
|
//#region src/services/notificationText.d.ts
|
|
3
3
|
/** The first user prompt, shortened to fit a shell tab. A blank prompt names nothing. */
|
|
4
|
-
declare function promptTitle(prompt: string): string | undefined;
|
|
5
|
-
interface ShellTabTitleInput {
|
|
4
|
+
export declare function promptTitle(prompt: string): string | undefined;
|
|
5
|
+
export interface ShellTabTitleInput {
|
|
6
6
|
cwd: string;
|
|
7
7
|
sessionName?: string;
|
|
8
8
|
prompt?: string;
|
|
@@ -13,16 +13,15 @@ interface ShellTabTitleInput {
|
|
|
13
13
|
* The session name wins over the first prompt because the user chose it. With
|
|
14
14
|
* neither, the repository alone still tells two tabs apart.
|
|
15
15
|
*/
|
|
16
|
-
declare function shellTabTitle({ cwd, sessionName, prompt }: ShellTabTitleInput): string;
|
|
16
|
+
export declare function shellTabTitle({ cwd, sessionName, prompt }: ShellTabTitleInput): string;
|
|
17
17
|
/** Notification bodies are one line in a system panel, so they collapse and truncate. */
|
|
18
|
-
declare function notificationBody(body: string): string;
|
|
18
|
+
export declare function notificationBody(body: string): string;
|
|
19
19
|
/** Sent while the agent holds the turn and something is blocking on the user. */
|
|
20
|
-
declare function attentionNotification(body: string): DesktopNotification;
|
|
20
|
+
export declare function attentionNotification(body: string): DesktopNotification;
|
|
21
21
|
/** Sent once a run has stopped, subtitled with whichever name the user recognises. */
|
|
22
|
-
declare function settledNotification({ cwd, sessionName }: {
|
|
22
|
+
export declare function settledNotification({ cwd, sessionName }: {
|
|
23
23
|
cwd: string;
|
|
24
24
|
sessionName?: string;
|
|
25
25
|
}): DesktopNotification;
|
|
26
26
|
//#endregion
|
|
27
|
-
export { ShellTabTitleInput, attentionNotification, notificationBody, promptTitle, settledNotification, shellTabTitle };
|
|
28
27
|
//# sourceMappingURL=notificationText.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notificationText.d.mts","names":[],"sources":["../../src/services/notificationText.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"notificationText.d.mts","names":[],"sources":["../../src/services/notificationText.ts"],"mappings":";;;wBAsBgB,YAAY;iBAKX;EACf;EACA;EACA;;;;;;;;wBASc,gBAAgB,KAAK,aAAa,UAAU;;wBAS5C,iBAAiB;;wBAKjB,sBAAsB,eAAe;;wBAKrC,sBAAsB,KAAK;EAAiB;EAAa;IAAyB"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
//#region src/types/notifications.d.ts
|
|
2
2
|
/** Writes a shell-tab title to whatever terminal surface the host owns. */
|
|
3
|
-
type WriteTitle = (title: string) => void;
|
|
3
|
+
export type WriteTitle = (title: string) => void;
|
|
4
4
|
/** The four things a title controller can be asked to do, and its method names. */
|
|
5
|
-
type ShellTitleAction = 'dispose' | 'set' | 'start' | 'stop';
|
|
5
|
+
export type ShellTitleAction = 'dispose' | 'set' | 'start' | 'stop';
|
|
6
6
|
/** One instruction for a title controller, and the message shape its worker consumes. */
|
|
7
|
-
interface ShellTitleCommand {
|
|
7
|
+
export interface ShellTitleCommand {
|
|
8
8
|
action: ShellTitleAction;
|
|
9
9
|
title: string;
|
|
10
10
|
}
|
|
@@ -16,18 +16,17 @@ interface ShellTitleCommand {
|
|
|
16
16
|
* title and releases whatever machinery the implementation owns. Every method
|
|
17
17
|
* takes the writer so the controller never has to hold a session reference.
|
|
18
18
|
*/
|
|
19
|
-
interface ShellTitleController {
|
|
19
|
+
export interface ShellTitleController {
|
|
20
20
|
set(title: string, write: WriteTitle): void;
|
|
21
21
|
start(title: string, write: WriteTitle): void;
|
|
22
22
|
stop(title: string, write: WriteTitle): void;
|
|
23
23
|
dispose(title: string, write: WriteTitle): void;
|
|
24
24
|
}
|
|
25
25
|
/** One OS-level desktop notification. */
|
|
26
|
-
interface DesktopNotification {
|
|
26
|
+
export interface DesktopNotification {
|
|
27
27
|
title: string;
|
|
28
28
|
subtitle: string;
|
|
29
29
|
body: string;
|
|
30
30
|
}
|
|
31
31
|
//#endregion
|
|
32
|
-
export { DesktopNotification, ShellTitleAction, ShellTitleCommand, ShellTitleController, WriteTitle };
|
|
33
32
|
//# sourceMappingURL=notifications.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.cts","names":[],"sources":["../../src/types/notifications.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"notifications.d.cts","names":[],"sources":["../../src/types/notifications.ts"],"mappings":";;YACY,cAAc;;YAGd;;iBAGK;EACf,QAAQ;EACR;;;;;;;;;;iBAWe;EACf,IAAI,eAAe,OAAO;EAC1B,MAAM,eAAe,OAAO;EAC5B,KAAK,eAAe,OAAO;EAC3B,QAAQ,eAAe,OAAO;;;iBAIf;EACf;EACA;EACA"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
//#region src/types/notifications.d.ts
|
|
2
2
|
/** Writes a shell-tab title to whatever terminal surface the host owns. */
|
|
3
|
-
type WriteTitle = (title: string) => void;
|
|
3
|
+
export type WriteTitle = (title: string) => void;
|
|
4
4
|
/** The four things a title controller can be asked to do, and its method names. */
|
|
5
|
-
type ShellTitleAction = 'dispose' | 'set' | 'start' | 'stop';
|
|
5
|
+
export type ShellTitleAction = 'dispose' | 'set' | 'start' | 'stop';
|
|
6
6
|
/** One instruction for a title controller, and the message shape its worker consumes. */
|
|
7
|
-
interface ShellTitleCommand {
|
|
7
|
+
export interface ShellTitleCommand {
|
|
8
8
|
action: ShellTitleAction;
|
|
9
9
|
title: string;
|
|
10
10
|
}
|
|
@@ -16,18 +16,17 @@ interface ShellTitleCommand {
|
|
|
16
16
|
* title and releases whatever machinery the implementation owns. Every method
|
|
17
17
|
* takes the writer so the controller never has to hold a session reference.
|
|
18
18
|
*/
|
|
19
|
-
interface ShellTitleController {
|
|
19
|
+
export interface ShellTitleController {
|
|
20
20
|
set(title: string, write: WriteTitle): void;
|
|
21
21
|
start(title: string, write: WriteTitle): void;
|
|
22
22
|
stop(title: string, write: WriteTitle): void;
|
|
23
23
|
dispose(title: string, write: WriteTitle): void;
|
|
24
24
|
}
|
|
25
25
|
/** One OS-level desktop notification. */
|
|
26
|
-
interface DesktopNotification {
|
|
26
|
+
export interface DesktopNotification {
|
|
27
27
|
title: string;
|
|
28
28
|
subtitle: string;
|
|
29
29
|
body: string;
|
|
30
30
|
}
|
|
31
31
|
//#endregion
|
|
32
|
-
export { DesktopNotification, ShellTitleAction, ShellTitleCommand, ShellTitleController, WriteTitle };
|
|
33
32
|
//# sourceMappingURL=notifications.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.mts","names":[],"sources":["../../src/types/notifications.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"notifications.d.mts","names":[],"sources":["../../src/types/notifications.ts"],"mappings":";;YACY,cAAc;;YAGd;;iBAGK;EACf,QAAQ;EACR;;;;;;;;;;iBAWe;EACf,IAAI,eAAe,OAAO;EAC1B,MAAM,eAAe,OAAO;EAC5B,KAAK,eAAe,OAAO;EAC3B,QAAQ,eAAe,OAAO;;;iBAIf;EACf;EACA;EACA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-notification",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.41",
|
|
4
4
|
"description": "Desktop notifications and an animated shell-tab title for DoomPi sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -54,20 +54,20 @@
|
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@deepseek-ai/cordis": "4.0.
|
|
58
|
-
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.
|
|
57
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
58
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.63"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@earendil-works/pi-coding-agent": "0.85.
|
|
62
|
-
"@types/node": "26.4.
|
|
63
|
-
"@vitest/coverage-v8": "
|
|
64
|
-
"tsdown": "0.
|
|
61
|
+
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
62
|
+
"@types/node": "26.4.1",
|
|
63
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
64
|
+
"tsdown": "0.23.0",
|
|
65
65
|
"typescript": "7.0.2",
|
|
66
|
-
"vitest": "
|
|
67
|
-
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.
|
|
66
|
+
"vitest": "5.0.0",
|
|
67
|
+
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.60"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@earendil-works/pi-coding-agent": "0.85.
|
|
70
|
+
"@earendil-works/pi-coding-agent": "0.85.1"
|
|
71
71
|
},
|
|
72
72
|
"peerDependenciesMeta": {
|
|
73
73
|
"@earendil-works/pi-coding-agent": {
|