@agimon-ai/doompi 0.0.1-alpha.49 → 0.0.1-alpha.50
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/contextApi.cjs +1 -0
- package/dist/contextApi.d.cts +2 -0
- package/dist/contextApi.d.mts +2 -0
- package/dist/contextApi.mjs +1 -0
- package/dist/sessionApi.cjs +1 -0
- package/dist/sessionApi.d.cts +2 -0
- package/dist/sessionApi.d.mts +2 -0
- package/dist/sessionApi.mjs +1 -0
- package/dist/src/adapters/contextApi.cjs +2 -0
- package/dist/src/adapters/contextApi.cjs.map +1 -0
- package/dist/src/adapters/contextApi.d.cts +14 -0
- package/dist/src/adapters/contextApi.d.cts.map +1 -0
- package/dist/src/adapters/contextApi.d.mts +14 -0
- package/dist/src/adapters/contextApi.d.mts.map +1 -0
- package/dist/src/adapters/contextApi.mjs +2 -0
- package/dist/src/adapters/contextApi.mjs.map +1 -0
- package/dist/src/adapters/contextDetailStore.cjs +2 -0
- package/dist/src/adapters/contextDetailStore.cjs.map +1 -0
- package/dist/src/adapters/contextDetailStore.d.cts +7 -0
- package/dist/src/adapters/contextDetailStore.d.cts.map +1 -0
- package/dist/src/adapters/contextDetailStore.d.mts +7 -0
- package/dist/src/adapters/contextDetailStore.d.mts.map +1 -0
- package/dist/src/adapters/contextDetailStore.mjs +2 -0
- package/dist/src/adapters/contextDetailStore.mjs.map +1 -0
- package/dist/src/extensions/entries/modeCatalog.cjs +1 -1
- package/dist/src/extensions/entries/modeCatalog.cjs.map +1 -1
- package/dist/src/extensions/entries/modeCatalog.d.cts.map +1 -1
- package/dist/src/extensions/entries/modeCatalog.d.mts.map +1 -1
- package/dist/src/extensions/entries/modeCatalog.mjs +1 -1
- package/dist/src/extensions/entries/modeCatalog.mjs.map +1 -1
- package/dist/src/services/contextCatalog.cjs +1 -1
- package/dist/src/services/contextCatalog.cjs.map +1 -1
- package/dist/src/services/contextCatalog.mjs +1 -1
- package/dist/src/services/contextCatalog.mjs.map +1 -1
- package/dist/src/services/contextDetail.cjs +2 -0
- package/dist/src/services/contextDetail.cjs.map +1 -0
- package/dist/src/services/contextDetail.mjs +2 -0
- package/dist/src/services/contextDetail.mjs.map +1 -0
- package/dist/src/services/contextProjection.cjs +1 -1
- package/dist/src/services/contextProjection.cjs.map +1 -1
- package/dist/src/services/contextProjection.d.cts +8 -0
- package/dist/src/services/contextProjection.d.cts.map +1 -0
- package/dist/src/services/contextProjection.d.mts +8 -0
- package/dist/src/services/contextProjection.d.mts.map +1 -0
- package/dist/src/services/contextProjection.mjs +1 -1
- package/dist/src/services/contextProjection.mjs.map +1 -1
- package/dist/src/types/contextApi.cjs +2 -0
- package/dist/src/types/contextApi.cjs.map +1 -0
- package/dist/src/types/contextApi.d.cts +69 -0
- package/dist/src/types/contextApi.d.cts.map +1 -0
- package/dist/src/types/contextApi.d.mts +69 -0
- package/dist/src/types/contextApi.d.mts.map +1 -0
- package/dist/src/types/contextApi.mjs +2 -0
- package/dist/src/types/contextApi.mjs.map +1 -0
- package/package.json +30 -13
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src/types/contextApi.cjs");exports.API_BASE_PATH=e.API_BASE_PATH,exports.CONTEXT_DETAIL_VERSION=e.CONTEXT_DETAIL_VERSION,exports.ITEM_ROUTE=e.ITEM_ROUTE,exports.KIND_QUERY_PARAM=e.KIND_QUERY_PARAM,exports.NAME_QUERY_PARAM=e.NAME_QUERY_PARAM,exports.SESSION_QUERY_PARAM=e.SESSION_QUERY_PARAM,exports.itemDetailUrl=e.itemDetailUrl;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { API_BASE_PATH, CONTEXT_DETAIL_VERSION, ContextDetailFile, ContextItemDetail, ContextItemKind, ContextSkillDetail, ContextTokenBreakdown, ContextToolDetail, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM, SESSION_QUERY_PARAM, itemDetailUrl } from "./src/types/contextApi.cjs";
|
|
2
|
+
export { API_BASE_PATH, CONTEXT_DETAIL_VERSION, ContextDetailFile, ContextItemDetail, ContextItemKind, ContextSkillDetail, ContextTokenBreakdown, ContextToolDetail, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM, SESSION_QUERY_PARAM, itemDetailUrl };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { API_BASE_PATH, CONTEXT_DETAIL_VERSION, ContextDetailFile, ContextItemDetail, ContextItemKind, ContextSkillDetail, ContextTokenBreakdown, ContextToolDetail, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM, SESSION_QUERY_PARAM, itemDetailUrl } from "./src/types/contextApi.mjs";
|
|
2
|
+
export { API_BASE_PATH, CONTEXT_DETAIL_VERSION, ContextDetailFile, ContextItemDetail, ContextItemKind, ContextSkillDetail, ContextTokenBreakdown, ContextToolDetail, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM, SESSION_QUERY_PARAM, itemDetailUrl };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{API_BASE_PATH as e,CONTEXT_DETAIL_VERSION as t,ITEM_ROUTE as n,KIND_QUERY_PARAM as r,NAME_QUERY_PARAM as i,SESSION_QUERY_PARAM as a,itemDetailUrl as o}from"./src/types/contextApi.mjs";export{e as API_BASE_PATH,t as CONTEXT_DETAIL_VERSION,n as ITEM_ROUTE,r as KIND_QUERY_PARAM,i as NAME_QUERY_PARAM,a as SESSION_QUERY_PARAM,o as itemDetailUrl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src/adapters/contextApi.cjs");exports.api=e.api,exports.createContextApi=e.createContextApi;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{api as e,createContextApi as t}from"./src/adapters/contextApi.mjs";export{e as api,t as createContextApi};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../types/contextApi.cjs"),t=require("./contextDetailStore.cjs");function n(e,t){return new Response(JSON.stringify(e),{status:t,headers:{"content-type":`application/json; charset=utf-8`}})}function r(e){return e===`tool`||e===`skill`}function i(i={}){let a=i.readDetail??t.readContextDetail;return{fetch(o){let s=new URL(o.url);if(s.pathname!==`/item`)return n({error:`No such route.`},404);if(o.method!==`GET`)return n({error:`Only GET is served here.`},405);if(i.sessionId===void 0)return n({error:`This host serves no session.`},404);let c=s.searchParams.get(e.KIND_QUERY_PARAM),l=s.searchParams.get(e.NAME_QUERY_PARAM);if(!r(c))return n({error:`Ask for a tool or a skill.`},400);if(l===null||l===``)return n({error:`Name the item to describe.`},400);let u=a(i.sessionId);if(u===void 0)return n({error:`This session has not reported its composition yet.`},404);let d=t.findContextItem(u,c,l);return d===void 0?n({error:`The session carries no ${c} called ${l}.`},404):n({revision:u.revision,item:d},200)},close:()=>{}}}const a={basePath:e.API_BASE_PATH,start(e){return i(e.sessionId===void 0?{}:{sessionId:e.sessionId})}};exports.api=a,exports.createContextApi=i;
|
|
2
|
+
//# sourceMappingURL=contextApi.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.cjs","names":["readContextDetail","KIND_QUERY_PARAM","NAME_QUERY_PARAM","findContextItem","API_BASE_PATH"],"sources":["../../../src/adapters/contextApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-extension-contracts/package-api';\nimport { findContextItem, readContextDetail } from './contextDetailStore.ts';\nimport { API_BASE_PATH, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM } from '../types/contextApi.ts';\n\n/**\n * What one row of the composition actually is, on request.\n *\n * The context panel prices every tool and skill the session carries, and the\n * figure is only half an answer: the reader deciding whether a package earns\n * its thousand tokens wants the prose and the schema behind it. Those are too\n * large to push, and they are wanted one row at a time, so they are served\n * here instead and fetched on the click.\n *\n * The agent writes the store; this reads it. The two are separate processes\n * and the inventory only exists inside the agent, which is the same split\n * every other package-owned API in the repository lives with.\n */\n\nfunction json(body: unknown, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json; charset=utf-8' },\n });\n}\n\nfunction isItemKind(value: string | null): value is 'tool' | 'skill' {\n return value === 'tool' || value === 'skill';\n}\n\nexport interface ContextApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n /** Injectable for tests; defaults to the file the agent writes. */\n readDetail?: typeof readContextDetail;\n}\n\nexport function createContextApi(options: ContextApiOptions = {}): DoomApiHandler {\n const readDetail = options.readDetail ?? readContextDetail;\n return {\n fetch(request: Request): Response {\n const url = new URL(request.url);\n if (url.pathname !== ITEM_ROUTE) return json({ error: 'No such route.' }, 404);\n if (request.method !== 'GET') return json({ error: 'Only GET is served here.' }, 405);\n if (options.sessionId === undefined) return json({ error: 'This host serves no session.' }, 404);\n\n const itemKind = url.searchParams.get(KIND_QUERY_PARAM);\n const name = url.searchParams.get(NAME_QUERY_PARAM);\n if (!isItemKind(itemKind)) return json({ error: 'Ask for a tool or a skill.' }, 400);\n if (name === null || name === '') return json({ error: 'Name the item to describe.' }, 400);\n\n const file = readDetail(options.sessionId);\n // A session that has not published yet is the ordinary state for the\n // first half-second of its life, and says so rather than looking broken.\n if (file === undefined) {\n return json({ error: 'This session has not reported its composition yet.' }, 404);\n }\n const item = findContextItem(file, itemKind, name);\n if (item === undefined) return json({ error: `The session carries no ${itemKind} called ${name}.` }, 404);\n return json({ revision: file.revision, item }, 200);\n },\n close: () => {},\n };\n}\n\nexport const api: DoomApi = {\n basePath: API_BASE_PATH,\n start(context: DoomApiContext): DoomApiHandler {\n return createContextApi(context.sessionId === undefined ? {} : { sessionId: context.sessionId });\n },\n};\n"],"mappings":"iFAkBA,SAAS,EAAK,EAAe,EAA0B,CACrD,OAAO,IAAI,SAAS,KAAK,UAAU,CAAI,EAAG,CACxC,SACA,QAAS,CAAE,eAAgB,iCAAkC,CAC/D,CAAC,CACH,CAEA,SAAS,EAAW,EAAiD,CACnE,OAAO,IAAU,QAAU,IAAU,OACvC,CASA,SAAgB,EAAiB,EAA6B,CAAC,EAAmB,CAChF,IAAM,EAAa,EAAQ,YAAcA,EAAAA,kBACzC,MAAO,CACL,MAAM,EAA4B,CAChC,IAAM,EAAM,IAAI,IAAI,EAAQ,GAAG,EAC/B,GAAI,EAAI,WAAA,QAAyB,OAAO,EAAK,CAAE,MAAO,gBAAiB,EAAG,GAAG,EAC7E,GAAI,EAAQ,SAAW,MAAO,OAAO,EAAK,CAAE,MAAO,0BAA2B,EAAG,GAAG,EACpF,GAAI,EAAQ,YAAc,IAAA,GAAW,OAAO,EAAK,CAAE,MAAO,8BAA+B,EAAG,GAAG,EAE/F,IAAM,EAAW,EAAI,aAAa,IAAIC,EAAAA,gBAAgB,EAChD,EAAO,EAAI,aAAa,IAAIC,EAAAA,gBAAgB,EAClD,GAAI,CAAC,EAAW,CAAQ,EAAG,OAAO,EAAK,CAAE,MAAO,4BAA6B,EAAG,GAAG,EACnF,GAAI,IAAS,MAAQ,IAAS,GAAI,OAAO,EAAK,CAAE,MAAO,4BAA6B,EAAG,GAAG,EAE1F,IAAM,EAAO,EAAW,EAAQ,SAAS,EAGzC,GAAI,IAAS,IAAA,GACX,OAAO,EAAK,CAAE,MAAO,oDAAqD,EAAG,GAAG,EAElF,IAAM,EAAOC,EAAAA,gBAAgB,EAAM,EAAU,CAAI,EAEjD,OADI,IAAS,IAAA,GAAkB,EAAK,CAAE,MAAO,0BAA0B,EAAS,UAAU,EAAK,EAAG,EAAG,GAAG,EACjG,EAAK,CAAE,SAAU,EAAK,SAAU,MAAK,EAAG,GAAG,CACpD,EACA,UAAa,CAAC,CAChB,CACF,CAEA,MAAa,EAAe,CAC1B,SAAUC,EAAAA,cACV,MAAM,EAAyC,CAC7C,OAAO,EAAiB,EAAQ,YAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,EAAQ,SAAU,CAAC,CACjG,CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { readContextDetail } from "./contextDetailStore.cjs";
|
|
2
|
+
import { DoomApi, DoomApiHandler } from "@agimon-ai/doompi-extension-contracts/package-api";
|
|
3
|
+
//#region src/adapters/contextApi.d.ts
|
|
4
|
+
interface ContextApiOptions {
|
|
5
|
+
/** The session these routes answer for; absent when the host is the hub. */
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
/** Injectable for tests; defaults to the file the agent writes. */
|
|
8
|
+
readDetail?: typeof readContextDetail;
|
|
9
|
+
}
|
|
10
|
+
declare function createContextApi(options?: ContextApiOptions): DoomApiHandler;
|
|
11
|
+
declare const api: DoomApi;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ContextApiOptions, api, createContextApi };
|
|
14
|
+
//# sourceMappingURL=contextApi.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.d.cts","names":[],"sources":["../../../src/adapters/contextApi.ts"],"mappings":";;;UA6BiB;;EAEf;;EAEA,oBAAoB;;iBAGN,iBAAiB,UAAS,oBAAyB;cA4BtD,KAAK"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { readContextDetail } from "./contextDetailStore.mjs";
|
|
2
|
+
import { DoomApi, DoomApiHandler } from "@agimon-ai/doompi-extension-contracts/package-api";
|
|
3
|
+
//#region src/adapters/contextApi.d.ts
|
|
4
|
+
interface ContextApiOptions {
|
|
5
|
+
/** The session these routes answer for; absent when the host is the hub. */
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
/** Injectable for tests; defaults to the file the agent writes. */
|
|
8
|
+
readDetail?: typeof readContextDetail;
|
|
9
|
+
}
|
|
10
|
+
declare function createContextApi(options?: ContextApiOptions): DoomApiHandler;
|
|
11
|
+
declare const api: DoomApi;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ContextApiOptions, api, createContextApi };
|
|
14
|
+
//# sourceMappingURL=contextApi.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.d.mts","names":[],"sources":["../../../src/adapters/contextApi.ts"],"mappings":";;;UA6BiB;;EAEf;;EAEA,oBAAoB;;iBAGN,iBAAiB,UAAS,oBAAyB;cA4BtD,KAAK"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{API_BASE_PATH as e,KIND_QUERY_PARAM as t,NAME_QUERY_PARAM as n}from"../types/contextApi.mjs";import{findContextItem as r,readContextDetail as i}from"./contextDetailStore.mjs";function a(e,t){return new Response(JSON.stringify(e),{status:t,headers:{"content-type":`application/json; charset=utf-8`}})}function o(e){return e===`tool`||e===`skill`}function s(e={}){let s=e.readDetail??i;return{fetch(i){let c=new URL(i.url);if(c.pathname!==`/item`)return a({error:`No such route.`},404);if(i.method!==`GET`)return a({error:`Only GET is served here.`},405);if(e.sessionId===void 0)return a({error:`This host serves no session.`},404);let l=c.searchParams.get(t),u=c.searchParams.get(n);if(!o(l))return a({error:`Ask for a tool or a skill.`},400);if(u===null||u===``)return a({error:`Name the item to describe.`},400);let d=s(e.sessionId);if(d===void 0)return a({error:`This session has not reported its composition yet.`},404);let f=r(d,l,u);return f===void 0?a({error:`The session carries no ${l} called ${u}.`},404):a({revision:d.revision,item:f},200)},close:()=>{}}}const c={basePath:e,start(e){return s(e.sessionId===void 0?{}:{sessionId:e.sessionId})}};export{c as api,s as createContextApi};
|
|
2
|
+
//# sourceMappingURL=contextApi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.mjs","names":[],"sources":["../../../src/adapters/contextApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-extension-contracts/package-api';\nimport { findContextItem, readContextDetail } from './contextDetailStore.ts';\nimport { API_BASE_PATH, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM } from '../types/contextApi.ts';\n\n/**\n * What one row of the composition actually is, on request.\n *\n * The context panel prices every tool and skill the session carries, and the\n * figure is only half an answer: the reader deciding whether a package earns\n * its thousand tokens wants the prose and the schema behind it. Those are too\n * large to push, and they are wanted one row at a time, so they are served\n * here instead and fetched on the click.\n *\n * The agent writes the store; this reads it. The two are separate processes\n * and the inventory only exists inside the agent, which is the same split\n * every other package-owned API in the repository lives with.\n */\n\nfunction json(body: unknown, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json; charset=utf-8' },\n });\n}\n\nfunction isItemKind(value: string | null): value is 'tool' | 'skill' {\n return value === 'tool' || value === 'skill';\n}\n\nexport interface ContextApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n /** Injectable for tests; defaults to the file the agent writes. */\n readDetail?: typeof readContextDetail;\n}\n\nexport function createContextApi(options: ContextApiOptions = {}): DoomApiHandler {\n const readDetail = options.readDetail ?? readContextDetail;\n return {\n fetch(request: Request): Response {\n const url = new URL(request.url);\n if (url.pathname !== ITEM_ROUTE) return json({ error: 'No such route.' }, 404);\n if (request.method !== 'GET') return json({ error: 'Only GET is served here.' }, 405);\n if (options.sessionId === undefined) return json({ error: 'This host serves no session.' }, 404);\n\n const itemKind = url.searchParams.get(KIND_QUERY_PARAM);\n const name = url.searchParams.get(NAME_QUERY_PARAM);\n if (!isItemKind(itemKind)) return json({ error: 'Ask for a tool or a skill.' }, 400);\n if (name === null || name === '') return json({ error: 'Name the item to describe.' }, 400);\n\n const file = readDetail(options.sessionId);\n // A session that has not published yet is the ordinary state for the\n // first half-second of its life, and says so rather than looking broken.\n if (file === undefined) {\n return json({ error: 'This session has not reported its composition yet.' }, 404);\n }\n const item = findContextItem(file, itemKind, name);\n if (item === undefined) return json({ error: `The session carries no ${itemKind} called ${name}.` }, 404);\n return json({ revision: file.revision, item }, 200);\n },\n close: () => {},\n };\n}\n\nexport const api: DoomApi = {\n basePath: API_BASE_PATH,\n start(context: DoomApiContext): DoomApiHandler {\n return createContextApi(context.sessionId === undefined ? {} : { sessionId: context.sessionId });\n },\n};\n"],"mappings":"sLAkBA,SAAS,EAAK,EAAe,EAA0B,CACrD,OAAO,IAAI,SAAS,KAAK,UAAU,CAAI,EAAG,CACxC,SACA,QAAS,CAAE,eAAgB,iCAAkC,CAC/D,CAAC,CACH,CAEA,SAAS,EAAW,EAAiD,CACnE,OAAO,IAAU,QAAU,IAAU,OACvC,CASA,SAAgB,EAAiB,EAA6B,CAAC,EAAmB,CAChF,IAAM,EAAa,EAAQ,YAAc,EACzC,MAAO,CACL,MAAM,EAA4B,CAChC,IAAM,EAAM,IAAI,IAAI,EAAQ,GAAG,EAC/B,GAAI,EAAI,WAAA,QAAyB,OAAO,EAAK,CAAE,MAAO,gBAAiB,EAAG,GAAG,EAC7E,GAAI,EAAQ,SAAW,MAAO,OAAO,EAAK,CAAE,MAAO,0BAA2B,EAAG,GAAG,EACpF,GAAI,EAAQ,YAAc,IAAA,GAAW,OAAO,EAAK,CAAE,MAAO,8BAA+B,EAAG,GAAG,EAE/F,IAAM,EAAW,EAAI,aAAa,IAAI,CAAgB,EAChD,EAAO,EAAI,aAAa,IAAI,CAAgB,EAClD,GAAI,CAAC,EAAW,CAAQ,EAAG,OAAO,EAAK,CAAE,MAAO,4BAA6B,EAAG,GAAG,EACnF,GAAI,IAAS,MAAQ,IAAS,GAAI,OAAO,EAAK,CAAE,MAAO,4BAA6B,EAAG,GAAG,EAE1F,IAAM,EAAO,EAAW,EAAQ,SAAS,EAGzC,GAAI,IAAS,IAAA,GACX,OAAO,EAAK,CAAE,MAAO,oDAAqD,EAAG,GAAG,EAElF,IAAM,EAAO,EAAgB,EAAM,EAAU,CAAI,EAEjD,OADI,IAAS,IAAA,GAAkB,EAAK,CAAE,MAAO,0BAA0B,EAAS,UAAU,EAAK,EAAG,EAAG,GAAG,EACjG,EAAK,CAAE,SAAU,EAAK,SAAU,MAAK,EAAG,GAAG,CACpD,EACA,UAAa,CAAC,CAChB,CACF,CAEA,MAAa,EAAe,CAC1B,SAAU,EACV,MAAM,EAAyC,CAC7C,OAAO,EAAiB,EAAQ,YAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,EAAQ,SAAU,CAAC,CACjG,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs");require("../types/contextApi.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:fs");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);function i(e){return e.PI_CODING_AGENT_DIR||t.default.join(r.default.homedir(),`.pi`,`agent`)}function a(e){return e.replace(/[^a-zA-Z0-9._-]/gu,`_`)}function o(e,n=process.env){return t.default.join(i(n),`doom-context`,`${a(e)}.json`)}function s(e,r,i,a=process.env){let s=o(e,a),c={version:1,revision:r,items:i},l=`${s}.${String(process.pid)}.tmp`;try{return n.default.mkdirSync(t.default.dirname(s),{recursive:!0,mode:448}),n.default.writeFileSync(l,JSON.stringify(c),{mode:384}),n.default.renameSync(l,s),!0}catch{try{n.default.rmSync(l,{force:!0})}catch{}return!1}}function c(e,t=process.env){try{n.default.rmSync(o(e,t),{force:!0})}catch{}}function l(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&Array.isArray(t.items)}function u(e,t=process.env){try{let r=JSON.parse(n.default.readFileSync(o(e,t),`utf8`));return l(r)?r:void 0}catch{return}}function d(e,t,n){return e.items.find(e=>e.itemKind===t&&e.name===n)}exports.contextDetailPath=o,exports.findContextItem=d,exports.readContextDetail=u,exports.removeContextDetail=c,exports.writeContextDetail=s;
|
|
2
|
+
//# sourceMappingURL=contextDetailStore.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextDetailStore.cjs","names":["path","os","fs"],"sources":["../../../src/adapters/contextDetailStore.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { ContextDetailFile, ContextItemDetail, ContextItemKind } from '../types/contextApi.ts';\nimport { CONTEXT_DETAIL_VERSION } from '../types/contextApi.ts';\n\n/**\n * Where the agent leaves the composition's detail for the session API to find.\n *\n * The two are separate processes: the inventory only exists inside the agent,\n * and the API that answers the browser is served by the session's host. Every\n * other package-owned API in the repository bridges that gap through the\n * filesystem rather than through the frame protocol, and this one follows,\n * because a file is also what makes the answer survive the agent going quiet\n * between turns.\n *\n * One file per session, replaced whole. The detail is a projection of the\n * composition, not a log of it, so an older copy has no reader.\n */\n\nconst DIRECTORY_NAME = 'doom-context';\nconst AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\n/** Owner-only: a tool schema is not secret, but it describes the machine. */\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nfunction agentDirectory(environment: NodeJS.ProcessEnv): string {\n return environment[AGENT_DIR_ENV] || path.join(os.homedir(), '.pi', 'agent');\n}\n\n/** A session id reaches this from a query string, so it never becomes a path. */\nfunction safeName(sessionId: string): string {\n return sessionId.replace(/[^a-zA-Z0-9._-]/gu, '_');\n}\n\nexport function contextDetailPath(sessionId: string, environment: NodeJS.ProcessEnv = process.env): string {\n return path.join(agentDirectory(environment), DIRECTORY_NAME, `${safeName(sessionId)}.json`);\n}\n\n/**\n * Publishes the detail for one session.\n *\n * Written through a temporary file so a reader never sees half a document, and\n * failures are reported to the caller rather than thrown: the panel works\n * without this, and a session must not be lost to a full disk.\n */\nexport function writeContextDetail(\n sessionId: string,\n revision: number,\n items: readonly ContextItemDetail[],\n environment: NodeJS.ProcessEnv = process.env,\n): boolean {\n const target = contextDetailPath(sessionId, environment);\n const payload: ContextDetailFile = { version: CONTEXT_DETAIL_VERSION, revision, items };\n const temporary = `${target}.${String(process.pid)}.tmp`;\n try {\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(temporary, JSON.stringify(payload), { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, target);\n return true;\n } catch {\n try {\n fs.rmSync(temporary, { force: true });\n } catch {\n // The temporary file is already unreachable; nothing further to do.\n }\n return false;\n }\n}\n\n/** Drops a session's file. Called when the session ends, so the directory does not grow forever. */\nexport function removeContextDetail(sessionId: string, environment: NodeJS.ProcessEnv = process.env): void {\n try {\n fs.rmSync(contextDetailPath(sessionId, environment), { force: true });\n } catch {\n // A file that cannot be removed is stale, not fatal; the next session with\n // this id overwrites it.\n }\n}\n\nfunction isDetailFile(value: unknown): value is ContextDetailFile {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return candidate.version === CONTEXT_DETAIL_VERSION && Array.isArray(candidate.items);\n}\n\n/** What the session wrote, or undefined when it has not written yet. */\nexport function readContextDetail(\n sessionId: string,\n environment: NodeJS.ProcessEnv = process.env,\n): ContextDetailFile | undefined {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(contextDetailPath(sessionId, environment), 'utf8'));\n return isDetailFile(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** One row's detail, matched the way the panel identifies it. */\nexport function findContextItem(\n file: ContextDetailFile,\n itemKind: ContextItemKind,\n name: string,\n): ContextItemDetail | undefined {\n return file.items.find((item) => item.itemKind === itemKind && item.name === name);\n}\n"],"mappings":"2NA0BA,SAAS,EAAe,EAAwC,CAC9D,OAAO,EAAY,qBAAkBA,EAAAA,QAAK,KAAKC,EAAAA,QAAG,QAAQ,EAAG,MAAO,OAAO,CAC7E,CAGA,SAAS,EAAS,EAA2B,CAC3C,OAAO,EAAU,QAAQ,oBAAqB,GAAG,CACnD,CAEA,SAAgB,EAAkB,EAAmB,EAAiC,QAAQ,IAAa,CACzG,OAAOD,EAAAA,QAAK,KAAK,EAAe,CAAW,EAAG,eAAgB,GAAG,EAAS,CAAS,EAAE,MAAM,CAC7F,CASA,SAAgB,EACd,EACA,EACA,EACA,EAAiC,QAAQ,IAChC,CACT,IAAM,EAAS,EAAkB,EAAW,CAAW,EACjD,EAA6B,CAAE,QAAA,EAAiC,WAAU,OAAM,EAChF,EAAY,GAAG,EAAO,GAAG,OAAO,QAAQ,GAAG,EAAE,MACnD,GAAI,CAIF,OAHA,EAAA,QAAG,UAAUA,EAAAA,QAAK,QAAQ,CAAM,EAAG,CAAE,UAAW,GAAM,KAAM,GAAuB,CAAC,EACpF,EAAA,QAAG,cAAc,EAAW,KAAK,UAAU,CAAO,EAAG,CAAE,KAAM,GAAkB,CAAC,EAChF,EAAA,QAAG,WAAW,EAAW,CAAM,EACxB,EACT,MAAQ,CACN,GAAI,CACF,EAAA,QAAG,OAAO,EAAW,CAAE,MAAO,EAAK,CAAC,CACtC,MAAQ,CAER,CACA,MAAO,EACT,CACF,CAGA,SAAgB,EAAoB,EAAmB,EAAiC,QAAQ,IAAW,CACzG,GAAI,CACF,EAAA,QAAG,OAAO,EAAkB,EAAW,CAAW,EAAG,CAAE,MAAO,EAAK,CAAC,CACtE,MAAQ,CAGR,CACF,CAEA,SAAS,EAAa,EAA4C,CAChE,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAY,EAClB,OAAO,EAAU,UAAA,GAAsC,MAAM,QAAQ,EAAU,KAAK,CACtF,CAGA,SAAgB,EACd,EACA,EAAiC,QAAQ,IACV,CAC/B,GAAI,CACF,IAAM,EAAkB,KAAK,MAAME,EAAAA,QAAG,aAAa,EAAkB,EAAW,CAAW,EAAG,MAAM,CAAC,EACrG,OAAO,EAAa,CAAM,EAAI,EAAS,IAAA,EACzC,MAAQ,CACN,MACF,CACF,CAGA,SAAgB,EACd,EACA,EACA,EAC+B,CAC/B,OAAO,EAAK,MAAM,KAAM,GAAS,EAAK,WAAa,GAAY,EAAK,OAAS,CAAI,CACnF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ContextDetailFile } from "../types/contextApi.cjs";
|
|
2
|
+
//#region src/adapters/contextDetailStore.d.ts
|
|
3
|
+
/** What the session wrote, or undefined when it has not written yet. */
|
|
4
|
+
declare function readContextDetail(sessionId: string, environment?: NodeJS.ProcessEnv): ContextDetailFile | undefined;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { readContextDetail };
|
|
7
|
+
//# sourceMappingURL=contextDetailStore.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextDetailStore.d.cts","names":[],"sources":["../../../src/adapters/contextDetailStore.ts"],"mappings":";;;iBAuFgB,kBACd,mBACA,cAAa,OAAO,aACnB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ContextDetailFile } from "../types/contextApi.mjs";
|
|
2
|
+
//#region src/adapters/contextDetailStore.d.ts
|
|
3
|
+
/** What the session wrote, or undefined when it has not written yet. */
|
|
4
|
+
declare function readContextDetail(sessionId: string, environment?: NodeJS.ProcessEnv): ContextDetailFile | undefined;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { readContextDetail };
|
|
7
|
+
//# sourceMappingURL=contextDetailStore.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextDetailStore.d.mts","names":[],"sources":["../../../src/adapters/contextDetailStore.ts"],"mappings":";;;iBAuFgB,kBACd,mBACA,cAAa,OAAO,aACnB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"../types/contextApi.mjs";import e from"node:path";import t from"node:fs";import n from"node:os";function r(t){return t.PI_CODING_AGENT_DIR||e.join(n.homedir(),`.pi`,`agent`)}function i(e){return e.replace(/[^a-zA-Z0-9._-]/gu,`_`)}function a(t,n=process.env){return e.join(r(n),`doom-context`,`${i(t)}.json`)}function o(n,r,i,o=process.env){let s=a(n,o),c={version:1,revision:r,items:i},l=`${s}.${String(process.pid)}.tmp`;try{return t.mkdirSync(e.dirname(s),{recursive:!0,mode:448}),t.writeFileSync(l,JSON.stringify(c),{mode:384}),t.renameSync(l,s),!0}catch{try{t.rmSync(l,{force:!0})}catch{}return!1}}function s(e,n=process.env){try{t.rmSync(a(e,n),{force:!0})}catch{}}function c(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&Array.isArray(t.items)}function l(e,n=process.env){try{let r=JSON.parse(t.readFileSync(a(e,n),`utf8`));return c(r)?r:void 0}catch{return}}function u(e,t,n){return e.items.find(e=>e.itemKind===t&&e.name===n)}export{a as contextDetailPath,u as findContextItem,l as readContextDetail,s as removeContextDetail,o as writeContextDetail};
|
|
2
|
+
//# sourceMappingURL=contextDetailStore.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextDetailStore.mjs","names":[],"sources":["../../../src/adapters/contextDetailStore.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { ContextDetailFile, ContextItemDetail, ContextItemKind } from '../types/contextApi.ts';\nimport { CONTEXT_DETAIL_VERSION } from '../types/contextApi.ts';\n\n/**\n * Where the agent leaves the composition's detail for the session API to find.\n *\n * The two are separate processes: the inventory only exists inside the agent,\n * and the API that answers the browser is served by the session's host. Every\n * other package-owned API in the repository bridges that gap through the\n * filesystem rather than through the frame protocol, and this one follows,\n * because a file is also what makes the answer survive the agent going quiet\n * between turns.\n *\n * One file per session, replaced whole. The detail is a projection of the\n * composition, not a log of it, so an older copy has no reader.\n */\n\nconst DIRECTORY_NAME = 'doom-context';\nconst AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\n/** Owner-only: a tool schema is not secret, but it describes the machine. */\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nfunction agentDirectory(environment: NodeJS.ProcessEnv): string {\n return environment[AGENT_DIR_ENV] || path.join(os.homedir(), '.pi', 'agent');\n}\n\n/** A session id reaches this from a query string, so it never becomes a path. */\nfunction safeName(sessionId: string): string {\n return sessionId.replace(/[^a-zA-Z0-9._-]/gu, '_');\n}\n\nexport function contextDetailPath(sessionId: string, environment: NodeJS.ProcessEnv = process.env): string {\n return path.join(agentDirectory(environment), DIRECTORY_NAME, `${safeName(sessionId)}.json`);\n}\n\n/**\n * Publishes the detail for one session.\n *\n * Written through a temporary file so a reader never sees half a document, and\n * failures are reported to the caller rather than thrown: the panel works\n * without this, and a session must not be lost to a full disk.\n */\nexport function writeContextDetail(\n sessionId: string,\n revision: number,\n items: readonly ContextItemDetail[],\n environment: NodeJS.ProcessEnv = process.env,\n): boolean {\n const target = contextDetailPath(sessionId, environment);\n const payload: ContextDetailFile = { version: CONTEXT_DETAIL_VERSION, revision, items };\n const temporary = `${target}.${String(process.pid)}.tmp`;\n try {\n fs.mkdirSync(path.dirname(target), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(temporary, JSON.stringify(payload), { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, target);\n return true;\n } catch {\n try {\n fs.rmSync(temporary, { force: true });\n } catch {\n // The temporary file is already unreachable; nothing further to do.\n }\n return false;\n }\n}\n\n/** Drops a session's file. Called when the session ends, so the directory does not grow forever. */\nexport function removeContextDetail(sessionId: string, environment: NodeJS.ProcessEnv = process.env): void {\n try {\n fs.rmSync(contextDetailPath(sessionId, environment), { force: true });\n } catch {\n // A file that cannot be removed is stale, not fatal; the next session with\n // this id overwrites it.\n }\n}\n\nfunction isDetailFile(value: unknown): value is ContextDetailFile {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return candidate.version === CONTEXT_DETAIL_VERSION && Array.isArray(candidate.items);\n}\n\n/** What the session wrote, or undefined when it has not written yet. */\nexport function readContextDetail(\n sessionId: string,\n environment: NodeJS.ProcessEnv = process.env,\n): ContextDetailFile | undefined {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(contextDetailPath(sessionId, environment), 'utf8'));\n return isDetailFile(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** One row's detail, matched the way the panel identifies it. */\nexport function findContextItem(\n file: ContextDetailFile,\n itemKind: ContextItemKind,\n name: string,\n): ContextItemDetail | undefined {\n return file.items.find((item) => item.itemKind === itemKind && item.name === name);\n}\n"],"mappings":"uGA0BA,SAAS,EAAe,EAAwC,CAC9D,OAAO,EAAY,qBAAkB,EAAK,KAAK,EAAG,QAAQ,EAAG,MAAO,OAAO,CAC7E,CAGA,SAAS,EAAS,EAA2B,CAC3C,OAAO,EAAU,QAAQ,oBAAqB,GAAG,CACnD,CAEA,SAAgB,EAAkB,EAAmB,EAAiC,QAAQ,IAAa,CACzG,OAAO,EAAK,KAAK,EAAe,CAAW,EAAG,eAAgB,GAAG,EAAS,CAAS,EAAE,MAAM,CAC7F,CASA,SAAgB,EACd,EACA,EACA,EACA,EAAiC,QAAQ,IAChC,CACT,IAAM,EAAS,EAAkB,EAAW,CAAW,EACjD,EAA6B,CAAE,QAAA,EAAiC,WAAU,OAAM,EAChF,EAAY,GAAG,EAAO,GAAG,OAAO,QAAQ,GAAG,EAAE,MACnD,GAAI,CAIF,OAHA,EAAG,UAAU,EAAK,QAAQ,CAAM,EAAG,CAAE,UAAW,GAAM,KAAM,GAAuB,CAAC,EACpF,EAAG,cAAc,EAAW,KAAK,UAAU,CAAO,EAAG,CAAE,KAAM,GAAkB,CAAC,EAChF,EAAG,WAAW,EAAW,CAAM,EACxB,EACT,MAAQ,CACN,GAAI,CACF,EAAG,OAAO,EAAW,CAAE,MAAO,EAAK,CAAC,CACtC,MAAQ,CAER,CACA,MAAO,EACT,CACF,CAGA,SAAgB,EAAoB,EAAmB,EAAiC,QAAQ,IAAW,CACzG,GAAI,CACF,EAAG,OAAO,EAAkB,EAAW,CAAW,EAAG,CAAE,MAAO,EAAK,CAAC,CACtE,MAAQ,CAGR,CACF,CAEA,SAAS,EAAa,EAA4C,CAChE,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAY,EAClB,OAAO,EAAU,UAAA,GAAsC,MAAM,QAAQ,EAAU,KAAK,CACtF,CAGA,SAAgB,EACd,EACA,EAAiC,QAAQ,IACV,CAC/B,GAAI,CACF,IAAM,EAAkB,KAAK,MAAM,EAAG,aAAa,EAAkB,EAAW,CAAW,EAAG,MAAM,CAAC,EACrG,OAAO,EAAa,CAAM,EAAI,EAAS,IAAA,EACzC,MAAQ,CACN,MACF,CACF,CAGA,SAAgB,EACd,EACA,EACA,EAC+B,CAC/B,OAAO,EAAK,MAAM,KAAM,GAAS,EAAK,WAAa,GAAY,EAAK,OAAS,CAAI,CACnF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../services/modeCatalog.cjs"),t=require("../../services/contextCatalog.cjs"),
|
|
1
|
+
const e=require("../../services/modeCatalog.cjs"),t=require("../../adapters/contextDetailStore.cjs"),n=require("../../services/contextCatalog.cjs"),r=require("../../services/minorModeProjection.cjs"),i=require("./minorModeCommand.cjs");let a=require("@agimon-ai/doompi-extension-contracts/cordis-host"),o=require("@agimon-ai/doompi-extension-contracts/mode"),s=require("@agimon-ai/doompi-extension-contracts/help"),c=require("@agimon-ai/doompi-extension-contracts/notification"),l=require("@agimon-ai/doompi-extension-contracts/transition");function u(e,t,n,r){let i=(0,c.readDoomNotificationService)(e);if(i){try{Promise.resolve(i.request({body:n,level:r})).catch(()=>void 0)}catch{}return}t.ui.notify(n,r)}function d(e){return e.includes(`voice`)?`voice`:e.includes(`leader`)?`leader`:e.includes(`ui`)?`ui`:`system`}function f(e){let t=e.current?.getSnapshot();return t?t.modes.filter(e=>e.state.activation===`active`).map(e=>({id:e.descriptor.id,label:e.descriptor.label})):[]}async function p(e){let r={current:void 0};i.registerMinorModeCommand(e,()=>r.current);let o={};e.on(`session_start`,()=>{setTimeout(()=>{r.republish?.(),o.publisher?.publish()},500)}),e.on(`turn_start`,()=>{o.publisher?.publish()});let s=await(0,a.connectDoomCordisHost)(e,`@agimon-ai/doompi/mode-catalog`),c=s.root.plugin(i=>(o.publisher=n.createContextPublisher(e,i,{readMinorModes:()=>f(r),readSessionId:()=>r.sessionId,writeDetail:(e,n,r)=>void t.writeContextDetail(e,n,r),removeDetail:t.removeContextDetail}),m(i,e,r,()=>void o.publisher?.publish()),()=>{o.publisher?.dispose(),o.publisher=void 0}));try{await c}catch(e){throw await c.dispose(),await s.dispose(),e}let l;e.on(`session_shutdown`,()=>l??=(async()=>{try{await c.dispose()}finally{await s.dispose()}})())}function m(t,n,i,c){t.inject([s.DOOM_HELP_SERVICE],e=>{let t=(0,s.requireDoomHelpService)(e).register({source:`@agimon-ai/doompi`,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`doompi-author-extension`,description:`Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.`}]});return()=>t.dispose()}),t.inject([a.DOOM_CORDIS_SESSION_SERVICE,l.DOOM_TRANSITION_SERVICE],t=>{let s=t.get(a.DOOM_CORDIS_SESSION_SERVICE),f=s.context,p=(0,l.requireDoomTransitionCoordinator)(t),m=s.reason===`reload`,h=s.sessionId,g=m?(0,l.consumeMinorModeReloadHandoff)(h):void 0;m||(0,l.discardMinorModeReloadHandoff)(h);let _=f.hasUI&&f.mode===`tui`?`tui`:`headless`,v=e.createMinorModeCatalogHost({sessionKind:_,context:f,restoreSnapshot:g,onRestorationError(e){u(t,f,`Could not restore a minor mode after reload: ${e instanceof Error?e.message:String(e)}`,`warning`)},async routeInvocation(e,t,n){let r,i=await p.execute({sessionId:f.sessionManager.getSessionId(),hostGeneration:p.hostGeneration,operationId:e.operationId,source:d(t),target:{axis:`minor-mode`,action:e,requesterSource:t}},async()=>(r=await n(),`applied`));if(i.outcome!==`applied`||!r)throw Error(`Minor-mode transition was ${i.outcome}: ${i.diagnostics.join(`, `)}`);return r}});t.provide(o.DOOM_MINOR_MODE_CATALOG_SERVICE,v),i.current=v,i.sessionId=h;let y,b,x=()=>{b=void 0;let e=r.projectMinorModes(v.getSnapshot(),_),t=JSON.stringify(e);t!==y&&(y=t,n.appendEntry(o.DOOM_MINOR_MODE_ENTRY_TYPE,e),c())},S=v.subscribe(()=>{b??=setTimeout(x,50)});return i.republish=()=>{y=void 0,x()},()=>{b&&clearTimeout(b),S(),i.republish=void 0,i.current=void 0,i.sessionId=void 0,v.dispose()}})}exports.default=p,exports.modeCatalogExtension=p;
|
|
2
2
|
//# sourceMappingURL=modeCatalog.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modeCatalog.cjs","names":["readDoomNotificationService","connectDoomCordisHost","createContextPublisher","DOOM_HELP_SERVICE","requireDoomHelpService","DOOM_CORDIS_SESSION_SERVICE","DOOM_TRANSITION_SERVICE","requireDoomTransitionCoordinator","consumeMinorModeReloadHandoff","createMinorModeCatalogHost","DOOM_MINOR_MODE_CATALOG_SERVICE","projectMinorModes","DOOM_MINOR_MODE_ENTRY_TYPE"],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // MCP status is pull-only and servers connect long after startup, so the\n // boot read above cannot see them. A turn is the moment the composition is\n // actually sent, which makes it both the freshest and the most meaningful\n // time to price. Identical compositions are skipped, so a quiet session\n // journals nothing extra.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis);\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\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\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"modeCatalog.cjs","names":["readDoomNotificationService","connectDoomCordisHost","createContextPublisher","writeContextDetail","removeContextDetail","DOOM_HELP_SERVICE","requireDoomHelpService","DOOM_CORDIS_SESSION_SERVICE","DOOM_TRANSITION_SERVICE","requireDoomTransitionCoordinator","consumeMinorModeReloadHandoff","createMinorModeCatalogHost","DOOM_MINOR_MODE_CATALOG_SERVICE","projectMinorModes","DOOM_MINOR_MODE_ENTRY_TYPE"],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { removeContextDetail, writeContextDetail } from '../../adapters/contextDetailStore.ts';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n /**\n * The bound session, which the detail store is keyed by.\n *\n * The server launches the agent with `--session-id`, so this is the same id\n * the cockpit and the session API know the session by, and a browser asking\n * for a row's detail looks under the file this session wrote.\n */\n sessionId?: string;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\n/**\n * The minor modes that are actually on, for the composition panel.\n *\n * Read from the bound catalog at publish time rather than passed in, because a\n * mode switching on is one of the reasons a publish happens at all, and a\n * remembered list would describe the composition before the switch.\n */\nfunction activeMinorModes(binding: CatalogBinding): { id: string; label: string }[] {\n const snapshot = binding.current?.getSnapshot();\n if (!snapshot) return [];\n return snapshot.modes\n .filter((record) => record.state.activation === 'active')\n .map((record) => ({ id: record.descriptor.id, label: record.descriptor.label }));\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // MCP status is pull-only and servers connect long after startup, so the\n // boot read above cannot see them. A turn is the moment the composition is\n // actually sent, which makes it both the freshest and the most meaningful\n // time to price. Identical compositions are skipped, so a quiet session\n // journals nothing extra.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis, {\n readMinorModes: () => activeMinorModes(activeCatalog),\n readSessionId: () => activeCatalog.sessionId,\n writeDetail: (sessionId, revision, items) => void writeContextDetail(sessionId, revision, items),\n removeDetail: removeContextDetail,\n });\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\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\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n activeCatalog.sessionId = sessionId;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n activeCatalog.sessionId = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modeCatalog.d.cts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"modeCatalog.d.cts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;iBA4FsB,qBAAqB,IAAI,eAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modeCatalog.d.mts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"modeCatalog.d.mts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;iBA4FsB,qBAAqB,IAAI,eAAe"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createMinorModeCatalogHost as e}from"../../services/modeCatalog.mjs";import{
|
|
1
|
+
import{createMinorModeCatalogHost as e}from"../../services/modeCatalog.mjs";import{removeContextDetail as t,writeContextDetail as n}from"../../adapters/contextDetailStore.mjs";import{createContextPublisher as r}from"../../services/contextCatalog.mjs";import{projectMinorModes as i}from"../../services/minorModeProjection.mjs";import{registerMinorModeCommand as a}from"./minorModeCommand.mjs";import{DOOM_CORDIS_SESSION_SERVICE as o,connectDoomCordisHost as s}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_MINOR_MODE_CATALOG_SERVICE as c,DOOM_MINOR_MODE_ENTRY_TYPE as l}from"@agimon-ai/doompi-extension-contracts/mode";import{DOOM_HELP_SERVICE as u,requireDoomHelpService as d}from"@agimon-ai/doompi-extension-contracts/help";import{readDoomNotificationService as f}from"@agimon-ai/doompi-extension-contracts/notification";import{DOOM_TRANSITION_SERVICE as p,consumeMinorModeReloadHandoff as m,discardMinorModeReloadHandoff as h,requireDoomTransitionCoordinator as g}from"@agimon-ai/doompi-extension-contracts/transition";function _(e,t,n,r){let i=f(e);if(i){try{Promise.resolve(i.request({body:n,level:r})).catch(()=>void 0)}catch{}return}t.ui.notify(n,r)}function v(e){return e.includes(`voice`)?`voice`:e.includes(`leader`)?`leader`:e.includes(`ui`)?`ui`:`system`}function y(e){let t=e.current?.getSnapshot();return t?t.modes.filter(e=>e.state.activation===`active`).map(e=>({id:e.descriptor.id,label:e.descriptor.label})):[]}async function b(e){let i={current:void 0};a(e,()=>i.current);let o={};e.on(`session_start`,()=>{setTimeout(()=>{i.republish?.(),o.publisher?.publish()},500)}),e.on(`turn_start`,()=>{o.publisher?.publish()});let c=await s(e,`@agimon-ai/doompi/mode-catalog`),l=c.root.plugin(a=>(o.publisher=r(e,a,{readMinorModes:()=>y(i),readSessionId:()=>i.sessionId,writeDetail:(e,t,r)=>void n(e,t,r),removeDetail:t}),x(a,e,i,()=>void o.publisher?.publish()),()=>{o.publisher?.dispose(),o.publisher=void 0}));try{await l}catch(e){throw await l.dispose(),await c.dispose(),e}let u;e.on(`session_shutdown`,()=>u??=(async()=>{try{await l.dispose()}finally{await c.dispose()}})())}function x(t,n,r,a){t.inject([u],e=>{let t=d(e).register({source:`@agimon-ai/doompi`,moduleUrl:import.meta.url,skills:[{name:`doompi-author-extension`,description:`Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.`}]});return()=>t.dispose()}),t.inject([o,p],t=>{let s=t.get(o),u=s.context,d=g(t),f=s.reason===`reload`,p=s.sessionId,y=f?m(p):void 0;f||h(p);let b=u.hasUI&&u.mode===`tui`?`tui`:`headless`,x=e({sessionKind:b,context:u,restoreSnapshot:y,onRestorationError(e){_(t,u,`Could not restore a minor mode after reload: ${e instanceof Error?e.message:String(e)}`,`warning`)},async routeInvocation(e,t,n){let r,i=await d.execute({sessionId:u.sessionManager.getSessionId(),hostGeneration:d.hostGeneration,operationId:e.operationId,source:v(t),target:{axis:`minor-mode`,action:e,requesterSource:t}},async()=>(r=await n(),`applied`));if(i.outcome!==`applied`||!r)throw Error(`Minor-mode transition was ${i.outcome}: ${i.diagnostics.join(`, `)}`);return r}});t.provide(c,x),r.current=x,r.sessionId=p;let S,C,w=()=>{C=void 0;let e=i(x.getSnapshot(),b),t=JSON.stringify(e);t!==S&&(S=t,n.appendEntry(l,e),a())},T=x.subscribe(()=>{C??=setTimeout(w,50)});return r.republish=()=>{S=void 0,w()},()=>{C&&clearTimeout(C),T(),r.republish=void 0,r.current=void 0,r.sessionId=void 0,x.dispose()}})}export{b as default,b as modeCatalogExtension};
|
|
2
2
|
//# sourceMappingURL=modeCatalog.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modeCatalog.mjs","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // MCP status is pull-only and servers connect long after startup, so the\n // boot read above cannot see them. A turn is the moment the composition is\n // actually sent, which makes it both the freshest and the most meaningful\n // time to price. Identical compositions are skipped, so a quiet session\n // journals nothing extra.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis);\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\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\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"modeCatalog.mjs","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { removeContextDetail, writeContextDetail } from '../../adapters/contextDetailStore.ts';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n /**\n * The bound session, which the detail store is keyed by.\n *\n * The server launches the agent with `--session-id`, so this is the same id\n * the cockpit and the session API know the session by, and a browser asking\n * for a row's detail looks under the file this session wrote.\n */\n sessionId?: string;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\n/**\n * The minor modes that are actually on, for the composition panel.\n *\n * Read from the bound catalog at publish time rather than passed in, because a\n * mode switching on is one of the reasons a publish happens at all, and a\n * remembered list would describe the composition before the switch.\n */\nfunction activeMinorModes(binding: CatalogBinding): { id: string; label: string }[] {\n const snapshot = binding.current?.getSnapshot();\n if (!snapshot) return [];\n return snapshot.modes\n .filter((record) => record.state.activation === 'active')\n .map((record) => ({ id: record.descriptor.id, label: record.descriptor.label }));\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // MCP status is pull-only and servers connect long after startup, so the\n // boot read above cannot see them. A turn is the moment the composition is\n // actually sent, which makes it both the freshest and the most meaningful\n // time to price. Identical compositions are skipped, so a quiet session\n // journals nothing extra.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis, {\n readMinorModes: () => activeMinorModes(activeCatalog),\n readSessionId: () => activeCatalog.sessionId,\n writeDetail: (sessionId, revision, items) => void writeContextDetail(sessionId, revision, items),\n removeDetail: removeContextDetail,\n });\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\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\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n activeCatalog.sessionId = sessionId;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n activeCatalog.sessionId = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./contextProjection.cjs");let
|
|
1
|
+
const e=require("./contextDetail.cjs"),t=require("./contextProjection.cjs");let n=require("@agimon-ai/doompi-config/domains"),r=require("@agimon-ai/doompi-ui/extensionName"),i=require("@agimon-ai/doompi-config/harnessStore"),a=require("@agimon-ai/doompi-skill/catalog"),o=require("@agimon-ai/doompi-extension-contracts/mcp-status"),s=require("@agimon-ai/doompi-extension-contracts/skills"),c=require("@agimon-ai/doompi-ui/toolInventory");function l(e){return e.flatMap(e=>e.owners.flatMap(e=>e.skills))}function u(e,t,r){let a={...(0,i.getHarnessState)().packageAttribution};try{for(let i of(0,n.resolvePluginEntries)(e,[...t],[...r]))i.name&&i.domain&&(a[i.name]={kind:`domain`,mode:i.domain})}catch{}return a}function d(n,d,f={}){let p,m=0,h=!1,g,_=async()=>{if(h)return;let _=(0,i.getHarnessState)(),v=(0,o.readDoomMcpStatus)(d)?.getSnapshot().servers,y=(0,c.buildToolSources)({tools:n.getAllTools(),activeTools:n.getActiveTools(),...v?{mcpServers:v}:{},resolveExtensionName:r.extensionName,resolveExtensionPackageName:r.extensionPackageName,resolveExtensionToolSource:e=>(0,r.extensionToolSource)(n,e)}),b=(0,i.harnessRoot)(_),x=[];try{x=l((await(0,a.buildSkillCatalog)({repoRoot:b,activeSkillDirectories:_.skillDirectories,extensionSources:(0,s.readDoomSkillSourcesService)(d)?.list()??[]})).groups)}catch{}let S=await(0,a.counter)(),C=t.projectContext({revision:m+1,majorMode:_.majorMode,minorModes:f.readMinorModes?.()??[],domains:_.domains,sources:y,skills:x,attribution:u(b,_.domains,_.pluginDirectories),countTokens:S}),w=JSON.stringify({...C,revision:0});if(w===p||h)return;p=w,m+=1;let T=f.readSessionId?.();T!==void 0&&T!==``&&f.writeDetail!==void 0&&(g=T,f.writeDetail(T,m,e.buildContextDetail({sources:y,skills:x,countTokens:S}))),n.appendEntry(t.DOOM_CONTEXT_ENTRY_TYPE,{...C,revision:m})};return{publish:async()=>{try{await _()}catch{}},dispose:()=>{h=!0,g!==void 0&&f.removeDetail?.(g),g=void 0}}}exports.createContextPublisher=d;
|
|
2
2
|
//# sourceMappingURL=contextCatalog.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextCatalog.cjs","names":["getHarnessState","resolvePluginEntries","readDoomMcpStatus","buildToolSources","extensionName","extensionPackageName","extensionToolSource","harnessRoot","buildSkillCatalog","readDoomSkillSourcesService","projectContext","
|
|
1
|
+
{"version":3,"file":"contextCatalog.cjs","names":["getHarnessState","resolvePluginEntries","readDoomMcpStatus","buildToolSources","extensionName","extensionPackageName","extensionToolSource","harnessRoot","buildSkillCatalog","readDoomSkillSourcesService","counter","projectContext","buildContextDetail","DOOM_CONTEXT_ENTRY_TYPE"],"sources":["../../../src/services/contextCatalog.ts"],"sourcesContent":["import { resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { getHarnessState, harnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport { readDoomMcpStatus } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport { readDoomSkillSourcesService } from '@agimon-ai/doompi-extension-contracts/skills';\nimport { buildSkillCatalog, counter, type SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { extensionName, extensionPackageName, extensionToolSource } from '@agimon-ai/doompi-ui/extensionName';\nimport { buildToolSources } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { ContextItemDetail } from '../types/contextApi.ts';\nimport { buildContextDetail } from './contextDetail.ts';\nimport { DOOM_CONTEXT_ENTRY_TYPE, projectContext } from './contextProjection.ts';\n\n/**\n * Publishes what the session is composed of, and what it costs.\n *\n * Read at publish time rather than accumulated, because the answer changes for\n * reasons this module does not see: a reconnected MCP server, a domain switch,\n * a plan-mode tool swap. Reading the live inventory is cheap next to being\n * wrong about it.\n */\nexport interface ContextPublisher {\n /** Never rejects: callers fire this and forget it. */\n publish: () => Promise<void>;\n dispose: () => void;\n}\n\n/** The active minor modes at publish time, read rather than remembered. */\nexport type ReadMinorModes = () => readonly { readonly id: string; readonly label: string }[];\n\nexport interface ContextPublisherOptions {\n readMinorModes?: ReadMinorModes;\n /**\n * The session the detail file is keyed by, read at publish time.\n *\n * Undefined until a session is bound, and undefined in a host that has no\n * session at all; the projection is still journaled either way, and only the\n * click-through detail goes unwritten.\n */\n readSessionId?: () => string | undefined;\n /**\n * Where the click-through detail is left for the session API to find.\n *\n * Injected because writing it is filesystem work and this is not the layer\n * that does filesystem work. A host that supplies neither still gets the\n * projection; only the detail behind a row goes unpublished.\n */\n writeDetail?: (sessionId: string, revision: number, items: readonly ContextItemDetail[]) => void;\n removeDetail?: (sessionId: string) => void;\n}\n\n/** Skills sit under owners, which is one level deeper than a flat list. */\nfunction flattenSkills(groups: Awaited<ReturnType<typeof buildSkillCatalog>>['groups']): SkillEntry[] {\n return groups.flatMap((group) => group.owners.flatMap((owner) => owner.skills));\n}\n\n/**\n * Package and plugin names to the mode that admitted them.\n *\n * Two halves meet here. Layer packages come from the harness, recorded when the\n * composition resolved. Domain plugins are re-resolved, because only the plugin\n * entry knows the domain that carried it.\n */\nfunction attributionFor(repoRoot: string, domains: readonly string[], pluginDirectories: readonly string[]) {\n const harness = getHarnessState();\n const attribution: Record<string, PackageAttribution> = { ...harness.packageAttribution };\n try {\n for (const entry of resolvePluginEntries(repoRoot, [...domains], [...pluginDirectories])) {\n if (entry.name && entry.domain) attribution[entry.name] = { kind: 'domain', mode: entry.domain };\n }\n } catch {\n // A domain that no longer resolves must not cost the reader the tool list.\n // Anything unmatched simply lands under core.\n }\n return attribution;\n}\n\nexport function createContextPublisher(\n pi: ExtensionAPI,\n cordis: Context,\n options: ContextPublisherOptions = {},\n): ContextPublisher {\n let published: string | undefined;\n let revision = 0;\n let disposed = false;\n /** Removed on disposal, so a session leaves nothing behind in the store. */\n let detailSessionId: string | undefined;\n\n const build = async (): Promise<void> => {\n if (disposed) return;\n const harness = getHarnessState();\n const mcpServers = readDoomMcpStatus(cordis)?.getSnapshot().servers;\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n resolveExtensionName: extensionName,\n resolveExtensionPackageName: extensionPackageName,\n resolveExtensionToolSource: (toolName) => extensionToolSource(pi, toolName),\n });\n\n const repoRoot = harnessRoot(harness);\n let skills: SkillEntry[] = [];\n try {\n const catalog = await buildSkillCatalog({\n repoRoot,\n activeSkillDirectories: harness.skillDirectories,\n extensionSources: readDoomSkillSourcesService(cordis)?.list() ?? [],\n });\n skills = flattenSkills(catalog.groups);\n } catch {\n // Tools are the larger cost and are already in hand; a skill walk that\n // fails should not take the whole figure down with it.\n }\n\n const countTokens = await counter();\n const projection = projectContext({\n revision: revision + 1,\n majorMode: harness.majorMode,\n minorModes: options.readMinorModes?.() ?? [],\n domains: harness.domains,\n sources,\n skills,\n attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),\n countTokens,\n });\n\n // Revision is compared out, so a republish that changed nothing is silent\n // rather than a new journal entry saying the same thing.\n const serialized = JSON.stringify({ ...projection, revision: 0 });\n if (serialized === published || disposed) return;\n published = serialized;\n revision += 1;\n // The detail lands before the entry that invites a reader to ask for it, so\n // a click that follows the panel's update cannot outrun the file behind it.\n const sessionId = options.readSessionId?.();\n if (sessionId !== undefined && sessionId !== '' && options.writeDetail !== undefined) {\n detailSessionId = sessionId;\n options.writeDetail(sessionId, revision, buildContextDetail({ sources, skills, countTokens }));\n }\n pi.appendEntry(DOOM_CONTEXT_ENTRY_TYPE, { ...projection, revision });\n };\n\n /**\n * Reporting the composition is a convenience, so it fails quietly.\n *\n * Callers publish without awaiting, and an escaping rejection would surface\n * as an unhandled error in a live session. A panel that cannot say what the\n * toolbox costs is a far smaller problem than that.\n */\n const publish = async (): Promise<void> => {\n try {\n await build();\n } catch {\n // Left unreported on purpose: the next composition change tries again.\n }\n };\n\n return {\n publish,\n dispose: () => {\n disposed = true;\n if (detailSessionId !== undefined) options.removeDetail?.(detailSessionId);\n detailSessionId = undefined;\n },\n };\n}\n"],"mappings":"sbAqDA,SAAS,EAAc,EAA+E,CACpG,OAAO,EAAO,QAAS,GAAU,EAAM,OAAO,QAAS,GAAU,EAAM,MAAM,CAAC,CAChF,CASA,SAAS,EAAe,EAAkB,EAA4B,EAAsC,CAE1G,IAAM,EAAkD,CAAE,IAAA,EAD1CA,EAAAA,gBAAAA,CACmD,CAAC,CAAC,kBAAmB,EACxF,GAAI,CACF,IAAK,IAAM,KAAA,EAASC,EAAAA,qBAAAA,CAAqB,EAAU,CAAC,GAAG,CAAO,EAAG,CAAC,GAAG,CAAiB,CAAC,EACjF,EAAM,MAAQ,EAAM,SAAQ,EAAY,EAAM,MAAQ,CAAE,KAAM,SAAU,KAAM,EAAM,MAAO,EAEnG,MAAQ,CAGR,CACA,OAAO,CACT,CAEA,SAAgB,EACd,EACA,EACA,EAAmC,CAAC,EAClB,CAClB,IAAI,EACA,EAAW,EACX,EAAW,GAEX,EAEE,EAAQ,SAA2B,CACvC,GAAI,EAAU,OACd,IAAM,GAAA,EAAUD,EAAAA,gBAAAA,CAAgB,EAC1B,GAAA,EAAaE,EAAAA,kBAAAA,CAAkB,CAAM,CAAC,EAAE,YAAY,CAAC,CAAC,QACtD,GAAA,EAAUC,EAAAA,iBAAAA,CAAiB,CAC/B,MAAO,EAAG,YAAY,EACtB,YAAa,EAAG,eAAe,EAC/B,GAAI,EAAa,CAAE,YAAW,EAAI,CAAC,EACnC,qBAAsBC,EAAAA,cACtB,4BAA6BC,EAAAA,qBAC7B,2BAA6B,IAAA,EAAaC,EAAAA,oBAAAA,CAAoB,EAAI,CAAQ,CAC5E,CAAC,EAEK,GAAA,EAAWC,EAAAA,YAAAA,CAAY,CAAO,EAChC,EAAuB,CAAC,EAC5B,GAAI,CAMF,EAAS,GAAc,MAAA,EALDC,EAAAA,kBAAAA,CAAkB,CACtC,WACA,uBAAwB,EAAQ,iBAChC,kBAAA,EAAkBC,EAAAA,4BAAAA,CAA4B,CAAM,CAAC,EAAE,KAAK,GAAK,CAAC,CACpE,CAAC,EAAA,CAC8B,MAAM,CACvC,MAAQ,CAGR,CAEA,IAAM,EAAc,MAAA,EAAMC,EAAAA,QAAAA,CAAQ,EAC5B,EAAaC,EAAAA,eAAe,CAChC,SAAU,EAAW,EACrB,UAAW,EAAQ,UACnB,WAAY,EAAQ,iBAAiB,GAAK,CAAC,EAC3C,QAAS,EAAQ,QACjB,UACA,SACA,YAAa,EAAe,EAAU,EAAQ,QAAS,EAAQ,iBAAiB,EAChF,aACF,CAAC,EAIK,EAAa,KAAK,UAAU,CAAE,GAAG,EAAY,SAAU,CAAE,CAAC,EAChE,GAAI,IAAe,GAAa,EAAU,OAC1C,EAAY,EACZ,GAAY,EAGZ,IAAM,EAAY,EAAQ,gBAAgB,EACtC,IAAc,IAAA,IAAa,IAAc,IAAM,EAAQ,cAAgB,IAAA,KACzE,EAAkB,EAClB,EAAQ,YAAY,EAAW,EAAUC,EAAAA,mBAAmB,CAAE,UAAS,SAAQ,aAAY,CAAC,CAAC,GAE/F,EAAG,YAAYC,EAAAA,wBAAyB,CAAE,GAAG,EAAY,UAAS,CAAC,CACrE,EAiBA,MAAO,CACL,iBATyC,CACzC,GAAI,CACF,MAAM,EAAM,CACd,MAAQ,CAER,CACF,EAIE,YAAe,CACb,EAAW,GACP,IAAoB,IAAA,IAAW,EAAQ,eAAe,CAAe,EACzE,EAAkB,IAAA,EACpB,CACF,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{DOOM_CONTEXT_ENTRY_TYPE as
|
|
1
|
+
import{buildContextDetail as e}from"./contextDetail.mjs";import{DOOM_CONTEXT_ENTRY_TYPE as t,projectContext as n}from"./contextProjection.mjs";import{resolvePluginEntries as r}from"@agimon-ai/doompi-config/domains";import{extensionName as i,extensionPackageName as a,extensionToolSource as o}from"@agimon-ai/doompi-ui/extensionName";import{getHarnessState as s,harnessRoot as c}from"@agimon-ai/doompi-config/harnessStore";import{buildSkillCatalog as l,counter as u}from"@agimon-ai/doompi-skill/catalog";import{readDoomMcpStatus as d}from"@agimon-ai/doompi-extension-contracts/mcp-status";import{readDoomSkillSourcesService as f}from"@agimon-ai/doompi-extension-contracts/skills";import{buildToolSources as p}from"@agimon-ai/doompi-ui/toolInventory";function m(e){return e.flatMap(e=>e.owners.flatMap(e=>e.skills))}function h(e,t,n){let i={...s().packageAttribution};try{for(let a of r(e,[...t],[...n]))a.name&&a.domain&&(i[a.name]={kind:`domain`,mode:a.domain})}catch{}return i}function g(r,g,_={}){let v,y=0,b=!1,x,S=async()=>{if(b)return;let S=s(),C=d(g)?.getSnapshot().servers,w=p({tools:r.getAllTools(),activeTools:r.getActiveTools(),...C?{mcpServers:C}:{},resolveExtensionName:i,resolveExtensionPackageName:a,resolveExtensionToolSource:e=>o(r,e)}),T=c(S),E=[];try{E=m((await l({repoRoot:T,activeSkillDirectories:S.skillDirectories,extensionSources:f(g)?.list()??[]})).groups)}catch{}let D=await u(),O=n({revision:y+1,majorMode:S.majorMode,minorModes:_.readMinorModes?.()??[],domains:S.domains,sources:w,skills:E,attribution:h(T,S.domains,S.pluginDirectories),countTokens:D}),k=JSON.stringify({...O,revision:0});if(k===v||b)return;v=k,y+=1;let A=_.readSessionId?.();A!==void 0&&A!==``&&_.writeDetail!==void 0&&(x=A,_.writeDetail(A,y,e({sources:w,skills:E,countTokens:D}))),r.appendEntry(t,{...O,revision:y})};return{publish:async()=>{try{await S()}catch{}},dispose:()=>{b=!0,x!==void 0&&_.removeDetail?.(x),x=void 0}}}export{g as createContextPublisher};
|
|
2
2
|
//# sourceMappingURL=contextCatalog.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextCatalog.mjs","names":[],"sources":["../../../src/services/contextCatalog.ts"],"sourcesContent":["import { resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { getHarnessState, harnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport { readDoomMcpStatus } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport { readDoomSkillSourcesService } from '@agimon-ai/doompi-extension-contracts/skills';\nimport { buildSkillCatalog, counter, type SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { extensionName, extensionPackageName, extensionToolSource } from '@agimon-ai/doompi-ui/extensionName';\nimport { buildToolSources } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { DOOM_CONTEXT_ENTRY_TYPE, projectContext } from './contextProjection.ts';\n\n/**\n * Publishes what the session is composed of, and what it costs.\n *\n * Read at publish time rather than accumulated, because the answer changes for\n * reasons this module does not see: a reconnected MCP server, a domain switch,\n * a plan-mode tool swap. Reading the live inventory is cheap next to being\n * wrong about it.\n */\nexport interface ContextPublisher {\n /** Never rejects: callers fire this and forget it. */\n publish: () => Promise<void>;\n dispose: () => void;\n}\n\n/** Skills sit under owners, which is one level deeper than a flat list. */\nfunction flattenSkills(groups: Awaited<ReturnType<typeof buildSkillCatalog>>['groups']): SkillEntry[] {\n return groups.flatMap((group) => group.owners.flatMap((owner) => owner.skills));\n}\n\n/**\n * Package and plugin names to the mode that admitted them.\n *\n * Two halves meet here. Layer packages come from the harness, recorded when the\n * composition resolved. Domain plugins are re-resolved, because only the plugin\n * entry knows the domain that carried it.\n */\nfunction attributionFor(repoRoot: string, domains: readonly string[], pluginDirectories: readonly string[]) {\n const harness = getHarnessState();\n const attribution: Record<string, PackageAttribution> = { ...harness.packageAttribution };\n try {\n for (const entry of resolvePluginEntries(repoRoot, [...domains], [...pluginDirectories])) {\n if (entry.name && entry.domain) attribution[entry.name] = { kind: 'domain', mode: entry.domain };\n }\n } catch {\n // A domain that no longer resolves must not cost the reader the tool list.\n // Anything unmatched simply lands under core.\n }\n return attribution;\n}\n\nexport function createContextPublisher(pi: ExtensionAPI
|
|
1
|
+
{"version":3,"file":"contextCatalog.mjs","names":[],"sources":["../../../src/services/contextCatalog.ts"],"sourcesContent":["import { resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { getHarnessState, harnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport { readDoomMcpStatus } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport { readDoomSkillSourcesService } from '@agimon-ai/doompi-extension-contracts/skills';\nimport { buildSkillCatalog, counter, type SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { extensionName, extensionPackageName, extensionToolSource } from '@agimon-ai/doompi-ui/extensionName';\nimport { buildToolSources } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { ContextItemDetail } from '../types/contextApi.ts';\nimport { buildContextDetail } from './contextDetail.ts';\nimport { DOOM_CONTEXT_ENTRY_TYPE, projectContext } from './contextProjection.ts';\n\n/**\n * Publishes what the session is composed of, and what it costs.\n *\n * Read at publish time rather than accumulated, because the answer changes for\n * reasons this module does not see: a reconnected MCP server, a domain switch,\n * a plan-mode tool swap. Reading the live inventory is cheap next to being\n * wrong about it.\n */\nexport interface ContextPublisher {\n /** Never rejects: callers fire this and forget it. */\n publish: () => Promise<void>;\n dispose: () => void;\n}\n\n/** The active minor modes at publish time, read rather than remembered. */\nexport type ReadMinorModes = () => readonly { readonly id: string; readonly label: string }[];\n\nexport interface ContextPublisherOptions {\n readMinorModes?: ReadMinorModes;\n /**\n * The session the detail file is keyed by, read at publish time.\n *\n * Undefined until a session is bound, and undefined in a host that has no\n * session at all; the projection is still journaled either way, and only the\n * click-through detail goes unwritten.\n */\n readSessionId?: () => string | undefined;\n /**\n * Where the click-through detail is left for the session API to find.\n *\n * Injected because writing it is filesystem work and this is not the layer\n * that does filesystem work. A host that supplies neither still gets the\n * projection; only the detail behind a row goes unpublished.\n */\n writeDetail?: (sessionId: string, revision: number, items: readonly ContextItemDetail[]) => void;\n removeDetail?: (sessionId: string) => void;\n}\n\n/** Skills sit under owners, which is one level deeper than a flat list. */\nfunction flattenSkills(groups: Awaited<ReturnType<typeof buildSkillCatalog>>['groups']): SkillEntry[] {\n return groups.flatMap((group) => group.owners.flatMap((owner) => owner.skills));\n}\n\n/**\n * Package and plugin names to the mode that admitted them.\n *\n * Two halves meet here. Layer packages come from the harness, recorded when the\n * composition resolved. Domain plugins are re-resolved, because only the plugin\n * entry knows the domain that carried it.\n */\nfunction attributionFor(repoRoot: string, domains: readonly string[], pluginDirectories: readonly string[]) {\n const harness = getHarnessState();\n const attribution: Record<string, PackageAttribution> = { ...harness.packageAttribution };\n try {\n for (const entry of resolvePluginEntries(repoRoot, [...domains], [...pluginDirectories])) {\n if (entry.name && entry.domain) attribution[entry.name] = { kind: 'domain', mode: entry.domain };\n }\n } catch {\n // A domain that no longer resolves must not cost the reader the tool list.\n // Anything unmatched simply lands under core.\n }\n return attribution;\n}\n\nexport function createContextPublisher(\n pi: ExtensionAPI,\n cordis: Context,\n options: ContextPublisherOptions = {},\n): ContextPublisher {\n let published: string | undefined;\n let revision = 0;\n let disposed = false;\n /** Removed on disposal, so a session leaves nothing behind in the store. */\n let detailSessionId: string | undefined;\n\n const build = async (): Promise<void> => {\n if (disposed) return;\n const harness = getHarnessState();\n const mcpServers = readDoomMcpStatus(cordis)?.getSnapshot().servers;\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n resolveExtensionName: extensionName,\n resolveExtensionPackageName: extensionPackageName,\n resolveExtensionToolSource: (toolName) => extensionToolSource(pi, toolName),\n });\n\n const repoRoot = harnessRoot(harness);\n let skills: SkillEntry[] = [];\n try {\n const catalog = await buildSkillCatalog({\n repoRoot,\n activeSkillDirectories: harness.skillDirectories,\n extensionSources: readDoomSkillSourcesService(cordis)?.list() ?? [],\n });\n skills = flattenSkills(catalog.groups);\n } catch {\n // Tools are the larger cost and are already in hand; a skill walk that\n // fails should not take the whole figure down with it.\n }\n\n const countTokens = await counter();\n const projection = projectContext({\n revision: revision + 1,\n majorMode: harness.majorMode,\n minorModes: options.readMinorModes?.() ?? [],\n domains: harness.domains,\n sources,\n skills,\n attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),\n countTokens,\n });\n\n // Revision is compared out, so a republish that changed nothing is silent\n // rather than a new journal entry saying the same thing.\n const serialized = JSON.stringify({ ...projection, revision: 0 });\n if (serialized === published || disposed) return;\n published = serialized;\n revision += 1;\n // The detail lands before the entry that invites a reader to ask for it, so\n // a click that follows the panel's update cannot outrun the file behind it.\n const sessionId = options.readSessionId?.();\n if (sessionId !== undefined && sessionId !== '' && options.writeDetail !== undefined) {\n detailSessionId = sessionId;\n options.writeDetail(sessionId, revision, buildContextDetail({ sources, skills, countTokens }));\n }\n pi.appendEntry(DOOM_CONTEXT_ENTRY_TYPE, { ...projection, revision });\n };\n\n /**\n * Reporting the composition is a convenience, so it fails quietly.\n *\n * Callers publish without awaiting, and an escaping rejection would surface\n * as an unhandled error in a live session. A panel that cannot say what the\n * toolbox costs is a far smaller problem than that.\n */\n const publish = async (): Promise<void> => {\n try {\n await build();\n } catch {\n // Left unreported on purpose: the next composition change tries again.\n }\n };\n\n return {\n publish,\n dispose: () => {\n disposed = true;\n if (detailSessionId !== undefined) options.removeDetail?.(detailSessionId);\n detailSessionId = undefined;\n },\n };\n}\n"],"mappings":"6uBAqDA,SAAS,EAAc,EAA+E,CACpG,OAAO,EAAO,QAAS,GAAU,EAAM,OAAO,QAAS,GAAU,EAAM,MAAM,CAAC,CAChF,CASA,SAAS,EAAe,EAAkB,EAA4B,EAAsC,CAE1G,IAAM,EAAkD,CAAE,GAD1C,EACmD,CAAC,CAAC,kBAAmB,EACxF,GAAI,CACF,IAAK,IAAM,KAAS,EAAqB,EAAU,CAAC,GAAG,CAAO,EAAG,CAAC,GAAG,CAAiB,CAAC,EACjF,EAAM,MAAQ,EAAM,SAAQ,EAAY,EAAM,MAAQ,CAAE,KAAM,SAAU,KAAM,EAAM,MAAO,EAEnG,MAAQ,CAGR,CACA,OAAO,CACT,CAEA,SAAgB,EACd,EACA,EACA,EAAmC,CAAC,EAClB,CAClB,IAAI,EACA,EAAW,EACX,EAAW,GAEX,EAEE,EAAQ,SAA2B,CACvC,GAAI,EAAU,OACd,IAAM,EAAU,EAAgB,EAC1B,EAAa,EAAkB,CAAM,CAAC,EAAE,YAAY,CAAC,CAAC,QACtD,EAAU,EAAiB,CAC/B,MAAO,EAAG,YAAY,EACtB,YAAa,EAAG,eAAe,EAC/B,GAAI,EAAa,CAAE,YAAW,EAAI,CAAC,EACnC,qBAAsB,EACtB,4BAA6B,EAC7B,2BAA6B,GAAa,EAAoB,EAAI,CAAQ,CAC5E,CAAC,EAEK,EAAW,EAAY,CAAO,EAChC,EAAuB,CAAC,EAC5B,GAAI,CAMF,EAAS,GAAc,MALD,EAAkB,CACtC,WACA,uBAAwB,EAAQ,iBAChC,iBAAkB,EAA4B,CAAM,CAAC,EAAE,KAAK,GAAK,CAAC,CACpE,CAAC,EAAA,CAC8B,MAAM,CACvC,MAAQ,CAGR,CAEA,IAAM,EAAc,MAAM,EAAQ,EAC5B,EAAa,EAAe,CAChC,SAAU,EAAW,EACrB,UAAW,EAAQ,UACnB,WAAY,EAAQ,iBAAiB,GAAK,CAAC,EAC3C,QAAS,EAAQ,QACjB,UACA,SACA,YAAa,EAAe,EAAU,EAAQ,QAAS,EAAQ,iBAAiB,EAChF,aACF,CAAC,EAIK,EAAa,KAAK,UAAU,CAAE,GAAG,EAAY,SAAU,CAAE,CAAC,EAChE,GAAI,IAAe,GAAa,EAAU,OAC1C,EAAY,EACZ,GAAY,EAGZ,IAAM,EAAY,EAAQ,gBAAgB,EACtC,IAAc,IAAA,IAAa,IAAc,IAAM,EAAQ,cAAgB,IAAA,KACzE,EAAkB,EAClB,EAAQ,YAAY,EAAW,EAAU,EAAmB,CAAE,UAAS,SAAQ,aAAY,CAAC,CAAC,GAE/F,EAAG,YAAY,EAAyB,CAAE,GAAG,EAAY,UAAS,CAAC,CACrE,EAiBA,MAAO,CACL,iBATyC,CACzC,GAAI,CACF,MAAM,EAAM,CACd,MAAQ,CAER,CACF,EAIE,YAAe,CACb,EAAW,GACP,IAAoB,IAAA,IAAW,EAAQ,eAAe,CAAe,EACzE,EAAkB,IAAA,EACpB,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("@agimon-ai/doompi-ui/toolInventory");function t(e){return e.kind===`extension`?e.packageName??e.key:e.kind===`mcp`?e.key.slice(4):`pi`}function n(e){return e===`plugins`?`plugin`:e===`extensions`?`extension`:`core`}function r(r){let i=[];for(let n of r.sources){let a=t(n);for(let t of n.tools){let o=(0,e.tokensForTool)(t,r.countTokens),s={itemKind:`tool`,name:t.name,owner:a,source:n.kind===`core`?`core`:n.kind,active:t.active,tokens:{schemaTokens:o.schemaTokens,promptTokens:o.promptTokens,totalTokens:o.totalTokens},...t.description?{description:t.description}:{},...t.promptSnippet?{promptSnippet:t.promptSnippet}:{},...t.promptGuidelines?.length?{promptGuidelines:t.promptGuidelines}:{},...t.parameters===void 0?{}:{parameters:t.parameters}};i.push(s)}}for(let e of r.skills)i.push({itemKind:`skill`,name:e.name,owner:e.owner,source:n(e.group),active:!0,tokens:e.promptTokens??0,description:e.description,filePath:e.filePath,modelInvocable:e.modelInvocable});return i}exports.buildContextDetail=r;
|
|
2
|
+
//# sourceMappingURL=contextDetail.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextDetail.cjs","names":["tokensForTool"],"sources":["../../../src/services/contextDetail.ts"],"sourcesContent":["import type { SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { type CountTokens, type ToolSource, tokensForTool } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { ContextItemDetail, ContextToolDetail } from '../types/contextApi.ts';\n\n/**\n * The prose and schema behind the projection's figures.\n *\n * Built from the same inventory in the same pass, so a detail can never\n * describe a composition the panel is not showing. Nothing is grouped here:\n * the panel already knows which mode a row sits under, and a lookup by name is\n * all a click needs.\n */\n\nconst MCP_KEY_PREFIX = 'mcp:';\nconst CORE_OWNER = 'pi';\n\n/** The same join the projection makes, so a row and its detail agree on the owner. */\nfunction ownerOf(source: ToolSource): string {\n if (source.kind === 'extension') return source.packageName ?? source.key;\n if (source.kind === 'mcp') return source.key.slice(MCP_KEY_PREFIX.length);\n return CORE_OWNER;\n}\n\nfunction skillSource(group: SkillEntry['group']): ContextItemDetail['source'] {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nexport interface ContextDetailInput {\n readonly sources: readonly ToolSource[];\n readonly skills: readonly SkillEntry[];\n readonly countTokens: CountTokens;\n}\n\nexport function buildContextDetail(input: ContextDetailInput): ContextItemDetail[] {\n const items: ContextItemDetail[] = [];\n\n for (const source of input.sources) {\n const owner = ownerOf(source);\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const detail: ContextToolDetail = {\n itemKind: 'tool',\n name: tool.name,\n owner,\n source: source.kind === 'core' ? 'core' : source.kind,\n active: tool.active,\n tokens: { schemaTokens: cost.schemaTokens, promptTokens: cost.promptTokens, totalTokens: cost.totalTokens },\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {}),\n ...(tool.promptGuidelines?.length ? { promptGuidelines: tool.promptGuidelines } : {}),\n ...(tool.parameters === undefined ? {} : { parameters: tool.parameters }),\n };\n items.push(detail);\n }\n }\n\n for (const skill of input.skills) {\n items.push({\n itemKind: 'skill',\n name: skill.name,\n owner: skill.owner,\n source: skillSource(skill.group),\n active: true,\n tokens: skill.promptTokens ?? 0,\n description: skill.description,\n filePath: skill.filePath,\n modelInvocable: skill.modelInvocable,\n });\n }\n\n return items;\n}\n"],"mappings":"oDAiBA,SAAS,EAAQ,EAA4B,CAG3C,OAFI,EAAO,OAAS,YAAoB,EAAO,aAAe,EAAO,IACjE,EAAO,OAAS,MAAc,EAAO,IAAI,MAAM,CAAqB,EACjE,IACT,CAEA,SAAS,EAAY,EAAyD,CAG5E,OAFI,IAAU,UAAkB,SAC5B,IAAU,aAAqB,YAC5B,MACT,CAQA,SAAgB,EAAmB,EAAgD,CACjF,IAAM,EAA6B,CAAC,EAEpC,IAAK,IAAM,KAAU,EAAM,QAAS,CAClC,IAAM,EAAQ,EAAQ,CAAM,EAC5B,IAAK,IAAM,KAAQ,EAAO,MAAO,CAC/B,IAAM,GAAA,EAAOA,EAAAA,cAAAA,CAAc,EAAM,EAAM,WAAW,EAC5C,EAA4B,CAChC,SAAU,OACV,KAAM,EAAK,KACX,QACA,OAAQ,EAAO,OAAS,OAAS,OAAS,EAAO,KACjD,OAAQ,EAAK,OACb,OAAQ,CAAE,aAAc,EAAK,aAAc,aAAc,EAAK,aAAc,YAAa,EAAK,WAAY,EAC1G,GAAI,EAAK,YAAc,CAAE,YAAa,EAAK,WAAY,EAAI,CAAC,EAC5D,GAAI,EAAK,cAAgB,CAAE,cAAe,EAAK,aAAc,EAAI,CAAC,EAClE,GAAI,EAAK,kBAAkB,OAAS,CAAE,iBAAkB,EAAK,gBAAiB,EAAI,CAAC,EACnF,GAAI,EAAK,aAAe,IAAA,GAAY,CAAC,EAAI,CAAE,WAAY,EAAK,UAAW,CACzE,EACA,EAAM,KAAK,CAAM,CACnB,CACF,CAEA,IAAK,IAAM,KAAS,EAAM,OACxB,EAAM,KAAK,CACT,SAAU,QACV,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,OAAQ,EAAY,EAAM,KAAK,EAC/B,OAAQ,GACR,OAAQ,EAAM,cAAgB,EAC9B,YAAa,EAAM,YACnB,SAAU,EAAM,SAChB,eAAgB,EAAM,cACxB,CAAC,EAGH,OAAO,CACT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{tokensForTool as e}from"@agimon-ai/doompi-ui/toolInventory";function t(e){return e.kind===`extension`?e.packageName??e.key:e.kind===`mcp`?e.key.slice(4):`pi`}function n(e){return e===`plugins`?`plugin`:e===`extensions`?`extension`:`core`}function r(r){let i=[];for(let n of r.sources){let a=t(n);for(let t of n.tools){let o=e(t,r.countTokens),s={itemKind:`tool`,name:t.name,owner:a,source:n.kind===`core`?`core`:n.kind,active:t.active,tokens:{schemaTokens:o.schemaTokens,promptTokens:o.promptTokens,totalTokens:o.totalTokens},...t.description?{description:t.description}:{},...t.promptSnippet?{promptSnippet:t.promptSnippet}:{},...t.promptGuidelines?.length?{promptGuidelines:t.promptGuidelines}:{},...t.parameters===void 0?{}:{parameters:t.parameters}};i.push(s)}}for(let e of r.skills)i.push({itemKind:`skill`,name:e.name,owner:e.owner,source:n(e.group),active:!0,tokens:e.promptTokens??0,description:e.description,filePath:e.filePath,modelInvocable:e.modelInvocable});return i}export{r as buildContextDetail};
|
|
2
|
+
//# sourceMappingURL=contextDetail.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextDetail.mjs","names":[],"sources":["../../../src/services/contextDetail.ts"],"sourcesContent":["import type { SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { type CountTokens, type ToolSource, tokensForTool } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { ContextItemDetail, ContextToolDetail } from '../types/contextApi.ts';\n\n/**\n * The prose and schema behind the projection's figures.\n *\n * Built from the same inventory in the same pass, so a detail can never\n * describe a composition the panel is not showing. Nothing is grouped here:\n * the panel already knows which mode a row sits under, and a lookup by name is\n * all a click needs.\n */\n\nconst MCP_KEY_PREFIX = 'mcp:';\nconst CORE_OWNER = 'pi';\n\n/** The same join the projection makes, so a row and its detail agree on the owner. */\nfunction ownerOf(source: ToolSource): string {\n if (source.kind === 'extension') return source.packageName ?? source.key;\n if (source.kind === 'mcp') return source.key.slice(MCP_KEY_PREFIX.length);\n return CORE_OWNER;\n}\n\nfunction skillSource(group: SkillEntry['group']): ContextItemDetail['source'] {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nexport interface ContextDetailInput {\n readonly sources: readonly ToolSource[];\n readonly skills: readonly SkillEntry[];\n readonly countTokens: CountTokens;\n}\n\nexport function buildContextDetail(input: ContextDetailInput): ContextItemDetail[] {\n const items: ContextItemDetail[] = [];\n\n for (const source of input.sources) {\n const owner = ownerOf(source);\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const detail: ContextToolDetail = {\n itemKind: 'tool',\n name: tool.name,\n owner,\n source: source.kind === 'core' ? 'core' : source.kind,\n active: tool.active,\n tokens: { schemaTokens: cost.schemaTokens, promptTokens: cost.promptTokens, totalTokens: cost.totalTokens },\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {}),\n ...(tool.promptGuidelines?.length ? { promptGuidelines: tool.promptGuidelines } : {}),\n ...(tool.parameters === undefined ? {} : { parameters: tool.parameters }),\n };\n items.push(detail);\n }\n }\n\n for (const skill of input.skills) {\n items.push({\n itemKind: 'skill',\n name: skill.name,\n owner: skill.owner,\n source: skillSource(skill.group),\n active: true,\n tokens: skill.promptTokens ?? 0,\n description: skill.description,\n filePath: skill.filePath,\n modelInvocable: skill.modelInvocable,\n });\n }\n\n return items;\n}\n"],"mappings":"mEAiBA,SAAS,EAAQ,EAA4B,CAG3C,OAFI,EAAO,OAAS,YAAoB,EAAO,aAAe,EAAO,IACjE,EAAO,OAAS,MAAc,EAAO,IAAI,MAAM,CAAqB,EACjE,IACT,CAEA,SAAS,EAAY,EAAyD,CAG5E,OAFI,IAAU,UAAkB,SAC5B,IAAU,aAAqB,YAC5B,MACT,CAQA,SAAgB,EAAmB,EAAgD,CACjF,IAAM,EAA6B,CAAC,EAEpC,IAAK,IAAM,KAAU,EAAM,QAAS,CAClC,IAAM,EAAQ,EAAQ,CAAM,EAC5B,IAAK,IAAM,KAAQ,EAAO,MAAO,CAC/B,IAAM,EAAO,EAAc,EAAM,EAAM,WAAW,EAC5C,EAA4B,CAChC,SAAU,OACV,KAAM,EAAK,KACX,QACA,OAAQ,EAAO,OAAS,OAAS,OAAS,EAAO,KACjD,OAAQ,EAAK,OACb,OAAQ,CAAE,aAAc,EAAK,aAAc,aAAc,EAAK,aAAc,YAAa,EAAK,WAAY,EAC1G,GAAI,EAAK,YAAc,CAAE,YAAa,EAAK,WAAY,EAAI,CAAC,EAC5D,GAAI,EAAK,cAAgB,CAAE,cAAe,EAAK,aAAc,EAAI,CAAC,EAClE,GAAI,EAAK,kBAAkB,OAAS,CAAE,iBAAkB,EAAK,gBAAiB,EAAI,CAAC,EACnF,GAAI,EAAK,aAAe,IAAA,GAAY,CAAC,EAAI,CAAE,WAAY,EAAK,UAAW,CACzE,EACA,EAAM,KAAK,CAAM,CACnB,CACF,CAEA,IAAK,IAAM,KAAS,EAAM,OACxB,EAAM,KAAK,CACT,SAAU,QACV,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,OAAQ,EAAY,EAAM,KAAK,EAC/B,OAAQ,GACR,OAAQ,EAAM,cAAgB,EAC9B,YAAa,EAAM,YACnB,SAAU,EAAM,SAChB,eAAgB,EAAM,cACxB,CAAC,EAGH,OAAO,CACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require("@agimon-ai/doompi-ui/toolInventory");const t=`gpt-tokenizer`,n=`core`,r={major:0,minor:1,domain:2,core:3},i={extension:0,mcp:1,plugin:2,core:3};function a(e){return e===`plugins`?`plugin`:e===`extensions`?`extension`:`core`}function o(e,t){return i[e.source]-i[t.source]||e.name.localeCompare(t.name)}function s(i){let s=new Map,c=(e,t,n)=>{s.has(e)||s.set(e,{kind:n,label:t,items:[]})};i.majorMode&&c(i.majorMode,i.majorMode,`major`);for(let e of i.minorModes)c(e.id,e.label,`minor`);let l=(e,t)=>{let r=i.attribution[e],a=r?.mode??n;c(a,a,r?r.kind===`domain`?`domain`:`major`:`core`),s.get(a)?.items.push(t)};for(let t of i.sources){let r=t.kind===`extension`?t.packageName??t.key:t.kind===`mcp`?t.key.slice(4):`pi`;for(let a of t.tools){let o=(0,e.tokensForTool)(a,i.countTokens),u={name:a.name,itemKind:`tool`,source:t.kind===`core`?`core`:t.kind,owner:r,tokens:o.totalTokens,active:a.active};t.kind===`core`?(c(n,n,`core`),s.get(n)?.items.push(u)):l(r,u)}}for(let e of i.skills)l(e.owner,{name:e.name,itemKind:`skill`,source:a(e.group),owner:e.owner,tokens:e.promptTokens??0,active:!0});let u=[...s.entries()].map(([e,t])=>{let n=[...t.items].sort(o);return{id:e,label:t.label,kind:t.kind,items:n,tokens:n.reduce((e,t)=>e+(t.active?t.tokens:0),0),inactiveTokens:n.reduce((e,t)=>e+(t.active?0:t.tokens),0)}}).sort((e,t)=>r[e.kind]-r[t.kind]||e.label.localeCompare(t.label));return{version:1,revision:i.revision,groups:u,totalTokens:u.reduce((e,t)=>e+t.tokens,0),inactiveTokens:u.reduce((e,t)=>e+t.inactiveTokens,0),estimator:t}}exports.CONTEXT_PROJECTION_ESTIMATOR=t,exports.DOOM_CONTEXT_ENTRY_TYPE=`doom-context`,exports.projectContext=s;
|
|
1
|
+
let e=require("@agimon-ai/doompi-ui/toolInventory");const t=`gpt-tokenizer`,n=`core`,r={major:0,minor:1,domain:2,core:3},i={extension:0,mcp:1,plugin:2,core:3};function a(e){return e===`plugins`?`plugin`:e===`extensions`?`extension`:`core`}function o(e,t){return i[e.source]-i[t.source]||e.name.localeCompare(t.name)}function s(i){let s=new Map,c=(e,t,n)=>{s.has(e)||s.set(e,{kind:n,label:t,items:[]})};i.majorMode&&c(i.majorMode,i.majorMode,`major`);for(let e of i.minorModes)c(e.id,e.label,`minor`);for(let e of i.domains)c(e,e,`domain`);let l=(e,t)=>{let r=i.attribution[e],a=r?.mode??n;c(a,a,r?r.kind===`domain`?`domain`:`major`:`core`),s.get(a)?.items.push(t)};for(let t of i.sources){let r=t.kind===`extension`?t.packageName??t.key:t.kind===`mcp`?t.key.slice(4):`pi`;for(let a of t.tools){let o=(0,e.tokensForTool)(a,i.countTokens),u={name:a.name,itemKind:`tool`,source:t.kind===`core`?`core`:t.kind,owner:r,tokens:o.totalTokens,active:a.active};t.kind===`core`?(c(n,n,`core`),s.get(n)?.items.push(u)):l(r,u)}}for(let e of i.skills)l(e.owner,{name:e.name,itemKind:`skill`,source:a(e.group),owner:e.owner,tokens:e.promptTokens??0,active:!0});let u=[...s.entries()].map(([e,t])=>{let n=[...t.items].sort(o);return{id:e,label:t.label,kind:t.kind,items:n,tokens:n.reduce((e,t)=>e+(t.active?t.tokens:0),0),inactiveTokens:n.reduce((e,t)=>e+(t.active?0:t.tokens),0)}}).sort((e,t)=>r[e.kind]-r[t.kind]||e.label.localeCompare(t.label));return{version:1,revision:i.revision,groups:u,totalTokens:u.reduce((e,t)=>e+t.tokens,0),inactiveTokens:u.reduce((e,t)=>e+t.inactiveTokens,0),estimator:t}}exports.CONTEXT_PROJECTION_ESTIMATOR=t,exports.DOOM_CONTEXT_ENTRY_TYPE=`doom-context`,exports.projectContext=s;
|
|
2
2
|
//# sourceMappingURL=contextProjection.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextProjection.cjs","names":["tokensForTool"],"sources":["../../../src/services/contextProjection.ts"],"sourcesContent":["import type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport type { SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { type CountTokens, type ToolSource, tokensForTool } from '@agimon-ai/doompi-ui/toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n /** Active minor modes, in the order the catalog reports them. */\n readonly minorModes: readonly { readonly id: string; readonly label: string }[];\n readonly sources: readonly ToolSource[];\n readonly skills: readonly SkillEntry[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: SkillEntry['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const mode of input.minorModes) declare(mode.id, mode.label, 'minor');\n\n const place = (owner: string, item: ContextItemProjection): void => {\n const attributed = input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n declare(id, id, attributed ? (attributed.kind === 'domain' ? 'domain' : 'major') : 'core');\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(skill.owner, {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n });\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":"oDAkBA,MAGa,EAA+B,gBAMtC,EAAa,OAKb,EAA+C,CAAE,MAAO,EAAG,MAAO,EAAG,OAAQ,EAAG,KAAM,CAAE,EACxF,EAAkD,CAAE,UAAW,EAAG,IAAK,EAAG,OAAQ,EAAG,KAAM,CAAE,EAwDnG,SAAS,EAAY,EAA+C,CAGlE,OAFI,IAAU,UAAkB,SAC5B,IAAU,aAAqB,YAC5B,MACT,CAEA,SAAS,EAAM,EAA6B,EAAsC,CAChF,OAAO,EAAa,EAAK,QAAU,EAAa,EAAM,SAAW,EAAK,KAAK,cAAc,EAAM,IAAI,CACrG,CAEA,SAAgB,EAAe,EAAkD,CAC/E,IAAM,EAAU,IAAI,IAKd,GAAW,EAAY,EAAe,IAAiC,CACtE,EAAQ,IAAI,CAAE,GAAG,EAAQ,IAAI,EAAI,CAAE,OAAM,QAAO,MAAO,CAAC,CAAE,CAAC,CAClE,EACI,EAAM,WAAW,EAAQ,EAAM,UAAW,EAAM,UAAW,OAAO,EACtE,IAAK,IAAM,KAAQ,EAAM,WAAY,EAAQ,EAAK,GAAI,EAAK,MAAO,OAAO,EAEzE,IAAM,GAAS,EAAe,IAAsC,CAClE,IAAM,EAAa,EAAM,YAAY,GAC/B,EAAK,GAAY,MAAQ,EAC/B,EAAQ,EAAI,EAAI,EAAc,EAAW,OAAS,SAAW,SAAW,QAAW,MAAM,EACzF,EAAQ,IAAI,CAAE,CAAC,EAAE,MAAM,KAAK,CAAI,CAClC,EAEA,IAAK,IAAM,KAAU,EAAM,QAAS,CAIlC,IAAM,EACJ,EAAO,OAAS,YACX,EAAO,aAAe,EAAO,IAC9B,EAAO,OAAS,MACd,EAAO,IAAI,MAAM,CAAqB,EACtC,KACR,IAAK,IAAM,KAAQ,EAAO,MAAO,CAC/B,IAAM,GAAA,EAAOA,EAAAA,cAAAA,CAAc,EAAM,EAAM,WAAW,EAC5C,EAA8B,CAClC,KAAM,EAAK,KACX,SAAU,OACV,OAAQ,EAAO,OAAS,OAAS,OAAS,EAAO,KACjD,QACA,OAAQ,EAAK,YACb,OAAQ,EAAK,MACf,EACI,EAAO,OAAS,QAClB,EAAQ,EAAY,EAAY,MAAM,EACtC,EAAQ,IAAI,CAAU,CAAC,EAAE,MAAM,KAAK,CAAI,GAExC,EAAM,EAAO,CAAI,CAErB,CACF,CAEA,IAAK,IAAM,KAAS,EAAM,OACxB,EAAM,EAAM,MAAO,CACjB,KAAM,EAAM,KACZ,SAAU,QACV,OAAQ,EAAY,EAAM,KAAK,EAC/B,MAAO,EAAM,MACb,OAAQ,EAAM,cAAgB,EAG9B,OAAQ,EACV,CAAC,EAGH,IAAM,EAAmC,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,CAC5D,KAAK,CAAC,EAAI,KAAY,CACrB,IAAM,EAAQ,CAAC,GAAG,EAAO,KAAK,CAAC,CAAC,KAAK,CAAK,EAI1C,MAAO,CACL,KACA,MAAO,EAAO,MACd,KAAM,EAAO,KACb,QACA,OAAQ,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAK,OAAS,GAAI,CAAC,EAChF,eAAgB,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAI,EAAK,QAAS,CAAC,CAC1F,CACF,CAAC,CAAC,CACD,MAAM,EAAM,IAAU,EAAW,EAAK,MAAQ,EAAW,EAAM,OAAS,EAAK,MAAM,cAAc,EAAM,KAAK,CAAC,EAEhH,MAAO,CACL,QAAA,EACA,SAAU,EAAM,SAChB,SACA,YAAa,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,OAAQ,CAAC,EACpE,eAAgB,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,eAAgB,CAAC,EAC/E,UAAW,CACb,CACF"}
|
|
1
|
+
{"version":3,"file":"contextProjection.cjs","names":["tokensForTool"],"sources":["../../../src/services/contextProjection.ts"],"sourcesContent":["import type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport type { SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { type CountTokens, type ToolSource, tokensForTool } from '@agimon-ai/doompi-ui/toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n /** Active minor modes, in the order the catalog reports them. */\n readonly minorModes: readonly { readonly id: string; readonly label: string }[];\n /** Domains the composition resolved under, whether or not they carried a plugin. */\n readonly domains: readonly string[];\n readonly sources: readonly ToolSource[];\n readonly skills: readonly SkillEntry[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: SkillEntry['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const mode of input.minorModes) declare(mode.id, mode.label, 'minor');\n // A domain that contributed no plugin is still part of what the session is\n // running under, and a reader comparing the panel against the footer would\n // otherwise read its absence as the panel being out of date.\n for (const domain of input.domains) declare(domain, domain, 'domain');\n\n const place = (owner: string, item: ContextItemProjection): void => {\n const attributed = input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n declare(id, id, attributed ? (attributed.kind === 'domain' ? 'domain' : 'major') : 'core');\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(skill.owner, {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n });\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":"oDAkBA,MAGa,EAA+B,gBAMtC,EAAa,OAKb,EAA+C,CAAE,MAAO,EAAG,MAAO,EAAG,OAAQ,EAAG,KAAM,CAAE,EACxF,EAAkD,CAAE,UAAW,EAAG,IAAK,EAAG,OAAQ,EAAG,KAAM,CAAE,EA0DnG,SAAS,EAAY,EAA+C,CAGlE,OAFI,IAAU,UAAkB,SAC5B,IAAU,aAAqB,YAC5B,MACT,CAEA,SAAS,EAAM,EAA6B,EAAsC,CAChF,OAAO,EAAa,EAAK,QAAU,EAAa,EAAM,SAAW,EAAK,KAAK,cAAc,EAAM,IAAI,CACrG,CAEA,SAAgB,EAAe,EAAkD,CAC/E,IAAM,EAAU,IAAI,IAKd,GAAW,EAAY,EAAe,IAAiC,CACtE,EAAQ,IAAI,CAAE,GAAG,EAAQ,IAAI,EAAI,CAAE,OAAM,QAAO,MAAO,CAAC,CAAE,CAAC,CAClE,EACI,EAAM,WAAW,EAAQ,EAAM,UAAW,EAAM,UAAW,OAAO,EACtE,IAAK,IAAM,KAAQ,EAAM,WAAY,EAAQ,EAAK,GAAI,EAAK,MAAO,OAAO,EAIzE,IAAK,IAAM,KAAU,EAAM,QAAS,EAAQ,EAAQ,EAAQ,QAAQ,EAEpE,IAAM,GAAS,EAAe,IAAsC,CAClE,IAAM,EAAa,EAAM,YAAY,GAC/B,EAAK,GAAY,MAAQ,EAC/B,EAAQ,EAAI,EAAI,EAAc,EAAW,OAAS,SAAW,SAAW,QAAW,MAAM,EACzF,EAAQ,IAAI,CAAE,CAAC,EAAE,MAAM,KAAK,CAAI,CAClC,EAEA,IAAK,IAAM,KAAU,EAAM,QAAS,CAIlC,IAAM,EACJ,EAAO,OAAS,YACX,EAAO,aAAe,EAAO,IAC9B,EAAO,OAAS,MACd,EAAO,IAAI,MAAM,CAAqB,EACtC,KACR,IAAK,IAAM,KAAQ,EAAO,MAAO,CAC/B,IAAM,GAAA,EAAOA,EAAAA,cAAAA,CAAc,EAAM,EAAM,WAAW,EAC5C,EAA8B,CAClC,KAAM,EAAK,KACX,SAAU,OACV,OAAQ,EAAO,OAAS,OAAS,OAAS,EAAO,KACjD,QACA,OAAQ,EAAK,YACb,OAAQ,EAAK,MACf,EACI,EAAO,OAAS,QAClB,EAAQ,EAAY,EAAY,MAAM,EACtC,EAAQ,IAAI,CAAU,CAAC,EAAE,MAAM,KAAK,CAAI,GAExC,EAAM,EAAO,CAAI,CAErB,CACF,CAEA,IAAK,IAAM,KAAS,EAAM,OACxB,EAAM,EAAM,MAAO,CACjB,KAAM,EAAM,KACZ,SAAU,QACV,OAAQ,EAAY,EAAM,KAAK,EAC/B,MAAO,EAAM,MACb,OAAQ,EAAM,cAAgB,EAG9B,OAAQ,EACV,CAAC,EAGH,IAAM,EAAmC,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,CAC5D,KAAK,CAAC,EAAI,KAAY,CACrB,IAAM,EAAQ,CAAC,GAAG,EAAO,KAAK,CAAC,CAAC,KAAK,CAAK,EAI1C,MAAO,CACL,KACA,MAAO,EAAO,MACd,KAAM,EAAO,KACb,QACA,OAAQ,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAK,OAAS,GAAI,CAAC,EAChF,eAAgB,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAI,EAAK,QAAS,CAAC,CAC1F,CACF,CAAC,CAAC,CACD,MAAM,EAAM,IAAU,EAAW,EAAK,MAAQ,EAAW,EAAM,OAAS,EAAK,MAAM,cAAc,EAAM,KAAK,CAAC,EAEhH,MAAO,CACL,QAAA,EACA,SAAU,EAAM,SAChB,SACA,YAAa,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,OAAQ,CAAC,EACpE,eAAgB,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,eAAgB,CAAC,EAC/E,UAAW,CACb,CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PackageAttribution } from "@agimon-ai/doompi-config/types";
|
|
2
|
+
import "@agimon-ai/doompi-skill/catalog";
|
|
3
|
+
import "@agimon-ai/doompi-ui/toolInventory";
|
|
4
|
+
//#region src/services/contextProjection.d.ts
|
|
5
|
+
type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';
|
|
6
|
+
//#endregion
|
|
7
|
+
export { ContextItemSource };
|
|
8
|
+
//# sourceMappingURL=contextProjection.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextProjection.d.cts","names":[],"sources":["../../../src/services/contextProjection.ts"],"mappings":";;;;KAwBY"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "@agimon-ai/doompi-skill/catalog";
|
|
2
|
+
import "@agimon-ai/doompi-ui/toolInventory";
|
|
3
|
+
import { PackageAttribution } from "@agimon-ai/doompi-config/types";
|
|
4
|
+
//#region src/services/contextProjection.d.ts
|
|
5
|
+
type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';
|
|
6
|
+
//#endregion
|
|
7
|
+
export { ContextItemSource };
|
|
8
|
+
//# sourceMappingURL=contextProjection.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextProjection.d.mts","names":[],"sources":["../../../src/services/contextProjection.ts"],"mappings":";;;;KAwBY"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{tokensForTool as e}from"@agimon-ai/doompi-ui/toolInventory";const t=`doom-context`,n=`gpt-tokenizer`,r=`core`,i={major:0,minor:1,domain:2,core:3},a={extension:0,mcp:1,plugin:2,core:3};function o(e){return e===`plugins`?`plugin`:e===`extensions`?`extension`:`core`}function s(e,t){return a[e.source]-a[t.source]||e.name.localeCompare(t.name)}function c(t){let a=new Map,c=(e,t,n)=>{a.has(e)||a.set(e,{kind:n,label:t,items:[]})};t.majorMode&&c(t.majorMode,t.majorMode,`major`);for(let e of t.minorModes)c(e.id,e.label,`minor`);let l=(e,n)=>{let i=t.attribution[e],o=i?.mode??r;c(o,o,i?i.kind===`domain`?`domain`:`major`:`core`),a.get(o)?.items.push(n)};for(let n of t.sources){let i=n.kind===`extension`?n.packageName??n.key:n.kind===`mcp`?n.key.slice(4):`pi`;for(let o of n.tools){let s=e(o,t.countTokens),u={name:o.name,itemKind:`tool`,source:n.kind===`core`?`core`:n.kind,owner:i,tokens:s.totalTokens,active:o.active};n.kind===`core`?(c(r,r,`core`),a.get(r)?.items.push(u)):l(i,u)}}for(let e of t.skills)l(e.owner,{name:e.name,itemKind:`skill`,source:o(e.group),owner:e.owner,tokens:e.promptTokens??0,active:!0});let u=[...a.entries()].map(([e,t])=>{let n=[...t.items].sort(s);return{id:e,label:t.label,kind:t.kind,items:n,tokens:n.reduce((e,t)=>e+(t.active?t.tokens:0),0),inactiveTokens:n.reduce((e,t)=>e+(t.active?0:t.tokens),0)}}).sort((e,t)=>i[e.kind]-i[t.kind]||e.label.localeCompare(t.label));return{version:1,revision:t.revision,groups:u,totalTokens:u.reduce((e,t)=>e+t.tokens,0),inactiveTokens:u.reduce((e,t)=>e+t.inactiveTokens,0),estimator:n}}export{n as CONTEXT_PROJECTION_ESTIMATOR,t as DOOM_CONTEXT_ENTRY_TYPE,c as projectContext};
|
|
1
|
+
import{tokensForTool as e}from"@agimon-ai/doompi-ui/toolInventory";const t=`doom-context`,n=`gpt-tokenizer`,r=`core`,i={major:0,minor:1,domain:2,core:3},a={extension:0,mcp:1,plugin:2,core:3};function o(e){return e===`plugins`?`plugin`:e===`extensions`?`extension`:`core`}function s(e,t){return a[e.source]-a[t.source]||e.name.localeCompare(t.name)}function c(t){let a=new Map,c=(e,t,n)=>{a.has(e)||a.set(e,{kind:n,label:t,items:[]})};t.majorMode&&c(t.majorMode,t.majorMode,`major`);for(let e of t.minorModes)c(e.id,e.label,`minor`);for(let e of t.domains)c(e,e,`domain`);let l=(e,n)=>{let i=t.attribution[e],o=i?.mode??r;c(o,o,i?i.kind===`domain`?`domain`:`major`:`core`),a.get(o)?.items.push(n)};for(let n of t.sources){let i=n.kind===`extension`?n.packageName??n.key:n.kind===`mcp`?n.key.slice(4):`pi`;for(let o of n.tools){let s=e(o,t.countTokens),u={name:o.name,itemKind:`tool`,source:n.kind===`core`?`core`:n.kind,owner:i,tokens:s.totalTokens,active:o.active};n.kind===`core`?(c(r,r,`core`),a.get(r)?.items.push(u)):l(i,u)}}for(let e of t.skills)l(e.owner,{name:e.name,itemKind:`skill`,source:o(e.group),owner:e.owner,tokens:e.promptTokens??0,active:!0});let u=[...a.entries()].map(([e,t])=>{let n=[...t.items].sort(s);return{id:e,label:t.label,kind:t.kind,items:n,tokens:n.reduce((e,t)=>e+(t.active?t.tokens:0),0),inactiveTokens:n.reduce((e,t)=>e+(t.active?0:t.tokens),0)}}).sort((e,t)=>i[e.kind]-i[t.kind]||e.label.localeCompare(t.label));return{version:1,revision:t.revision,groups:u,totalTokens:u.reduce((e,t)=>e+t.tokens,0),inactiveTokens:u.reduce((e,t)=>e+t.inactiveTokens,0),estimator:n}}export{n as CONTEXT_PROJECTION_ESTIMATOR,t as DOOM_CONTEXT_ENTRY_TYPE,c as projectContext};
|
|
2
2
|
//# sourceMappingURL=contextProjection.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextProjection.mjs","names":[],"sources":["../../../src/services/contextProjection.ts"],"sourcesContent":["import type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport type { SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { type CountTokens, type ToolSource, tokensForTool } from '@agimon-ai/doompi-ui/toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n /** Active minor modes, in the order the catalog reports them. */\n readonly minorModes: readonly { readonly id: string; readonly label: string }[];\n readonly sources: readonly ToolSource[];\n readonly skills: readonly SkillEntry[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: SkillEntry['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const mode of input.minorModes) declare(mode.id, mode.label, 'minor');\n\n const place = (owner: string, item: ContextItemProjection): void => {\n const attributed = input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n declare(id, id, attributed ? (attributed.kind === 'domain' ? 'domain' : 'major') : 'core');\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(skill.owner, {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n });\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":"mEAkBA,MAAa,EAA0B,eAG1B,EAA+B,gBAMtC,EAAa,OAKb,EAA+C,CAAE,MAAO,EAAG,MAAO,EAAG,OAAQ,EAAG,KAAM,CAAE,EACxF,EAAkD,CAAE,UAAW,EAAG,IAAK,EAAG,OAAQ,EAAG,KAAM,CAAE,EAwDnG,SAAS,EAAY,EAA+C,CAGlE,OAFI,IAAU,UAAkB,SAC5B,IAAU,aAAqB,YAC5B,MACT,CAEA,SAAS,EAAM,EAA6B,EAAsC,CAChF,OAAO,EAAa,EAAK,QAAU,EAAa,EAAM,SAAW,EAAK,KAAK,cAAc,EAAM,IAAI,CACrG,CAEA,SAAgB,EAAe,EAAkD,CAC/E,IAAM,EAAU,IAAI,IAKd,GAAW,EAAY,EAAe,IAAiC,CACtE,EAAQ,IAAI,CAAE,GAAG,EAAQ,IAAI,EAAI,CAAE,OAAM,QAAO,MAAO,CAAC,CAAE,CAAC,CAClE,EACI,EAAM,WAAW,EAAQ,EAAM,UAAW,EAAM,UAAW,OAAO,EACtE,IAAK,IAAM,KAAQ,EAAM,WAAY,EAAQ,EAAK,GAAI,EAAK,MAAO,OAAO,EAEzE,IAAM,GAAS,EAAe,IAAsC,CAClE,IAAM,EAAa,EAAM,YAAY,GAC/B,EAAK,GAAY,MAAQ,EAC/B,EAAQ,EAAI,EAAI,EAAc,EAAW,OAAS,SAAW,SAAW,QAAW,MAAM,EACzF,EAAQ,IAAI,CAAE,CAAC,EAAE,MAAM,KAAK,CAAI,CAClC,EAEA,IAAK,IAAM,KAAU,EAAM,QAAS,CAIlC,IAAM,EACJ,EAAO,OAAS,YACX,EAAO,aAAe,EAAO,IAC9B,EAAO,OAAS,MACd,EAAO,IAAI,MAAM,CAAqB,EACtC,KACR,IAAK,IAAM,KAAQ,EAAO,MAAO,CAC/B,IAAM,EAAO,EAAc,EAAM,EAAM,WAAW,EAC5C,EAA8B,CAClC,KAAM,EAAK,KACX,SAAU,OACV,OAAQ,EAAO,OAAS,OAAS,OAAS,EAAO,KACjD,QACA,OAAQ,EAAK,YACb,OAAQ,EAAK,MACf,EACI,EAAO,OAAS,QAClB,EAAQ,EAAY,EAAY,MAAM,EACtC,EAAQ,IAAI,CAAU,CAAC,EAAE,MAAM,KAAK,CAAI,GAExC,EAAM,EAAO,CAAI,CAErB,CACF,CAEA,IAAK,IAAM,KAAS,EAAM,OACxB,EAAM,EAAM,MAAO,CACjB,KAAM,EAAM,KACZ,SAAU,QACV,OAAQ,EAAY,EAAM,KAAK,EAC/B,MAAO,EAAM,MACb,OAAQ,EAAM,cAAgB,EAG9B,OAAQ,EACV,CAAC,EAGH,IAAM,EAAmC,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,CAC5D,KAAK,CAAC,EAAI,KAAY,CACrB,IAAM,EAAQ,CAAC,GAAG,EAAO,KAAK,CAAC,CAAC,KAAK,CAAK,EAI1C,MAAO,CACL,KACA,MAAO,EAAO,MACd,KAAM,EAAO,KACb,QACA,OAAQ,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAK,OAAS,GAAI,CAAC,EAChF,eAAgB,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAI,EAAK,QAAS,CAAC,CAC1F,CACF,CAAC,CAAC,CACD,MAAM,EAAM,IAAU,EAAW,EAAK,MAAQ,EAAW,EAAM,OAAS,EAAK,MAAM,cAAc,EAAM,KAAK,CAAC,EAEhH,MAAO,CACL,QAAA,EACA,SAAU,EAAM,SAChB,SACA,YAAa,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,OAAQ,CAAC,EACpE,eAAgB,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,eAAgB,CAAC,EAC/E,UAAW,CACb,CACF"}
|
|
1
|
+
{"version":3,"file":"contextProjection.mjs","names":[],"sources":["../../../src/services/contextProjection.ts"],"sourcesContent":["import type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport type { SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { type CountTokens, type ToolSource, tokensForTool } from '@agimon-ai/doompi-ui/toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n /** Active minor modes, in the order the catalog reports them. */\n readonly minorModes: readonly { readonly id: string; readonly label: string }[];\n /** Domains the composition resolved under, whether or not they carried a plugin. */\n readonly domains: readonly string[];\n readonly sources: readonly ToolSource[];\n readonly skills: readonly SkillEntry[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: SkillEntry['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const mode of input.minorModes) declare(mode.id, mode.label, 'minor');\n // A domain that contributed no plugin is still part of what the session is\n // running under, and a reader comparing the panel against the footer would\n // otherwise read its absence as the panel being out of date.\n for (const domain of input.domains) declare(domain, domain, 'domain');\n\n const place = (owner: string, item: ContextItemProjection): void => {\n const attributed = input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n declare(id, id, attributed ? (attributed.kind === 'domain' ? 'domain' : 'major') : 'core');\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(skill.owner, {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n });\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":"mEAkBA,MAAa,EAA0B,eAG1B,EAA+B,gBAMtC,EAAa,OAKb,EAA+C,CAAE,MAAO,EAAG,MAAO,EAAG,OAAQ,EAAG,KAAM,CAAE,EACxF,EAAkD,CAAE,UAAW,EAAG,IAAK,EAAG,OAAQ,EAAG,KAAM,CAAE,EA0DnG,SAAS,EAAY,EAA+C,CAGlE,OAFI,IAAU,UAAkB,SAC5B,IAAU,aAAqB,YAC5B,MACT,CAEA,SAAS,EAAM,EAA6B,EAAsC,CAChF,OAAO,EAAa,EAAK,QAAU,EAAa,EAAM,SAAW,EAAK,KAAK,cAAc,EAAM,IAAI,CACrG,CAEA,SAAgB,EAAe,EAAkD,CAC/E,IAAM,EAAU,IAAI,IAKd,GAAW,EAAY,EAAe,IAAiC,CACtE,EAAQ,IAAI,CAAE,GAAG,EAAQ,IAAI,EAAI,CAAE,OAAM,QAAO,MAAO,CAAC,CAAE,CAAC,CAClE,EACI,EAAM,WAAW,EAAQ,EAAM,UAAW,EAAM,UAAW,OAAO,EACtE,IAAK,IAAM,KAAQ,EAAM,WAAY,EAAQ,EAAK,GAAI,EAAK,MAAO,OAAO,EAIzE,IAAK,IAAM,KAAU,EAAM,QAAS,EAAQ,EAAQ,EAAQ,QAAQ,EAEpE,IAAM,GAAS,EAAe,IAAsC,CAClE,IAAM,EAAa,EAAM,YAAY,GAC/B,EAAK,GAAY,MAAQ,EAC/B,EAAQ,EAAI,EAAI,EAAc,EAAW,OAAS,SAAW,SAAW,QAAW,MAAM,EACzF,EAAQ,IAAI,CAAE,CAAC,EAAE,MAAM,KAAK,CAAI,CAClC,EAEA,IAAK,IAAM,KAAU,EAAM,QAAS,CAIlC,IAAM,EACJ,EAAO,OAAS,YACX,EAAO,aAAe,EAAO,IAC9B,EAAO,OAAS,MACd,EAAO,IAAI,MAAM,CAAqB,EACtC,KACR,IAAK,IAAM,KAAQ,EAAO,MAAO,CAC/B,IAAM,EAAO,EAAc,EAAM,EAAM,WAAW,EAC5C,EAA8B,CAClC,KAAM,EAAK,KACX,SAAU,OACV,OAAQ,EAAO,OAAS,OAAS,OAAS,EAAO,KACjD,QACA,OAAQ,EAAK,YACb,OAAQ,EAAK,MACf,EACI,EAAO,OAAS,QAClB,EAAQ,EAAY,EAAY,MAAM,EACtC,EAAQ,IAAI,CAAU,CAAC,EAAE,MAAM,KAAK,CAAI,GAExC,EAAM,EAAO,CAAI,CAErB,CACF,CAEA,IAAK,IAAM,KAAS,EAAM,OACxB,EAAM,EAAM,MAAO,CACjB,KAAM,EAAM,KACZ,SAAU,QACV,OAAQ,EAAY,EAAM,KAAK,EAC/B,MAAO,EAAM,MACb,OAAQ,EAAM,cAAgB,EAG9B,OAAQ,EACV,CAAC,EAGH,IAAM,EAAmC,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,CAC5D,KAAK,CAAC,EAAI,KAAY,CACrB,IAAM,EAAQ,CAAC,GAAG,EAAO,KAAK,CAAC,CAAC,KAAK,CAAK,EAI1C,MAAO,CACL,KACA,MAAO,EAAO,MACd,KAAM,EAAO,KACb,QACA,OAAQ,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAK,OAAS,GAAI,CAAC,EAChF,eAAgB,EAAM,QAAQ,EAAO,IAAS,GAAS,EAAK,OAAS,EAAI,EAAK,QAAS,CAAC,CAC1F,CACF,CAAC,CAAC,CACD,MAAM,EAAM,IAAU,EAAW,EAAK,MAAQ,EAAW,EAAM,OAAS,EAAK,MAAM,cAAc,EAAM,KAAK,CAAC,EAEhH,MAAO,CACL,QAAA,EACA,SAAU,EAAM,SAChB,SACA,YAAa,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,OAAQ,CAAC,EACpE,eAAgB,EAAO,QAAQ,EAAO,IAAU,EAAQ,EAAM,eAAgB,CAAC,EAC/E,UAAW,CACb,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`context`,t=`session`,n=`/item`,r=`name`,i=`kind`;function a(a,o,s){let c=new URLSearchParams({[t]:a,[i]:o,[r]:s});return`/api/plugin/${e}${n}?${c.toString()}`}exports.API_BASE_PATH=e,exports.CONTEXT_DETAIL_VERSION=1,exports.ITEM_ROUTE=n,exports.KIND_QUERY_PARAM=i,exports.NAME_QUERY_PARAM=r,exports.SESSION_QUERY_PARAM=t,exports.itemDetailUrl=a;
|
|
2
|
+
//# sourceMappingURL=contextApi.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.cjs","names":[],"sources":["../../../src/types/contextApi.ts"],"sourcesContent":["import type { ContextItemSource } from '../services/contextProjection.ts';\n\n/**\n * What one entry in the composition actually is, for a reader who clicked it.\n *\n * The projection prices the composition and stops there: names, owners, and\n * integer counts, small enough to republish whenever the composition changes.\n * The prose and the schema behind a figure are an order of magnitude larger and\n * are wanted one row at a time, so they travel on request instead. This module\n * is the contract both ends of that request share, and it is deliberately free\n * of Node imports so a browser can hold it too.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugin/. */\nexport const API_BASE_PATH = 'context';\n\n/** The session a proxied request belongs to; the hub strips it before forwarding. */\nexport const SESSION_QUERY_PARAM = 'session';\n\nexport const ITEM_ROUTE = '/item';\nexport const NAME_QUERY_PARAM = 'name';\nexport const KIND_QUERY_PARAM = 'kind';\n\nexport type ContextItemKind = 'tool' | 'skill';\n\n/** What a tool costs, split the way it is actually paid. */\nexport interface ContextTokenBreakdown {\n /** The JSON schema in the tool list. */\n readonly schemaTokens: number;\n /** The prose Pi folds into the system prompt. */\n readonly promptTokens: number;\n readonly totalTokens: number;\n}\n\nexport interface ContextToolDetail {\n readonly itemKind: 'tool';\n readonly name: string;\n readonly owner: string;\n readonly source: ContextItemSource;\n readonly active: boolean;\n readonly tokens: ContextTokenBreakdown;\n readonly description?: string;\n /** The one-line snippet Pi keys by tool name in the system prompt. */\n readonly promptSnippet?: string;\n readonly promptGuidelines?: readonly string[];\n /** The parameter schema as the model receives it. */\n readonly parameters?: unknown;\n}\n\nexport interface ContextSkillDetail {\n readonly itemKind: 'skill';\n readonly name: string;\n readonly owner: string;\n readonly source: ContextItemSource;\n readonly active: true;\n /** A skill has no schema half, so the figure is the prompt cost alone. */\n readonly tokens: number;\n readonly description: string;\n readonly filePath: string;\n /** Whether the model may invoke it, as opposed to a human running it. */\n readonly modelInvocable: boolean;\n}\n\nexport type ContextItemDetail = ContextToolDetail | ContextSkillDetail;\n\n/** The file the agent writes and the session API reads back. */\nexport interface ContextDetailFile {\n readonly version: 1;\n /** Matches the projection revision the panel is rendering. */\n readonly revision: number;\n readonly items: readonly ContextItemDetail[];\n}\n\nexport const CONTEXT_DETAIL_VERSION = 1;\n\n/** The absolute URL a page reads one row's detail through. */\nexport function itemDetailUrl(sessionId: string, itemKind: ContextItemKind, name: string): string {\n const search = new URLSearchParams({\n [SESSION_QUERY_PARAM]: sessionId,\n [KIND_QUERY_PARAM]: itemKind,\n [NAME_QUERY_PARAM]: name,\n });\n return `/api/plugin/${API_BASE_PATH}${ITEM_ROUTE}?${search.toString()}`;\n}\n"],"mappings":"AAcA,MAAa,EAAgB,UAGhB,EAAsB,UAEtB,EAAa,QACb,EAAmB,OACnB,EAAmB,OAuDhC,SAAgB,EAAc,EAAmB,EAA2B,EAAsB,CAChG,IAAM,EAAS,IAAI,gBAAgB,EAChC,GAAsB,GACtB,GAAmB,GACnB,GAAmB,CACtB,CAAC,EACD,MAAO,eAAe,IAAgB,EAAW,GAAG,EAAO,SAAS,GACtE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ContextItemSource } from "../services/contextProjection.cjs";
|
|
2
|
+
//#region src/types/contextApi.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* What one entry in the composition actually is, for a reader who clicked it.
|
|
5
|
+
*
|
|
6
|
+
* The projection prices the composition and stops there: names, owners, and
|
|
7
|
+
* integer counts, small enough to republish whenever the composition changes.
|
|
8
|
+
* The prose and the schema behind a figure are an order of magnitude larger and
|
|
9
|
+
* are wanted one row at a time, so they travel on request instead. This module
|
|
10
|
+
* is the contract both ends of that request share, and it is deliberately free
|
|
11
|
+
* of Node imports so a browser can hold it too.
|
|
12
|
+
*/
|
|
13
|
+
/** Where a host mounts this package's API; the segment after /api/plugin/. */
|
|
14
|
+
declare const API_BASE_PATH = "context";
|
|
15
|
+
/** The session a proxied request belongs to; the hub strips it before forwarding. */
|
|
16
|
+
declare const SESSION_QUERY_PARAM = "session";
|
|
17
|
+
declare const ITEM_ROUTE = "/item";
|
|
18
|
+
declare const NAME_QUERY_PARAM = "name";
|
|
19
|
+
declare const KIND_QUERY_PARAM = "kind";
|
|
20
|
+
type ContextItemKind = 'tool' | 'skill';
|
|
21
|
+
/** What a tool costs, split the way it is actually paid. */
|
|
22
|
+
interface ContextTokenBreakdown {
|
|
23
|
+
/** The JSON schema in the tool list. */
|
|
24
|
+
readonly schemaTokens: number;
|
|
25
|
+
/** The prose Pi folds into the system prompt. */
|
|
26
|
+
readonly promptTokens: number;
|
|
27
|
+
readonly totalTokens: number;
|
|
28
|
+
}
|
|
29
|
+
interface ContextToolDetail {
|
|
30
|
+
readonly itemKind: 'tool';
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly owner: string;
|
|
33
|
+
readonly source: ContextItemSource;
|
|
34
|
+
readonly active: boolean;
|
|
35
|
+
readonly tokens: ContextTokenBreakdown;
|
|
36
|
+
readonly description?: string;
|
|
37
|
+
/** The one-line snippet Pi keys by tool name in the system prompt. */
|
|
38
|
+
readonly promptSnippet?: string;
|
|
39
|
+
readonly promptGuidelines?: readonly string[];
|
|
40
|
+
/** The parameter schema as the model receives it. */
|
|
41
|
+
readonly parameters?: unknown;
|
|
42
|
+
}
|
|
43
|
+
interface ContextSkillDetail {
|
|
44
|
+
readonly itemKind: 'skill';
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly owner: string;
|
|
47
|
+
readonly source: ContextItemSource;
|
|
48
|
+
readonly active: true;
|
|
49
|
+
/** A skill has no schema half, so the figure is the prompt cost alone. */
|
|
50
|
+
readonly tokens: number;
|
|
51
|
+
readonly description: string;
|
|
52
|
+
readonly filePath: string;
|
|
53
|
+
/** Whether the model may invoke it, as opposed to a human running it. */
|
|
54
|
+
readonly modelInvocable: boolean;
|
|
55
|
+
}
|
|
56
|
+
type ContextItemDetail = ContextToolDetail | ContextSkillDetail;
|
|
57
|
+
/** The file the agent writes and the session API reads back. */
|
|
58
|
+
interface ContextDetailFile {
|
|
59
|
+
readonly version: 1;
|
|
60
|
+
/** Matches the projection revision the panel is rendering. */
|
|
61
|
+
readonly revision: number;
|
|
62
|
+
readonly items: readonly ContextItemDetail[];
|
|
63
|
+
}
|
|
64
|
+
declare const CONTEXT_DETAIL_VERSION = 1;
|
|
65
|
+
/** The absolute URL a page reads one row's detail through. */
|
|
66
|
+
declare function itemDetailUrl(sessionId: string, itemKind: ContextItemKind, name: string): string;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { API_BASE_PATH, CONTEXT_DETAIL_VERSION, ContextDetailFile, ContextItemDetail, ContextItemKind, ContextSkillDetail, ContextTokenBreakdown, ContextToolDetail, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM, SESSION_QUERY_PARAM, itemDetailUrl };
|
|
69
|
+
//# sourceMappingURL=contextApi.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.d.cts","names":[],"sources":["../../../src/types/contextApi.ts"],"mappings":";;;;;;;;;;;;;cAca;;cAGA;cAEA;cACA;cACA;KAED;;UAGK;;WAEN;;WAEA;WACA;;UAGM;WACN;WACA;WACA;WACA,QAAQ;WACR;WACA,QAAQ;WACR;;WAEA;WACA;;WAEA;;UAGM;WACN;WACA;WACA;WACA,QAAQ;WACR;;WAEA;WACA;WACA;;WAEA;;KAGC,oBAAoB,oBAAoB;;UAGnC;WACN;;WAEA;WACA,gBAAgB;;cAGd;;iBAGG,cAAc,mBAAmB,UAAU,iBAAiB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ContextItemSource } from "../services/contextProjection.mjs";
|
|
2
|
+
//#region src/types/contextApi.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* What one entry in the composition actually is, for a reader who clicked it.
|
|
5
|
+
*
|
|
6
|
+
* The projection prices the composition and stops there: names, owners, and
|
|
7
|
+
* integer counts, small enough to republish whenever the composition changes.
|
|
8
|
+
* The prose and the schema behind a figure are an order of magnitude larger and
|
|
9
|
+
* are wanted one row at a time, so they travel on request instead. This module
|
|
10
|
+
* is the contract both ends of that request share, and it is deliberately free
|
|
11
|
+
* of Node imports so a browser can hold it too.
|
|
12
|
+
*/
|
|
13
|
+
/** Where a host mounts this package's API; the segment after /api/plugin/. */
|
|
14
|
+
declare const API_BASE_PATH = "context";
|
|
15
|
+
/** The session a proxied request belongs to; the hub strips it before forwarding. */
|
|
16
|
+
declare const SESSION_QUERY_PARAM = "session";
|
|
17
|
+
declare const ITEM_ROUTE = "/item";
|
|
18
|
+
declare const NAME_QUERY_PARAM = "name";
|
|
19
|
+
declare const KIND_QUERY_PARAM = "kind";
|
|
20
|
+
type ContextItemKind = 'tool' | 'skill';
|
|
21
|
+
/** What a tool costs, split the way it is actually paid. */
|
|
22
|
+
interface ContextTokenBreakdown {
|
|
23
|
+
/** The JSON schema in the tool list. */
|
|
24
|
+
readonly schemaTokens: number;
|
|
25
|
+
/** The prose Pi folds into the system prompt. */
|
|
26
|
+
readonly promptTokens: number;
|
|
27
|
+
readonly totalTokens: number;
|
|
28
|
+
}
|
|
29
|
+
interface ContextToolDetail {
|
|
30
|
+
readonly itemKind: 'tool';
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly owner: string;
|
|
33
|
+
readonly source: ContextItemSource;
|
|
34
|
+
readonly active: boolean;
|
|
35
|
+
readonly tokens: ContextTokenBreakdown;
|
|
36
|
+
readonly description?: string;
|
|
37
|
+
/** The one-line snippet Pi keys by tool name in the system prompt. */
|
|
38
|
+
readonly promptSnippet?: string;
|
|
39
|
+
readonly promptGuidelines?: readonly string[];
|
|
40
|
+
/** The parameter schema as the model receives it. */
|
|
41
|
+
readonly parameters?: unknown;
|
|
42
|
+
}
|
|
43
|
+
interface ContextSkillDetail {
|
|
44
|
+
readonly itemKind: 'skill';
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly owner: string;
|
|
47
|
+
readonly source: ContextItemSource;
|
|
48
|
+
readonly active: true;
|
|
49
|
+
/** A skill has no schema half, so the figure is the prompt cost alone. */
|
|
50
|
+
readonly tokens: number;
|
|
51
|
+
readonly description: string;
|
|
52
|
+
readonly filePath: string;
|
|
53
|
+
/** Whether the model may invoke it, as opposed to a human running it. */
|
|
54
|
+
readonly modelInvocable: boolean;
|
|
55
|
+
}
|
|
56
|
+
type ContextItemDetail = ContextToolDetail | ContextSkillDetail;
|
|
57
|
+
/** The file the agent writes and the session API reads back. */
|
|
58
|
+
interface ContextDetailFile {
|
|
59
|
+
readonly version: 1;
|
|
60
|
+
/** Matches the projection revision the panel is rendering. */
|
|
61
|
+
readonly revision: number;
|
|
62
|
+
readonly items: readonly ContextItemDetail[];
|
|
63
|
+
}
|
|
64
|
+
declare const CONTEXT_DETAIL_VERSION = 1;
|
|
65
|
+
/** The absolute URL a page reads one row's detail through. */
|
|
66
|
+
declare function itemDetailUrl(sessionId: string, itemKind: ContextItemKind, name: string): string;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { API_BASE_PATH, CONTEXT_DETAIL_VERSION, ContextDetailFile, ContextItemDetail, ContextItemKind, ContextSkillDetail, ContextTokenBreakdown, ContextToolDetail, ITEM_ROUTE, KIND_QUERY_PARAM, NAME_QUERY_PARAM, SESSION_QUERY_PARAM, itemDetailUrl };
|
|
69
|
+
//# sourceMappingURL=contextApi.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.d.mts","names":[],"sources":["../../../src/types/contextApi.ts"],"mappings":";;;;;;;;;;;;;cAca;;cAGA;cAEA;cACA;cACA;KAED;;UAGK;;WAEN;;WAEA;WACA;;UAGM;WACN;WACA;WACA;WACA,QAAQ;WACR;WACA,QAAQ;WACR;;WAEA;WACA;;WAEA;;UAGM;WACN;WACA;WACA;WACA,QAAQ;WACR;;WAEA;WACA;WACA;;WAEA;;KAGC,oBAAoB,oBAAoB;;UAGnC;WACN;;WAEA;WACA,gBAAgB;;cAGd;;iBAGG,cAAc,mBAAmB,UAAU,iBAAiB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`context`,t=`session`,n=`/item`,r=`name`,i=`kind`,a=1;function o(a,o,s){let c=new URLSearchParams({[t]:a,[i]:o,[r]:s});return`/api/plugin/${e}${n}?${c.toString()}`}export{e as API_BASE_PATH,a as CONTEXT_DETAIL_VERSION,n as ITEM_ROUTE,i as KIND_QUERY_PARAM,r as NAME_QUERY_PARAM,t as SESSION_QUERY_PARAM,o as itemDetailUrl};
|
|
2
|
+
//# sourceMappingURL=contextApi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextApi.mjs","names":[],"sources":["../../../src/types/contextApi.ts"],"sourcesContent":["import type { ContextItemSource } from '../services/contextProjection.ts';\n\n/**\n * What one entry in the composition actually is, for a reader who clicked it.\n *\n * The projection prices the composition and stops there: names, owners, and\n * integer counts, small enough to republish whenever the composition changes.\n * The prose and the schema behind a figure are an order of magnitude larger and\n * are wanted one row at a time, so they travel on request instead. This module\n * is the contract both ends of that request share, and it is deliberately free\n * of Node imports so a browser can hold it too.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugin/. */\nexport const API_BASE_PATH = 'context';\n\n/** The session a proxied request belongs to; the hub strips it before forwarding. */\nexport const SESSION_QUERY_PARAM = 'session';\n\nexport const ITEM_ROUTE = '/item';\nexport const NAME_QUERY_PARAM = 'name';\nexport const KIND_QUERY_PARAM = 'kind';\n\nexport type ContextItemKind = 'tool' | 'skill';\n\n/** What a tool costs, split the way it is actually paid. */\nexport interface ContextTokenBreakdown {\n /** The JSON schema in the tool list. */\n readonly schemaTokens: number;\n /** The prose Pi folds into the system prompt. */\n readonly promptTokens: number;\n readonly totalTokens: number;\n}\n\nexport interface ContextToolDetail {\n readonly itemKind: 'tool';\n readonly name: string;\n readonly owner: string;\n readonly source: ContextItemSource;\n readonly active: boolean;\n readonly tokens: ContextTokenBreakdown;\n readonly description?: string;\n /** The one-line snippet Pi keys by tool name in the system prompt. */\n readonly promptSnippet?: string;\n readonly promptGuidelines?: readonly string[];\n /** The parameter schema as the model receives it. */\n readonly parameters?: unknown;\n}\n\nexport interface ContextSkillDetail {\n readonly itemKind: 'skill';\n readonly name: string;\n readonly owner: string;\n readonly source: ContextItemSource;\n readonly active: true;\n /** A skill has no schema half, so the figure is the prompt cost alone. */\n readonly tokens: number;\n readonly description: string;\n readonly filePath: string;\n /** Whether the model may invoke it, as opposed to a human running it. */\n readonly modelInvocable: boolean;\n}\n\nexport type ContextItemDetail = ContextToolDetail | ContextSkillDetail;\n\n/** The file the agent writes and the session API reads back. */\nexport interface ContextDetailFile {\n readonly version: 1;\n /** Matches the projection revision the panel is rendering. */\n readonly revision: number;\n readonly items: readonly ContextItemDetail[];\n}\n\nexport const CONTEXT_DETAIL_VERSION = 1;\n\n/** The absolute URL a page reads one row's detail through. */\nexport function itemDetailUrl(sessionId: string, itemKind: ContextItemKind, name: string): string {\n const search = new URLSearchParams({\n [SESSION_QUERY_PARAM]: sessionId,\n [KIND_QUERY_PARAM]: itemKind,\n [NAME_QUERY_PARAM]: name,\n });\n return `/api/plugin/${API_BASE_PATH}${ITEM_ROUTE}?${search.toString()}`;\n}\n"],"mappings":"AAcA,MAAa,EAAgB,UAGhB,EAAsB,UAEtB,EAAa,QACb,EAAmB,OACnB,EAAmB,OAoDnB,EAAyB,EAGtC,SAAgB,EAAc,EAAmB,EAA2B,EAAsB,CAChG,IAAM,EAAS,IAAI,gBAAgB,EAChC,GAAsB,GACtB,GAAmB,GACnB,GAAmB,CACtB,CAAC,EACD,MAAO,eAAe,IAAgB,EAAW,GAAG,EAAO,SAAS,GACtE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.50",
|
|
4
4
|
"description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -43,6 +43,16 @@
|
|
|
43
43
|
"import": "./dist/index.mjs",
|
|
44
44
|
"require": "./dist/index.cjs"
|
|
45
45
|
},
|
|
46
|
+
"./contextApi": {
|
|
47
|
+
"types": "./dist/contextApi.d.mts",
|
|
48
|
+
"import": "./dist/contextApi.mjs",
|
|
49
|
+
"require": "./dist/contextApi.cjs"
|
|
50
|
+
},
|
|
51
|
+
"./sessionApi": {
|
|
52
|
+
"types": "./dist/sessionApi.d.mts",
|
|
53
|
+
"import": "./dist/sessionApi.mjs",
|
|
54
|
+
"require": "./dist/sessionApi.cjs"
|
|
55
|
+
},
|
|
46
56
|
"./bin/cli": {
|
|
47
57
|
"types": "./dist/bin/cli.d.mts",
|
|
48
58
|
"import": "./dist/bin/cli.mjs",
|
|
@@ -309,17 +319,17 @@
|
|
|
309
319
|
"rolldown": "1.2.6",
|
|
310
320
|
"typebox": "1.3.19",
|
|
311
321
|
"yaml": "2.9.0",
|
|
312
|
-
"@agimon-ai/doompi-autostop": "0.0.1-alpha.
|
|
313
|
-
"@agimon-ai/doompi-
|
|
314
|
-
"@agimon-ai/doompi-
|
|
315
|
-
"@agimon-ai/doompi-
|
|
316
|
-
"@agimon-ai/doompi-
|
|
317
|
-
"@agimon-ai/doompi-major-mode": "0.0.1-alpha.
|
|
318
|
-
"@agimon-ai/doompi-
|
|
319
|
-
"@agimon-ai/doompi-
|
|
320
|
-
"@agimon-ai/doompi-
|
|
321
|
-
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.
|
|
322
|
-
"@agimon-ai/doompi-
|
|
322
|
+
"@agimon-ai/doompi-autostop": "0.0.1-alpha.27",
|
|
323
|
+
"@agimon-ai/doompi-cache": "0.0.1-alpha.16",
|
|
324
|
+
"@agimon-ai/doompi-config": "0.0.1-alpha.49",
|
|
325
|
+
"@agimon-ai/doompi-domain": "0.0.1-alpha.28",
|
|
326
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.49",
|
|
327
|
+
"@agimon-ai/doompi-major-mode": "0.0.1-alpha.28",
|
|
328
|
+
"@agimon-ai/doompi-notification": "0.0.1-alpha.27",
|
|
329
|
+
"@agimon-ai/doompi-profile": "0.0.1-alpha.28",
|
|
330
|
+
"@agimon-ai/doompi-skill": "0.0.1-alpha.28",
|
|
331
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.49",
|
|
332
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.50"
|
|
323
333
|
},
|
|
324
334
|
"devDependencies": {
|
|
325
335
|
"@earendil-works/pi-ai": "0.84.4",
|
|
@@ -329,7 +339,7 @@
|
|
|
329
339
|
"@vitest/coverage-v8": "4.1.11",
|
|
330
340
|
"typescript": "7.0.2",
|
|
331
341
|
"vitest": "4.1.11",
|
|
332
|
-
"@agimon-ai/doompi-runner": "0.0.1-alpha.
|
|
342
|
+
"@agimon-ai/doompi-runner": "0.0.1-alpha.50"
|
|
333
343
|
},
|
|
334
344
|
"peerDependencies": {
|
|
335
345
|
"@earendil-works/pi-tui": "0.84.4"
|
|
@@ -342,6 +352,13 @@
|
|
|
342
352
|
"engines": {
|
|
343
353
|
"node": ">=22.19.0"
|
|
344
354
|
},
|
|
355
|
+
"doompiApi": {
|
|
356
|
+
"basePath": "context",
|
|
357
|
+
"session": {
|
|
358
|
+
"entry": "./src/exports/sessionApi.ts",
|
|
359
|
+
"dist": "./dist/sessionApi.mjs"
|
|
360
|
+
}
|
|
361
|
+
},
|
|
345
362
|
"pi": {
|
|
346
363
|
"extensions": [
|
|
347
364
|
"./dist/extensions/pi.mjs"
|