@agimon-ai/doompi-notification 0.0.1-alpha.2 → 0.0.1-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -13
- package/dist/adapters/notificationRouter.cjs +2 -0
- package/dist/adapters/notificationRouter.cjs.map +1 -0
- package/dist/adapters/notificationRouter.mjs +2 -0
- package/dist/adapters/notificationRouter.mjs.map +1 -0
- package/dist/adapters/pi/extension.cjs +1 -1
- package/dist/adapters/pi/extension.cjs.map +1 -1
- package/dist/adapters/pi/extension.d.cts +1 -7
- package/dist/adapters/pi/extension.d.cts.map +1 -1
- package/dist/adapters/pi/extension.d.mts +1 -7
- 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/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/llms.txt +2 -2
- package/package.json +17 -9
package/README.md
CHANGED
|
@@ -1,22 +1,60 @@
|
|
|
1
1
|
# @agimon-ai/doompi-notification
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
System and browser notifications, plus an animated shell-tab title, for
|
|
4
4
|
[DoomPi](https://www.npmjs.com/package/@agimon-ai/doompi) sessions.
|
|
5
5
|
|
|
6
|
-
Long agent runs are worth walking away from. This package
|
|
7
|
-
|
|
6
|
+
Long agent runs are worth walking away from. This package owns the shared `doom/notification` service,
|
|
7
|
+
routes each request for the active session, and announces when the agent needs attention.
|
|
8
8
|
|
|
9
9
|
## What it does
|
|
10
10
|
|
|
11
|
-
| Surface |
|
|
12
|
-
| ---------------- |
|
|
13
|
-
| Shell tab title | `π - <session or first prompt> - <repository>`, with a braille spinner while the agent works
|
|
14
|
-
|
|
|
15
|
-
|
|
|
11
|
+
| Surface | Behavior |
|
|
12
|
+
| ---------------- | -------------------------------------------------------------------------------------------- |
|
|
13
|
+
| Shell tab title | `π - <session or first prompt> - <repository>`, with a braille spinner while the agent works |
|
|
14
|
+
| Notification API | caller-authored notices through the shared `doom/notification` Cordis service |
|
|
15
|
+
| Mid-run | one notification for each agent-initiated dialog and each `ask_user_question` prompt |
|
|
16
|
+
| At rest | one notification when a run settles with nothing queued behind it |
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
In an interactive terminal session, requests use `cmux notify` first because it routes them back to
|
|
19
|
+
the session window. On macOS, a missing `cmux` falls back to `osascript`. In RPC mode, requests become
|
|
20
|
+
versioned `doom-notification` session entries for a live client such as `doompi-web`. An RPC append
|
|
21
|
+
failure stays silent and never falls back to a host notifier. Hosts without a supported notifier also
|
|
22
|
+
stay silent.
|
|
23
|
+
|
|
24
|
+
## Service usage
|
|
25
|
+
|
|
26
|
+
Callers should discover the optional service through Cordis and request delivery without depending on
|
|
27
|
+
a particular host:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import {
|
|
31
|
+
DOOM_NOTIFICATION_SERVICE,
|
|
32
|
+
readDoomNotificationService,
|
|
33
|
+
} from '@agimon-ai/doompi-extension-contracts/notification';
|
|
34
|
+
|
|
35
|
+
ctx.inject([DOOM_NOTIFICATION_SERVICE], (notificationContext) => {
|
|
36
|
+
void readDoomNotificationService(notificationContext)?.request({
|
|
37
|
+
body: 'Deployment needs approval',
|
|
38
|
+
level: 'warning',
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`body` is required. `title`, `subtitle`, and `level` are optional, and `level` accepts `info`,
|
|
44
|
+
`warning`, or `error`. The router defaults the title to `Pi`, the subtitle to the session name or
|
|
45
|
+
working-directory basename, and the level to `info`. Invalid requests, missing active sessions,
|
|
46
|
+
unavailable providers, and delivery failures are silent so notifications cannot fail an agent turn.
|
|
47
|
+
|
|
48
|
+
The package also wraps Pi's broad `ui.notify(message, level)` API while active, so existing extensions
|
|
49
|
+
use the same route without changing their calls. If this package is muted or the process is a detached
|
|
50
|
+
subagent child, it does not install that wrapper and Pi's original `ui.notify` behavior remains.
|
|
51
|
+
|
|
52
|
+
## Browser delivery
|
|
53
|
+
|
|
54
|
+
`doompi-web` shows only entries received by a currently open page. Every connected page receives live
|
|
55
|
+
notification entries for every attached session, not only the focused session. Permission is requested
|
|
56
|
+
only when the user clicks **allow notifications** in web settings. There is no service worker, Web Push,
|
|
57
|
+
replay, or durable notification queue, so a closed or disconnected page receives nothing later.
|
|
20
58
|
|
|
21
59
|
## The shell title
|
|
22
60
|
|
|
@@ -29,8 +67,8 @@ escape sequences never end up in machine-read output.
|
|
|
29
67
|
|
|
30
68
|
## Quiet by design
|
|
31
69
|
|
|
32
|
-
- A detached subagent (`PI_SUBAGENT_CHILD`)
|
|
33
|
-
desktop, and a second voice reporting the same run is noise.
|
|
70
|
+
- A detached subagent (`PI_SUBAGENT_CHILD`) does not register this package's service or wrappers. It
|
|
71
|
+
shares its parent's terminal and desktop, and a second voice reporting the same run is noise.
|
|
34
72
|
- Dialogs only notify while the agent holds the turn. A dialog the user opened is already in front
|
|
35
73
|
of them.
|
|
36
74
|
- An `ask_user_question` prompt notifies from its own event, and the dialog it then opens stays
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./systemNotification.cjs");let n=require("@agimon-ai/doompi-extension-contracts/notification"),r=require("node:path");r=e.__toESM(r,1);function i({generation:e,pi:i,context:a}){return Object.freeze({generation:e,async request(e){let o=a();if(o)try{let a=(0,n.normalizeDoomNotificationRequest)(e);if(!a)return;let s=(0,n.createDoomNotificationEntryData)({title:a.title??`Pi`,subtitle:a.subtitle??i.getSessionName()??r.default.basename(o.cwd),body:a.body,level:a.level??`info`});if(!s)return;if(o.mode===`rpc`){i.appendEntry(n.DOOM_NOTIFICATION_ENTRY_TYPE,s);return}await t.sendSystemNotification(i,s)}catch{}}})}exports.createDoomNotificationRouter=i;
|
|
2
|
+
//# sourceMappingURL=notificationRouter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notificationRouter.cjs","names":["normalizeDoomNotificationRequest","createDoomNotificationEntryData","path","DOOM_NOTIFICATION_ENTRY_TYPE","sendSystemNotification"],"sources":["../../src/adapters/notificationRouter.ts"],"sourcesContent":["import path from 'node:path';\nimport {\n createDoomNotificationEntryData,\n DOOM_NOTIFICATION_ENTRY_TYPE,\n type DoomNotificationRequest,\n type DoomNotificationService,\n normalizeDoomNotificationRequest,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { sendSystemNotification } from './systemNotification.ts';\n\nconst DEFAULT_TITLE = 'Pi';\n\nexport interface DoomNotificationRouterOptions {\n readonly generation: string;\n readonly pi: ExtensionAPI;\n readonly context: () => ExtensionContext | undefined;\n}\n\n/**\n * Creates the shared notification router for one extension runtime.\n *\n * A missing session, invalid request, or delivery failure is an intentional\n * silent outcome because notification delivery must never fail the active turn.\n * RPC delivery never falls back to a host notifier after an append failure.\n */\nexport function createDoomNotificationRouter({\n generation,\n pi,\n context: readContext,\n}: DoomNotificationRouterOptions): DoomNotificationService {\n return Object.freeze({\n generation,\n async request(request: DoomNotificationRequest): Promise<void> {\n const context = readContext();\n if (!context) return;\n\n try {\n const normalized = normalizeDoomNotificationRequest(request);\n if (!normalized) return;\n const data = createDoomNotificationEntryData({\n title: normalized.title ?? DEFAULT_TITLE,\n subtitle: normalized.subtitle ?? pi.getSessionName() ?? path.basename(context.cwd),\n body: normalized.body,\n level: normalized.level ?? 'info',\n });\n if (!data) return;\n\n if (context.mode === 'rpc') {\n pi.appendEntry(DOOM_NOTIFICATION_ENTRY_TYPE, data);\n return;\n }\n await sendSystemNotification(pi, data);\n } catch (error) {\n // Best-effort delivery deliberately ends here. In particular, an RPC\n // append failure must not leak the notification through the host OS.\n void error;\n }\n },\n });\n}\n"],"mappings":"uMA0BA,SAAgB,EAA6B,CAC3C,aACA,KACA,QAAS,GACgD,CACzD,OAAO,OAAO,OAAO,CACnB,aACA,MAAM,QAAQ,EAAiD,CAC7D,IAAM,EAAU,EAAY,EACvB,KAEL,GAAI,CACF,IAAM,GAAA,EAAaA,EAAAA,iCAAAA,CAAiC,CAAO,EAC3D,GAAI,CAAC,EAAY,OACjB,IAAM,GAAA,EAAOC,EAAAA,gCAAAA,CAAgC,CAC3C,MAAO,EAAW,OAAS,KAC3B,SAAU,EAAW,UAAY,EAAG,eAAe,GAAKC,EAAAA,QAAK,SAAS,EAAQ,GAAG,EACjF,KAAM,EAAW,KACjB,MAAO,EAAW,OAAS,MAC7B,CAAC,EACD,GAAI,CAAC,EAAM,OAEX,GAAI,EAAQ,OAAS,MAAO,CAC1B,EAAG,YAAYC,EAAAA,6BAA8B,CAAI,EACjD,MACF,CACA,MAAMC,EAAAA,uBAAuB,EAAI,CAAI,CACvC,MAAgB,CAIhB,CACF,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{sendSystemNotification as e}from"./systemNotification.mjs";import{DOOM_NOTIFICATION_ENTRY_TYPE as t,createDoomNotificationEntryData as n,normalizeDoomNotificationRequest as r}from"@agimon-ai/doompi-extension-contracts/notification";import i from"node:path";function a({generation:a,pi:o,context:s}){return Object.freeze({generation:a,async request(a){let c=s();if(c)try{let s=r(a);if(!s)return;let l=n({title:s.title??`Pi`,subtitle:s.subtitle??o.getSessionName()??i.basename(c.cwd),body:s.body,level:s.level??`info`});if(!l)return;if(c.mode===`rpc`){o.appendEntry(t,l);return}await e(o,l)}catch{}}})}export{a as createDoomNotificationRouter};
|
|
2
|
+
//# sourceMappingURL=notificationRouter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notificationRouter.mjs","names":[],"sources":["../../src/adapters/notificationRouter.ts"],"sourcesContent":["import path from 'node:path';\nimport {\n createDoomNotificationEntryData,\n DOOM_NOTIFICATION_ENTRY_TYPE,\n type DoomNotificationRequest,\n type DoomNotificationService,\n normalizeDoomNotificationRequest,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { sendSystemNotification } from './systemNotification.ts';\n\nconst DEFAULT_TITLE = 'Pi';\n\nexport interface DoomNotificationRouterOptions {\n readonly generation: string;\n readonly pi: ExtensionAPI;\n readonly context: () => ExtensionContext | undefined;\n}\n\n/**\n * Creates the shared notification router for one extension runtime.\n *\n * A missing session, invalid request, or delivery failure is an intentional\n * silent outcome because notification delivery must never fail the active turn.\n * RPC delivery never falls back to a host notifier after an append failure.\n */\nexport function createDoomNotificationRouter({\n generation,\n pi,\n context: readContext,\n}: DoomNotificationRouterOptions): DoomNotificationService {\n return Object.freeze({\n generation,\n async request(request: DoomNotificationRequest): Promise<void> {\n const context = readContext();\n if (!context) return;\n\n try {\n const normalized = normalizeDoomNotificationRequest(request);\n if (!normalized) return;\n const data = createDoomNotificationEntryData({\n title: normalized.title ?? DEFAULT_TITLE,\n subtitle: normalized.subtitle ?? pi.getSessionName() ?? path.basename(context.cwd),\n body: normalized.body,\n level: normalized.level ?? 'info',\n });\n if (!data) return;\n\n if (context.mode === 'rpc') {\n pi.appendEntry(DOOM_NOTIFICATION_ENTRY_TYPE, data);\n return;\n }\n await sendSystemNotification(pi, data);\n } catch (error) {\n // Best-effort delivery deliberately ends here. In particular, an RPC\n // append failure must not leak the notification through the host OS.\n void error;\n }\n },\n });\n}\n"],"mappings":"wQA0BA,SAAgB,EAA6B,CAC3C,aACA,KACA,QAAS,GACgD,CACzD,OAAO,OAAO,OAAO,CACnB,aACA,MAAM,QAAQ,EAAiD,CAC7D,IAAM,EAAU,EAAY,EACvB,KAEL,GAAI,CACF,IAAM,EAAa,EAAiC,CAAO,EAC3D,GAAI,CAAC,EAAY,OACjB,IAAM,EAAO,EAAgC,CAC3C,MAAO,EAAW,OAAS,KAC3B,SAAU,EAAW,UAAY,EAAG,eAAe,GAAK,EAAK,SAAS,EAAQ,GAAG,EACjF,KAAM,EAAW,KACjB,MAAO,EAAW,OAAS,MAC7B,CAAC,EACD,GAAI,CAAC,EAAM,OAEX,GAAI,EAAQ,OAAS,MAAO,CAC1B,EAAG,YAAY,EAA8B,CAAI,EACjD,MACF,CACA,MAAM,EAAuB,EAAI,CAAI,CACvC,MAAgB,CAIhB,CACF,CACF,CAAC,CACH"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../services/notificationPolicy.cjs"),t=require("../../services/notificationText.cjs"),n=require("../
|
|
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":["SUBAGENT_CHILD_ENV","Context","createWorkerTitleController","shellTabTitle","supportsShellTitle","sendSystemNotification","attentionNotification","subscribeToAskUserEvents","askUserPromptBody","promptTitle","warrantsAttentionNotification","warrantsSettledNotification","settledNotification"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { subscribeToAskUserEvents } from '@agimon-ai/doompi-extension-contracts';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { 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 { createWorkerTitleController } from '../shellTitleController.ts';\nimport { sendSystemNotification } from '../systemNotification.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_SHUTDOWN_EVENT = 'session_shutdown';\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\n/**\n * Announces every dialog the agent opens on its own initiative.\n *\n * Pi has no event for \"a dialog is showing\", so the only way to catch one is to\n * wrap the UI context the session hands out. Each wrapper delegates to the\n * original, so nothing about the dialog itself changes.\n */\nfunction wrapAttentionDialogs(\n ui: ExtensionUIContext,\n shouldNotify: () => boolean,\n notify: (body: string) => void,\n): void {\n const originalConfirm = ui.confirm.bind(ui);\n ui.confirm = (title, message, options) => {\n if (shouldNotify()) notify(`${title}: ${message}`);\n return originalConfirm(title, message, options);\n };\n\n const originalSelect = ui.select.bind(ui);\n ui.select = (title, options, dialogOptions) => {\n if (shouldNotify()) notify(title);\n return originalSelect(title, options, dialogOptions);\n };\n\n const originalInput = ui.input.bind(ui);\n ui.input = (title, placeholder, options) => {\n if (shouldNotify()) notify(title);\n return originalInput(title, placeholder, options);\n };\n\n const originalEditor = ui.editor.bind(ui);\n ui.editor = (title, prefill) => {\n if (shouldNotify()) notify(title);\n return originalEditor(title, prefill);\n };\n}\n\n/**\n * The package's single standard Pi factory, and the owner of the cordis Context.\n *\n * Pi can reload an extension in-process, so every registration is yielded to the\n * fiber and one dispose on session shutdown unwinds them in reverse: the handlers\n * go quiet, the tab title is restored, then the event subscriptions are dropped.\n */\nexport async function notificationExtension(\n pi: ExtensionAPI,\n options: NotificationExtensionOptions = {},\n): Promise<void> {\n // A detached subagent shares its parent's terminal and desktop, so anything it\n // announced would be a second voice reporting on the same run.\n if ((options.environment ?? process.env)[SUBAGENT_CHILD_ENV]) return;\n\n const cordis = new Context();\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n disposal ??= cordis.fiber.dispose();\n return disposal;\n };\n\n try {\n cordis.effect(function* () {\n const titles = options.titleController ?? createWorkerTitleController();\n const wrappedUis = new WeakSet<ExtensionUIContext>();\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 sendSystemNotification(pi, attentionNotification(body));\n };\n\n const askUserSubscriptions = subscribeToAskUserEvents(pi.events, {\n onPrompt(prompt) {\n notifyAttention(askUserPromptBody(prompt.questions));\n },\n onBlocked(payload) {\n askUserBlocked = payload.active;\n },\n });\n yield () => askUserSubscriptions.dispose();\n\n // The fiber owns the animator, so the tab lands on its idle title however\n // the session ends — including a reload that fires no settle event.\n yield () => {\n if (surface) titles.dispose(idleTitle(surface.cwd), surface.write);\n surface = undefined;\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\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 wrappedUis.add(context.ui);\n wrapAttentionDialogs(\n context.ui,\n () => active && warrantsAttentionNotification({ agentRunning, askUserBlocked }),\n notifyAttention,\n );\n });\n\n pi.on(AGENT_START_EVENT, (_event, context) => {\n if (!active) return;\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 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\n await sendSystemNotification(pi, settledNotification({ cwd: context.cwd, sessionName: pi.getSessionName() }));\n });\n }, PACKAGE_SOURCE);\n } catch (error) {\n await dispose();\n throw error;\n }\n\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\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":"0bAgCA,MAIM,EAAiB,iCAiBjB,EAAqB,OAAO,IAAI,mDAAmD,EAGzF,SAAS,EACP,EACA,EAC4B,CAC5B,IAAI,EAAoB,EAAG,GACrB,EAAO,IAAI,IACjB,KAAO,OAAO,GAAa,YAAc,CAAC,EAAK,IAAI,CAAQ,GAAG,CAC5D,EAAK,IAAI,CAAQ,EACjB,IAAM,EAAW,QAAQ,IAAI,EAAU,CAAkB,EACzD,GAAI,OAAO,GAAa,WAAY,MACpC,EAAW,CACb,CACA,OAAO,CACT,CAEA,SAAS,EAAa,EAAiB,EAAwB,CAC7D,QAAQ,eAAe,EAAS,EAAoB,CAClD,aAAc,GACd,WAAY,GACZ,MAAO,EACP,SAAU,EACZ,CAAC,CACH,CAOA,SAAS,EACP,EACA,EACA,EACA,EACA,EACY,CACZ,IAAM,EAAkB,EAAgB,EAAI,SAAS,EAC/C,GAAiD,EAAO,EAAS,KACjE,EAAsB,GAAG,EAAgB,GAAG,EAAM,IAAI,GAAS,EAC5D,EAAgB,KAAK,EAAI,EAAO,EAAS,CAAO,GAEzD,EAAa,EAAgB,CAAe,EAC5C,EAAG,QAAU,EAEb,IAAM,EAAiB,EAAgB,EAAI,QAAQ,EAC7C,GAA+C,EAAO,EAAS,KAC/D,EAAsB,GAAG,EAAgB,CAAK,EAC3C,EAAe,KAAK,EAAI,EAAO,EAAS,CAAa,GAE9D,EAAa,EAAe,CAAc,EAC1C,EAAG,OAAS,EAEZ,IAAM,EAAgB,EAAgB,EAAI,OAAO,EAC3C,GAA6C,EAAO,EAAa,KACjE,EAAsB,GAAG,EAAgB,CAAK,EAC3C,EAAc,KAAK,EAAI,EAAO,EAAa,CAAO,GAE3D,EAAa,EAAc,CAAa,EACxC,EAAG,MAAQ,EAEX,IAAM,EAAiB,EAAgB,EAAI,QAAQ,EAC7C,GAA+C,EAAO,KACtD,EAAsB,GAAG,EAAgB,CAAK,EAC3C,EAAe,KAAK,EAAI,EAAO,CAAO,GAE/C,EAAa,EAAe,CAAc,EAC1C,EAAG,OAAS,EAEZ,IAAM,EAAiB,EAAgB,EAAI,QAAQ,EAC7C,GAA+C,EAAS,IAAU,CAClE,EAAS,EAAG,EAAO,EAAS,CAAK,EAChC,EAAe,KAAK,EAAI,EAAS,CAAK,CAC7C,EAIA,OAHA,EAAa,EAAe,CAAc,EAC1C,EAAG,OAAS,MAEC,CACP,EAAG,UAAY,IAAgB,EAAG,QAAU,GAC5C,EAAG,SAAW,IAAe,EAAG,OAAS,GACzC,EAAG,QAAU,IAAc,EAAG,MAAQ,GACtC,EAAG,SAAW,IAAe,EAAG,OAAS,GACzC,EAAG,SAAW,IAAe,EAAG,OAAS,EAC/C,CACF,CAQA,SAAS,EAAmB,EAAiB,CAAE,aAAY,KAAI,WAA2C,CACxG,IAAI,EACE,EAASA,EAAAA,6BAA6B,CAAE,aAAY,KAAI,YAAe,CAAc,CAAC,EAC5F,EAAO,QAAQC,EAAAA,0BAA2B,CAAM,EAChD,EAAO,OAAO,CAACC,EAAAA,2BAA2B,EAAI,GAAmB,CAE/D,IAAM,EADU,EAAe,IAAIA,EAAAA,2BACb,CAAC,CAAC,QAExB,MADA,GAAgB,MACH,CACP,IAAkB,IAAS,EAAgB,IAAA,GACjD,CACF,CAAC,EAED,EAAO,OAAO,WAAa,CACzB,IAAM,EAAS,EAAQ,iBAAmBC,EAAAA,4BAA4B,EAChE,EAAa,IAAI,IACnB,EAAS,GACT,EAAe,GACf,EAAiB,GACjB,EACA,EAEE,EAAa,GACjBC,EAAAA,cAAc,CAAE,MAAK,YAAa,EAAG,eAAe,EAAG,OAAQ,CAAY,CAAC,EACxE,EAAgB,GAAwD,CACxE,MAAC,GAAU,CAACC,EAAAA,mBAAmB,CAAO,GAE1C,MADA,GAAU,CAAE,IAAK,EAAQ,IAAK,MAAQ,GAAU,EAAQ,GAAG,SAAS,CAAK,CAAE,EACpE,CACT,EACM,EAAmB,GAAuB,CAC1C,GAAQ,EAAY,QAAQC,EAAAA,sBAAsB,CAAI,CAAC,CAC7D,EAEA,MAAM,EAAO,GAAGC,EAAAA,2BAA6B,GAAW,CACtD,EAAgBC,EAAAA,kBAAkB,EAAO,SAAS,CAAC,CACrD,CAAC,EACD,MAAM,EAAO,GAAGC,EAAAA,4BAA8B,GAAY,CACxD,EAAiB,EAAQ,MAC3B,CAAC,EAED,SAAY,CACN,GAAS,EAAO,QAAQ,EAAU,EAAQ,GAAG,EAAG,EAAQ,KAAK,EACjE,EAAU,IAAA,EACZ,EACA,SAAY,CACV,IAAK,IAAM,KAAW,EAAW,OAAO,EAAG,EAAQ,EACnD,EAAW,MAAM,CACnB,EACA,SAAY,CACV,EAAS,GACT,EAAe,GACf,EAAiB,EACnB,EAEA,EAAG,GAAG,SAAc,EAAO,IAAY,CACrC,GAAI,CAAC,GAAU,GAAe,EAAM,SAAW,YAAwB,OACvE,EAAgB,EAChB,EAAcC,EAAAA,YAAY,EAAM,IAAI,EACpC,IAAM,EAAS,EAAc,EAAa,CAAO,EAAI,IAAA,GACjD,GAAQ,EAAO,IAAI,EAAU,EAAO,GAAG,EAAG,EAAO,KAAK,CAC5D,CAAC,EAED,EAAG,GAAG,iBAAsB,EAAQ,IAAY,CAC1C,CAAC,GAAU,EAAW,IAAI,EAAQ,EAAE,IACxC,EAAgB,EAChB,EAAW,IACT,EAAQ,GACR,EACE,EAAQ,OACF,MACA,GAAUC,EAAAA,8BAA8B,CAAE,eAAc,gBAAe,CAAC,EAC9E,GACC,EAAM,IAAU,KAAK,EAAO,QAAQ,CAAE,OAAM,GAAI,EAAQ,CAAE,OAAM,EAAI,CAAC,CAAG,CAAC,CAC5E,CACF,EACF,CAAC,EAED,EAAG,GAAG,eAAoB,EAAQ,IAAY,CAC5C,GAAI,CAAC,EAAQ,OACb,EAAgB,EAChB,EAAe,GACf,IAAM,EAAS,EAAa,CAAO,EAC/B,GAAQ,EAAO,MAAM,EAAU,EAAO,GAAG,EAAG,EAAO,KAAK,CAC9D,CAAC,EAED,EAAG,GAAG,gBAAqB,MAAO,EAAQ,IAAY,CACpD,GAAI,CAAC,EAAQ,OACb,EAAgB,EAChB,EAAe,GACf,EAAiB,GACjB,IAAM,EAAS,EAAa,CAAO,EAC/B,GAAQ,EAAO,KAAK,EAAU,EAAO,GAAG,EAAG,EAAO,KAAK,EACtDC,EAAAA,4BAA4B,EAAQ,mBAAmB,CAAC,GAC7D,MAAM,EAAO,QAAQC,EAAAA,oBAAoB,CAAE,IAAK,EAAQ,IAAK,YAAa,EAAG,eAAe,CAAE,CAAC,CAAC,CAClG,CAAC,CACH,EAAG,CAAc,CACnB,CAGA,eAAsB,EACpB,EACA,EAAwC,CAAC,EAC1B,CACf,IAAK,EAAQ,aAAe,QAAQ,IAAA,CAAKC,EAAAA,oBAAqB,OAE9D,IAAM,EAAa,MAAA,EAAMC,EAAAA,sBAAAA,CAAsB,EAAI,EAAgB,CACjE,YAAa,EAAQ,aAAe,QAAQ,GAC9C,CAAC,EACK,EAAQ,EAAW,KAAK,OAAO,EAAoB,CACvD,WAAY,GAAG,EAAW,QAAQ,WAAW,eAC7C,KACA,SACF,CAAC,EACD,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CACA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF"}
|
|
@@ -6,13 +6,7 @@ interface NotificationExtensionOptions {
|
|
|
6
6
|
titleController?: ShellTitleController;
|
|
7
7
|
environment?: NodeJS.ProcessEnv;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
* The package's single standard Pi factory, and the owner of the cordis Context.
|
|
11
|
-
*
|
|
12
|
-
* Pi can reload an extension in-process, so every registration is yielded to the
|
|
13
|
-
* fiber and one dispose on session shutdown unwinds them in reverse: the handlers
|
|
14
|
-
* go quiet, the tab title is restored, then the event subscriptions are dropped.
|
|
15
|
-
*/
|
|
9
|
+
/** The package's single standard Pi factory. */
|
|
16
10
|
declare function notificationExtension(pi: ExtensionAPI, options?: NotificationExtensionOptions): Promise<void>;
|
|
17
11
|
//#endregion
|
|
18
12
|
export { NotificationExtensionOptions, notificationExtension as default, notificationExtension };
|
|
@@ -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":";;;UAuCiB;;EAEf,kBAAkB;EAClB,cAAc,OAAO;;;iBA4MD,sBACpB,IAAI,cACJ,UAAS,+BACR"}
|
|
@@ -6,13 +6,7 @@ interface NotificationExtensionOptions {
|
|
|
6
6
|
titleController?: ShellTitleController;
|
|
7
7
|
environment?: NodeJS.ProcessEnv;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
* The package's single standard Pi factory, and the owner of the cordis Context.
|
|
11
|
-
*
|
|
12
|
-
* Pi can reload an extension in-process, so every registration is yielded to the
|
|
13
|
-
* fiber and one dispose on session shutdown unwinds them in reverse: the handlers
|
|
14
|
-
* go quiet, the tab title is restored, then the event subscriptions are dropped.
|
|
15
|
-
*/
|
|
9
|
+
/** The package's single standard Pi factory. */
|
|
16
10
|
declare function notificationExtension(pi: ExtensionAPI, options?: NotificationExtensionOptions): Promise<void>;
|
|
17
11
|
//#endregion
|
|
18
12
|
export { NotificationExtensionOptions, notificationExtension as default, notificationExtension };
|
|
@@ -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":";;;UAuCiB;;EAEf,kBAAkB;EAClB,cAAc,OAAO;;;iBA4MD,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{
|
|
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 { subscribeToAskUserEvents } from '@agimon-ai/doompi-extension-contracts';\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { 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 { createWorkerTitleController } from '../shellTitleController.ts';\nimport { sendSystemNotification } from '../systemNotification.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_SHUTDOWN_EVENT = 'session_shutdown';\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\n/**\n * Announces every dialog the agent opens on its own initiative.\n *\n * Pi has no event for \"a dialog is showing\", so the only way to catch one is to\n * wrap the UI context the session hands out. Each wrapper delegates to the\n * original, so nothing about the dialog itself changes.\n */\nfunction wrapAttentionDialogs(\n ui: ExtensionUIContext,\n shouldNotify: () => boolean,\n notify: (body: string) => void,\n): void {\n const originalConfirm = ui.confirm.bind(ui);\n ui.confirm = (title, message, options) => {\n if (shouldNotify()) notify(`${title}: ${message}`);\n return originalConfirm(title, message, options);\n };\n\n const originalSelect = ui.select.bind(ui);\n ui.select = (title, options, dialogOptions) => {\n if (shouldNotify()) notify(title);\n return originalSelect(title, options, dialogOptions);\n };\n\n const originalInput = ui.input.bind(ui);\n ui.input = (title, placeholder, options) => {\n if (shouldNotify()) notify(title);\n return originalInput(title, placeholder, options);\n };\n\n const originalEditor = ui.editor.bind(ui);\n ui.editor = (title, prefill) => {\n if (shouldNotify()) notify(title);\n return originalEditor(title, prefill);\n };\n}\n\n/**\n * The package's single standard Pi factory, and the owner of the cordis Context.\n *\n * Pi can reload an extension in-process, so every registration is yielded to the\n * fiber and one dispose on session shutdown unwinds them in reverse: the handlers\n * go quiet, the tab title is restored, then the event subscriptions are dropped.\n */\nexport async function notificationExtension(\n pi: ExtensionAPI,\n options: NotificationExtensionOptions = {},\n): Promise<void> {\n // A detached subagent shares its parent's terminal and desktop, so anything it\n // announced would be a second voice reporting on the same run.\n if ((options.environment ?? process.env)[SUBAGENT_CHILD_ENV]) return;\n\n const cordis = new Context();\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n disposal ??= cordis.fiber.dispose();\n return disposal;\n };\n\n try {\n cordis.effect(function* () {\n const titles = options.titleController ?? createWorkerTitleController();\n const wrappedUis = new WeakSet<ExtensionUIContext>();\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 sendSystemNotification(pi, attentionNotification(body));\n };\n\n const askUserSubscriptions = subscribeToAskUserEvents(pi.events, {\n onPrompt(prompt) {\n notifyAttention(askUserPromptBody(prompt.questions));\n },\n onBlocked(payload) {\n askUserBlocked = payload.active;\n },\n });\n yield () => askUserSubscriptions.dispose();\n\n // The fiber owns the animator, so the tab lands on its idle title however\n // the session ends — including a reload that fires no settle event.\n yield () => {\n if (surface) titles.dispose(idleTitle(surface.cwd), surface.write);\n surface = undefined;\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\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 wrappedUis.add(context.ui);\n wrapAttentionDialogs(\n context.ui,\n () => active && warrantsAttentionNotification({ agentRunning, askUserBlocked }),\n notifyAttention,\n );\n });\n\n pi.on(AGENT_START_EVENT, (_event, context) => {\n if (!active) return;\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 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\n await sendSystemNotification(pi, settledNotification({ cwd: context.cwd, sessionName: pi.getSessionName() }));\n });\n }, PACKAGE_SOURCE);\n } catch (error) {\n await dispose();\n throw error;\n }\n\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\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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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./services/notificationPolicy.cjs"),t=require("./services/notificationText.cjs"),n=require("./adapters/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./services/notificationPolicy.cjs"),t=require("./services/notificationText.cjs"),n=require("./adapters/systemNotification.cjs"),r=require("./adapters/shellTitleController.cjs"),i=require("./adapters/pi/extension.cjs");exports.askUserPromptBody=e.askUserPromptBody,exports.attentionNotification=t.attentionNotification,exports.createMainThreadTitleController=r.createMainThreadTitleController,exports.createWorkerTitleController=r.createWorkerTitleController,exports.notificationBody=t.notificationBody,exports.notificationExtension=i.default,exports.promptTitle=t.promptTitle,exports.sendSystemNotification=n.sendSystemNotification,exports.settledNotification=t.settledNotification,exports.shellTabTitle=t.shellTabTitle,exports.supportsShellTitle=e.supportsShellTitle,exports.warrantsAttentionNotification=e.warrantsAttentionNotification,exports.warrantsSettledNotification=e.warrantsSettledNotification;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{askUserPromptBody as e,supportsShellTitle as t,warrantsAttentionNotification as n,warrantsSettledNotification as r}from"./services/notificationPolicy.mjs";import{attentionNotification as i,notificationBody as a,promptTitle as o,settledNotification as s,shellTabTitle as c}from"./services/notificationText.mjs";import{
|
|
1
|
+
import{askUserPromptBody as e,supportsShellTitle as t,warrantsAttentionNotification as n,warrantsSettledNotification as r}from"./services/notificationPolicy.mjs";import{attentionNotification as i,notificationBody as a,promptTitle as o,settledNotification as s,shellTabTitle as c}from"./services/notificationText.mjs";import{sendSystemNotification as l}from"./adapters/systemNotification.mjs";import{createMainThreadTitleController as u,createWorkerTitleController as d}from"./adapters/shellTitleController.mjs";import f from"./adapters/pi/extension.mjs";export{e as askUserPromptBody,i as attentionNotification,u as createMainThreadTitleController,d as createWorkerTitleController,a as notificationBody,f as notificationExtension,o as promptTitle,l as sendSystemNotification,s as settledNotification,c as shellTabTitle,t as supportsShellTitle,n as warrantsAttentionNotification,r as warrantsSettledNotification};
|
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Doom Pi Notification
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shared notification routing, browser and system delivery, and an animated shell-tab title for Doom Pi sessions.
|
|
4
4
|
|
|
5
5
|
## Resources
|
|
6
6
|
|
|
7
|
-
- [Package README](./README.md):
|
|
7
|
+
- [Package README](./README.md): public `doom/notification` service usage, request defaults, RPC and interactive delivery, live-page browser permission behavior, shell-title animation, and silent fallback rules.
|
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.20",
|
|
4
4
|
"description": "Desktop notifications and an animated shell-tab title for DoomPi sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -16,8 +16,16 @@
|
|
|
16
16
|
"terminal-title"
|
|
17
17
|
],
|
|
18
18
|
"homepage": "https://agimon.ai",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/AgiFlow/doompi/issues"
|
|
21
|
+
},
|
|
19
22
|
"license": "MIT",
|
|
20
23
|
"author": "Vuong Ngo",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/AgiFlow/doompi.git",
|
|
27
|
+
"directory": "packages/core/doompi-notification"
|
|
28
|
+
},
|
|
21
29
|
"files": [
|
|
22
30
|
"dist",
|
|
23
31
|
"llms.txt",
|
|
@@ -47,19 +55,19 @@
|
|
|
47
55
|
},
|
|
48
56
|
"dependencies": {
|
|
49
57
|
"@deepseek-ai/cordis": "4.0.1",
|
|
50
|
-
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.
|
|
58
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.42"
|
|
51
59
|
},
|
|
52
60
|
"devDependencies": {
|
|
53
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
54
|
-
"@types/node": "26.
|
|
55
|
-
"@vitest/coverage-v8": "4.1.
|
|
61
|
+
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
62
|
+
"@types/node": "26.2.0",
|
|
63
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
56
64
|
"tsdown": "0.22.14",
|
|
57
|
-
"typescript": "
|
|
58
|
-
"vitest": "4.1.
|
|
59
|
-
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.
|
|
65
|
+
"typescript": "7.0.2",
|
|
66
|
+
"vitest": "4.1.11",
|
|
67
|
+
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.40"
|
|
60
68
|
},
|
|
61
69
|
"peerDependencies": {
|
|
62
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
70
|
+
"@earendil-works/pi-coding-agent": "0.84.4"
|
|
63
71
|
},
|
|
64
72
|
"peerDependenciesMeta": {
|
|
65
73
|
"@earendil-works/pi-coding-agent": {
|