@agimon-ai/doompi-read 0.0.1-alpha.41 → 0.0.1-alpha.43

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.
Files changed (91) hide show
  1. package/README.md +3 -1
  2. package/dist/api-contracts.cjs +1 -0
  3. package/dist/api-contracts.d.cts +2 -0
  4. package/dist/api-contracts.d.mts +2 -0
  5. package/dist/api-contracts.mjs +1 -0
  6. package/dist/constants/package.cjs +2 -0
  7. package/dist/constants/package.cjs.map +1 -0
  8. package/dist/constants/package.mjs +2 -0
  9. package/dist/constants/package.mjs.map +1 -0
  10. package/dist/extensions/pi.cjs +2 -1
  11. package/dist/extensions/pi.cjs.map +1 -0
  12. package/dist/extensions/pi.d.cts +5 -2
  13. package/dist/extensions/pi.d.cts.map +1 -0
  14. package/dist/extensions/pi.d.mts +5 -2
  15. package/dist/extensions/pi.d.mts.map +1 -0
  16. package/dist/extensions/pi.mjs +2 -1
  17. package/dist/extensions/pi.mjs.map +1 -0
  18. package/dist/extensions/server.cjs +2 -0
  19. package/dist/extensions/server.cjs.map +1 -0
  20. package/dist/extensions/server.d.cts +5 -0
  21. package/dist/extensions/server.d.cts.map +1 -0
  22. package/dist/extensions/server.d.mts +5 -0
  23. package/dist/extensions/server.d.mts.map +1 -0
  24. package/dist/extensions/server.mjs +2 -0
  25. package/dist/extensions/server.mjs.map +1 -0
  26. package/dist/index.cjs +1 -1
  27. package/dist/index.d.cts +3 -3
  28. package/dist/index.d.mts +3 -3
  29. package/dist/index.mjs +1 -1
  30. package/dist/schemas/apiContracts.cjs +2 -0
  31. package/dist/schemas/apiContracts.cjs.map +1 -0
  32. package/dist/schemas/apiContracts.d.cts +53 -0
  33. package/dist/schemas/apiContracts.d.cts.map +1 -0
  34. package/dist/schemas/apiContracts.d.mts +53 -0
  35. package/dist/schemas/apiContracts.d.mts.map +1 -0
  36. package/dist/schemas/apiContracts.mjs +2 -0
  37. package/dist/schemas/apiContracts.mjs.map +1 -0
  38. package/dist/services/piReadImage/index.cjs +2 -0
  39. package/dist/services/piReadImage/index.cjs.map +1 -0
  40. package/dist/services/piReadImage/index.mjs +2 -0
  41. package/dist/services/piReadImage/index.mjs.map +1 -0
  42. package/dist/services/readImage/index.cjs +2 -0
  43. package/dist/services/readImage/index.cjs.map +1 -0
  44. package/dist/services/readImage/index.d.cts +14 -0
  45. package/dist/services/readImage/index.d.cts.map +1 -0
  46. package/dist/services/readImage/index.d.mts +14 -0
  47. package/dist/services/readImage/index.d.mts.map +1 -0
  48. package/dist/services/readImage/index.mjs +2 -0
  49. package/dist/services/readImage/index.mjs.map +1 -0
  50. package/dist/services/readTool/index.cjs +3 -0
  51. package/dist/services/readTool/index.cjs.map +1 -0
  52. package/dist/services/readTool/index.d.cts +13 -0
  53. package/dist/services/readTool/index.d.cts.map +1 -0
  54. package/dist/services/readTool/index.d.mts +13 -0
  55. package/dist/services/readTool/index.d.mts.map +1 -0
  56. package/dist/services/readTool/index.mjs +3 -0
  57. package/dist/services/readTool/index.mjs.map +1 -0
  58. package/dist/tools/piRead.cjs +2 -0
  59. package/dist/tools/piRead.cjs.map +1 -0
  60. package/dist/tools/piRead.d.cts +9 -0
  61. package/dist/tools/piRead.d.cts.map +1 -0
  62. package/dist/tools/piRead.d.mts +9 -0
  63. package/dist/tools/piRead.d.mts.map +1 -0
  64. package/dist/tools/piRead.mjs +2 -0
  65. package/dist/tools/piRead.mjs.map +1 -0
  66. package/package.json +27 -9
  67. package/src/extensions/web.ts +14 -0
  68. package/src/web/components/ReadToolMessage.tsx +3 -2
  69. package/src/web/tsconfig.json +1 -1
  70. package/dist/adapters/pi/extension.cjs +0 -2
  71. package/dist/adapters/pi/extension.cjs.map +0 -1
  72. package/dist/adapters/pi/extension.d.cts +0 -10
  73. package/dist/adapters/pi/extension.d.cts.map +0 -1
  74. package/dist/adapters/pi/extension.d.mts +0 -10
  75. package/dist/adapters/pi/extension.d.mts.map +0 -1
  76. package/dist/adapters/pi/extension.mjs +0 -2
  77. package/dist/adapters/pi/extension.mjs.map +0 -1
  78. package/dist/adapters/pi/readImage.cjs +0 -2
  79. package/dist/adapters/pi/readImage.cjs.map +0 -1
  80. package/dist/adapters/pi/readImage.mjs +0 -2
  81. package/dist/adapters/pi/readImage.mjs.map +0 -1
  82. package/dist/adapters/pi/readTool.cjs +0 -3
  83. package/dist/adapters/pi/readTool.cjs.map +0 -1
  84. package/dist/adapters/pi/readTool.d.cts +0 -8
  85. package/dist/adapters/pi/readTool.d.cts.map +0 -1
  86. package/dist/adapters/pi/readTool.d.mts +0 -8
  87. package/dist/adapters/pi/readTool.d.mts.map +0 -1
  88. package/dist/adapters/pi/readTool.mjs +0 -3
  89. package/dist/adapters/pi/readTool.mjs.map +0 -1
  90. package/src/exports/webClient.ts +0 -1
  91. package/src/web/index.ts +0 -11
package/README.md CHANGED
@@ -53,7 +53,7 @@ boundary.
53
53
  ## Public API
54
54
 
55
55
  ```ts
56
- import { ReadParamsSchema, registerHashlineReadTool } from '@agimon-ai/doompi-read';
56
+ import { ReadParamsSchema, createHashlineReadTool } from '@agimon-ai/doompi-read';
57
57
  import type { ReadParams } from '@agimon-ai/doompi-read';
58
58
  ```
59
59
 
@@ -71,3 +71,5 @@ Maintained by [Agimon](https://agimon.ai/about).
71
71
  ## License
72
72
 
73
73
  MIT
74
+
75
+ The Pi entry in `src/extensions/pi.ts` declares a typed `read` replacement. The helper owns its override claim and releases it when the plugin stops. The server entry declares the headless tool separately. Tool declarations consume logic from named service folders; flat `src/exports` files expose reusable factories and schemas.
@@ -0,0 +1 @@
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./schemas/apiContracts.cjs");exports.apiContracts=e.apiContracts,exports.default=e.apiContracts;
@@ -0,0 +1,2 @@
1
+ import { apiContracts } from "./schemas/apiContracts.cjs";
2
+ export { apiContracts, apiContracts as default };
@@ -0,0 +1,2 @@
1
+ import { apiContracts } from "./schemas/apiContracts.mjs";
2
+ export { apiContracts, apiContracts as default };
@@ -0,0 +1 @@
1
+ import{apiContracts as e}from"./schemas/apiContracts.mjs";export{e as apiContracts,e as default};
@@ -0,0 +1,2 @@
1
+ exports.PACKAGE_SOURCE=`@agimon-ai/doompi-read`;
2
+ //# sourceMappingURL=package.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.cjs","names":[],"sources":["../../src/constants/package.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-read';\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ const e=`@agimon-ai/doompi-read`;export{e as PACKAGE_SOURCE};
2
+ //# sourceMappingURL=package.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.mjs","names":[],"sources":["../../src/constants/package.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-read';\n"],"mappings":"AAAA,MAAa,EAAiB"}
@@ -1 +1,2 @@
1
- const e=require("../adapters/pi/extension.cjs");module.exports=e.activateDoomPiReadExtension;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../tools/piRead.cjs"),t=require("../constants/package.cjs");let n=require("@agimon-ai/doompi-core/pi-extension");const r=(0,n.definePiExtension)(t.PACKAGE_SOURCE,()=>({toolOverrides:[{source:t.PACKAGE_SOURCE,tools:[`read`],replacements:[(0,n.definePiTool)(e.createHashlineReadTool())]}]}));exports.activateDoomPiReadExtension=r,exports.default=r;
2
+ //# sourceMappingURL=pi.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.cjs","names":["definePiExtension","PACKAGE_SOURCE","definePiTool","createHashlineReadTool"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { definePiExtension, definePiTool } from '@agimon-ai/doompi-core/pi-extension';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { createHashlineReadTool } from '../tools/piRead';\n\nexport const activateDoomPiReadExtension = definePiExtension(PACKAGE_SOURCE, () => ({\n toolOverrides: [{ source: PACKAGE_SOURCE, tools: ['read'], replacements: [definePiTool(createHashlineReadTool())] }],\n}));\nexport default activateDoomPiReadExtension;\n"],"mappings":"iOAKA,MAAa,GAAA,EAA8BA,EAAAA,kBAAAA,CAAkBC,EAAAA,oBAAuB,CAClF,cAAe,CAAC,CAAE,OAAQA,EAAAA,eAAgB,MAAO,CAAC,MAAM,EAAG,aAAc,EAAA,EAACC,EAAAA,aAAAA,CAAaC,EAAAA,uBAAuB,CAAC,CAAC,CAAE,CAAC,CACrH,EAAE"}
@@ -1,2 +1,5 @@
1
- import { activateDoomPiReadExtension } from "../adapters/pi/extension.cjs";
2
- export = activateDoomPiReadExtension;
1
+ //#region src/extensions/pi.d.ts
2
+ export declare const activateDoomPiReadExtension: import("@agimon-ai/doompi-core/runtime-pi-extension").DefinedPiExtension<undefined>;
3
+ //#endregion
4
+ export { activateDoomPiReadExtension as default };
5
+ //# sourceMappingURL=pi.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.d.cts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";qBAKa,mFAA2B"}
@@ -1,2 +1,5 @@
1
- import { activateDoomPiReadExtension } from "../adapters/pi/extension.mjs";
2
- export { activateDoomPiReadExtension as default };
1
+ //#region src/extensions/pi.d.ts
2
+ export declare const activateDoomPiReadExtension: import("@agimon-ai/doompi-core/runtime-pi-extension").DefinedPiExtension<undefined>;
3
+ //#endregion
4
+ export { activateDoomPiReadExtension as default };
5
+ //# sourceMappingURL=pi.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.d.mts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";qBAKa,mFAA2B"}
@@ -1 +1,2 @@
1
- import{activateDoomPiReadExtension as e}from"../adapters/pi/extension.mjs";export{e as default};
1
+ import{createHashlineReadTool as e}from"../tools/piRead.mjs";import{PACKAGE_SOURCE as t}from"../constants/package.mjs";import{definePiExtension as n,definePiTool as r}from"@agimon-ai/doompi-core/pi-extension";const i=n(t,()=>({toolOverrides:[{source:t,tools:[`read`],replacements:[r(e())]}]}));export{i as activateDoomPiReadExtension,i as default};
2
+ //# sourceMappingURL=pi.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { definePiExtension, definePiTool } from '@agimon-ai/doompi-core/pi-extension';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { createHashlineReadTool } from '../tools/piRead';\n\nexport const activateDoomPiReadExtension = definePiExtension(PACKAGE_SOURCE, () => ({\n toolOverrides: [{ source: PACKAGE_SOURCE, tools: ['read'], replacements: [definePiTool(createHashlineReadTool())] }],\n}));\nexport default activateDoomPiReadExtension;\n"],"mappings":"iNAKA,MAAa,EAA8B,EAAkB,OAAuB,CAClF,cAAe,CAAC,CAAE,OAAQ,EAAgB,MAAO,CAAC,MAAM,EAAG,aAAc,CAAC,EAAa,EAAuB,CAAC,CAAC,CAAE,CAAC,CACrH,EAAE"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../services/readTool/index.cjs"),t=require("../constants/package.cjs"),n=(0,require("@agimon-ai/doompi-core/server-facet").defineServerPlugin)({name:t.PACKAGE_SOURCE,session:()=>({tools:[e.createHeadlessReadTool()]})});exports.default=n,exports.readServerFacet=n;
2
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.cjs","names":["defineServerPlugin","PACKAGE_SOURCE","createHeadlessReadTool"],"sources":["../../src/extensions/server.ts"],"sourcesContent":["import { defineServerPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { createHeadlessReadTool } from '../services/readTool';\n\nexport const readServerFacet = defineServerPlugin({\n name: PACKAGE_SOURCE,\n session: () => ({ tools: [createHeadlessReadTool()] }),\n});\nexport default readServerFacet;\n"],"mappings":"uLAKa,GAAA,+CAAkBA,CAAAA,CAAAA,mBAAAA,CAAmB,CAChD,KAAMC,EAAAA,eACN,aAAgB,CAAE,MAAO,CAACC,EAAAA,uBAAuB,CAAC,CAAE,EACtD,CAAC"}
@@ -0,0 +1,5 @@
1
+ //#region src/extensions/server.d.ts
2
+ export declare const readServerFacet: import("@agimon-ai/doompi-core/server-facet").DoomServerFacet;
3
+ //#endregion
4
+ export { readServerFacet as default };
5
+ //# sourceMappingURL=server.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.cts","names":[],"sources":["../../src/extensions/server.ts"],"mappings":";qBAKa,+DAAe"}
@@ -0,0 +1,5 @@
1
+ //#region src/extensions/server.d.ts
2
+ export declare const readServerFacet: import("@agimon-ai/doompi-core/server-facet").DoomServerFacet;
3
+ //#endregion
4
+ export { readServerFacet as default };
5
+ //# sourceMappingURL=server.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../src/extensions/server.ts"],"mappings":";qBAKa,+DAAe"}
@@ -0,0 +1,2 @@
1
+ import{createHeadlessReadTool as e}from"../services/readTool/index.mjs";import{PACKAGE_SOURCE as t}from"../constants/package.mjs";import{defineServerPlugin as n}from"@agimon-ai/doompi-core/server-facet";const r=n({name:t,session:()=>({tools:[e()]})});export{r as default,r as readServerFacet};
2
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.mjs","names":[],"sources":["../../src/extensions/server.ts"],"sourcesContent":["import { defineServerPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { createHeadlessReadTool } from '../services/readTool';\n\nexport const readServerFacet = defineServerPlugin({\n name: PACKAGE_SOURCE,\n session: () => ({ tools: [createHeadlessReadTool()] }),\n});\nexport default readServerFacet;\n"],"mappings":"2MAKA,MAAa,EAAkB,EAAmB,CAChD,KAAM,EACN,aAAgB,CAAE,MAAO,CAAC,EAAuB,CAAC,CAAE,EACtD,CAAC"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./schemas/readTool.cjs"),t=require("./adapters/pi/readTool.cjs"),n=require("./adapters/pi/extension.cjs");exports.ReadParamsSchema=e.ReadParamsSchema,exports.activateDoomPiReadExtension=n.activateDoomPiReadExtension,exports.installDoomPiReadRuntime=n.installDoomPiReadRuntime,exports.registerHashlineReadTool=t.registerHashlineReadTool;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./schemas/readTool.cjs"),t=require("./services/readTool/index.cjs"),n=require("./tools/piRead.cjs");exports.ReadParamsSchema=e.ReadParamsSchema,exports.createHashlineReadTool=n.createHashlineReadTool,exports.createHeadlessReadTool=t.createHeadlessReadTool,exports.executeHeadlessRead=t.executeHeadlessRead;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { activateDoomPiReadExtension, installDoomPiReadRuntime } from "./adapters/pi/extension.cjs";
2
1
  import { ReadParams, ReadParamsSchema } from "./schemas/readTool.cjs";
3
- import { registerHashlineReadTool } from "./adapters/pi/readTool.cjs";
4
- export { type ReadParams, ReadParamsSchema, activateDoomPiReadExtension, installDoomPiReadRuntime, registerHashlineReadTool };
2
+ import { createHeadlessReadTool, executeHeadlessRead } from "./services/readTool/index.cjs";
3
+ import { createHashlineReadTool } from "./tools/piRead.cjs";
4
+ export { type ReadParams, ReadParamsSchema, createHashlineReadTool, createHeadlessReadTool, executeHeadlessRead };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { activateDoomPiReadExtension, installDoomPiReadRuntime } from "./adapters/pi/extension.mjs";
2
1
  import { ReadParams, ReadParamsSchema } from "./schemas/readTool.mjs";
3
- import { registerHashlineReadTool } from "./adapters/pi/readTool.mjs";
4
- export { type ReadParams, ReadParamsSchema, activateDoomPiReadExtension, installDoomPiReadRuntime, registerHashlineReadTool };
2
+ import { createHeadlessReadTool, executeHeadlessRead } from "./services/readTool/index.mjs";
3
+ import { createHashlineReadTool } from "./tools/piRead.mjs";
4
+ export { type ReadParams, ReadParamsSchema, createHashlineReadTool, createHeadlessReadTool, executeHeadlessRead };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{ReadParamsSchema as e}from"./schemas/readTool.mjs";import{registerHashlineReadTool as t}from"./adapters/pi/readTool.mjs";import{activateDoomPiReadExtension as n,installDoomPiReadRuntime as r}from"./adapters/pi/extension.mjs";export{e as ReadParamsSchema,n as activateDoomPiReadExtension,r as installDoomPiReadRuntime,t as registerHashlineReadTool};
1
+ import{ReadParamsSchema as e}from"./schemas/readTool.mjs";import{createHeadlessReadTool as t,executeHeadlessRead as n}from"./services/readTool/index.mjs";import{createHashlineReadTool as r}from"./tools/piRead.mjs";export{e as ReadParamsSchema,r as createHashlineReadTool,t as createHeadlessReadTool,n as executeHeadlessRead};
@@ -0,0 +1,2 @@
1
+ const e=(0,require("@agimon-ai/doompi-core/api-contracts").defineApiContract)({version:1,http:[],sockets:[],dynamic:[]});exports.apiContracts=e,exports.default=e;
2
+ //# sourceMappingURL=apiContracts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.cjs","names":["defineApiContract"],"sources":["../../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/doompi-core/api-contracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":"AAGA,MAAa,GAAA,gDAAeA,CAAAA,CAAAA,kBAAAA,CAAkB,CAAE,QAAS,EAAG,KAAM,CAAC,EAAG,QAAS,CAAC,EAAG,QAAS,CAAC,CAAE,CAAC"}
@@ -0,0 +1,53 @@
1
+ //#region src/schemas/apiContracts.d.ts
2
+ /** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
3
+ export declare const apiContracts: {
4
+ version: 1;
5
+ protocols?: Record<string, string> | undefined;
6
+ schemas?: Record<string, boolean | object> | undefined;
7
+ http: {
8
+ id: string;
9
+ scope: "global" | "session" | "workspace";
10
+ basePath?: string | undefined;
11
+ path: string;
12
+ method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
13
+ description: string;
14
+ availability?: string | undefined;
15
+ authentication: "device" | "none" | "owner";
16
+ parameters?: {
17
+ name: string;
18
+ in: "header" | "path" | "query";
19
+ required: boolean;
20
+ schema: boolean | object;
21
+ }[] | undefined;
22
+ body?: {
23
+ contentType: string;
24
+ contentTypes?: string[] | undefined;
25
+ required: boolean;
26
+ schema: boolean | object;
27
+ } | undefined;
28
+ responses: Record<string, {
29
+ description: string;
30
+ contentType?: string | undefined;
31
+ schema?: boolean | object | undefined;
32
+ events?: Record<string, boolean | object> | undefined;
33
+ }>;
34
+ }[];
35
+ sockets: {
36
+ id: string;
37
+ scope: "global" | "session" | "workspace";
38
+ description: string;
39
+ availability?: string | undefined;
40
+ service: string;
41
+ member: string;
42
+ direction: "client-to-server" | "server-to-client";
43
+ kind: "channel" | "method" | "state";
44
+ input: boolean | object;
45
+ output?: boolean | object | undefined;
46
+ errors?: boolean | object | undefined;
47
+ }[];
48
+ dynamic: string[];
49
+ gaps?: string[] | undefined;
50
+ };
51
+ //#endregion
52
+ export { apiContracts as default };
53
+ //# sourceMappingURL=apiContracts.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.d.cts","names":[],"sources":["../../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
@@ -0,0 +1,53 @@
1
+ //#region src/schemas/apiContracts.d.ts
2
+ /** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
3
+ export declare const apiContracts: {
4
+ version: 1;
5
+ protocols?: Record<string, string> | undefined;
6
+ schemas?: Record<string, boolean | object> | undefined;
7
+ http: {
8
+ id: string;
9
+ scope: "global" | "session" | "workspace";
10
+ basePath?: string | undefined;
11
+ path: string;
12
+ method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
13
+ description: string;
14
+ availability?: string | undefined;
15
+ authentication: "device" | "none" | "owner";
16
+ parameters?: {
17
+ name: string;
18
+ in: "header" | "path" | "query";
19
+ required: boolean;
20
+ schema: boolean | object;
21
+ }[] | undefined;
22
+ body?: {
23
+ contentType: string;
24
+ contentTypes?: string[] | undefined;
25
+ required: boolean;
26
+ schema: boolean | object;
27
+ } | undefined;
28
+ responses: Record<string, {
29
+ description: string;
30
+ contentType?: string | undefined;
31
+ schema?: boolean | object | undefined;
32
+ events?: Record<string, boolean | object> | undefined;
33
+ }>;
34
+ }[];
35
+ sockets: {
36
+ id: string;
37
+ scope: "global" | "session" | "workspace";
38
+ description: string;
39
+ availability?: string | undefined;
40
+ service: string;
41
+ member: string;
42
+ direction: "client-to-server" | "server-to-client";
43
+ kind: "channel" | "method" | "state";
44
+ input: boolean | object;
45
+ output?: boolean | object | undefined;
46
+ errors?: boolean | object | undefined;
47
+ }[];
48
+ dynamic: string[];
49
+ gaps?: string[] | undefined;
50
+ };
51
+ //#endregion
52
+ export { apiContracts as default };
53
+ //# sourceMappingURL=apiContracts.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
@@ -0,0 +1,2 @@
1
+ import{defineApiContract as e}from"@agimon-ai/doompi-core/api-contracts";const t=e({version:1,http:[],sockets:[],dynamic:[]});export{t as apiContracts,t as default};
2
+ //# sourceMappingURL=apiContracts.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.mjs","names":[],"sources":["../../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/doompi-core/api-contracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":"yEAGA,MAAa,EAAe,EAAkB,CAAE,QAAS,EAAG,KAAM,CAAC,EAAG,QAAS,CAAC,EAAG,QAAS,CAAC,CAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ const e=require("../readImage/index.cjs");let t=require("@earendil-works/pi-coding-agent");const n={resize:t.resizeImage,formatDimensionNote:e=>(0,t.formatDimensionNote)(e)};function r(t,r){return e.applyImageLimits(t,r,n)}exports.applyImageLimits=r,exports.imageLimits=e.imageLimits;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["resizeImage","formatDimensionNote","applyServiceImageLimits"],"sources":["../../../src/services/piReadImage/index.ts"],"sourcesContent":["import { formatDimensionNote, resizeImage } from '@earendil-works/pi-coding-agent';\n\nimport {\n applyImageLimits as applyServiceImageLimits,\n type ReadContentPart,\n type ReadImageResizeOperations,\n type ReadImageResizeResult,\n} from '../readImage';\n\nexport { imageLimits, type ReadContentPart, type ReadImagePart, type ReadTextPart } from '../readImage';\n\nconst resizeOperations: ReadImageResizeOperations = {\n resize: resizeImage,\n formatDimensionNote: (result: ReadImageResizeResult) => formatDimensionNote(result),\n};\n\nexport function applyImageLimits(\n content: ReadContentPart[],\n limits: Parameters<typeof applyServiceImageLimits>[1],\n): Promise<ReadContentPart[]> {\n return applyServiceImageLimits(content, limits, resizeOperations);\n}\n"],"mappings":"2FAWA,MAAM,EAA8C,CAClD,OAAQA,EAAAA,YACR,oBAAsB,IAAA,EAAkCC,EAAAA,oBAAAA,CAAoB,CAAM,CACpF,EAEA,SAAgB,EACd,EACA,EAC4B,CAC5B,OAAOC,EAAAA,iBAAwB,EAAS,EAAQ,CAAgB,CAClE"}
@@ -0,0 +1,2 @@
1
+ import{applyImageLimits as e,imageLimits as t}from"../readImage/index.mjs";import{formatDimensionNote as n,resizeImage as r}from"@earendil-works/pi-coding-agent";const i={resize:r,formatDimensionNote:e=>n(e)};function a(t,n){return e(t,n,i)}export{a as applyImageLimits,t as imageLimits};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["applyServiceImageLimits"],"sources":["../../../src/services/piReadImage/index.ts"],"sourcesContent":["import { formatDimensionNote, resizeImage } from '@earendil-works/pi-coding-agent';\n\nimport {\n applyImageLimits as applyServiceImageLimits,\n type ReadContentPart,\n type ReadImageResizeOperations,\n type ReadImageResizeResult,\n} from '../readImage';\n\nexport { imageLimits, type ReadContentPart, type ReadImagePart, type ReadTextPart } from '../readImage';\n\nconst resizeOperations: ReadImageResizeOperations = {\n resize: resizeImage,\n formatDimensionNote: (result: ReadImageResizeResult) => formatDimensionNote(result),\n};\n\nexport function applyImageLimits(\n content: ReadContentPart[],\n limits: Parameters<typeof applyServiceImageLimits>[1],\n): Promise<ReadContentPart[]> {\n return applyServiceImageLimits(content, limits, resizeOperations);\n}\n"],"mappings":"kKAWA,MAAM,EAA8C,CAClD,OAAQ,EACR,oBAAsB,GAAkC,EAAoB,CAAM,CACpF,EAEA,SAAgB,EACd,EACA,EAC4B,CAC5B,OAAOA,EAAwB,EAAS,EAAQ,CAAgB,CAClE"}
@@ -0,0 +1,2 @@
1
+ let e=require("@agimon-ai/doompi-config/pi-config");function t(){return(0,e.loadPiImageSettings)()}async function n(e,t,n){if(!t.autoResize||!e.some(e=>e.type===`image`))return e;let r=[];for(let i of e){if(i.type!==`image`){r.push(i);continue}let e=await n.resize(Buffer.from(i.data,`base64`),i.mimeType,{maxWidth:t.maxDimension,maxHeight:t.maxDimension});if(!e){r.push({type:`text`,text:`[Image omitted: could not be resized below the inline image size limit.]`});continue}r.push({type:`image`,data:e.data,mimeType:e.mimeType});let a=n.formatDimensionNote(e);a&&r.push({type:`text`,text:a})}return r}exports.applyImageLimits=n,exports.imageLimits=t;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["loadPiImageSettings"],"sources":["../../../src/services/readImage/index.ts"],"sourcesContent":["import { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/doompi-config/pi-config';\n\nconst OMITTED_NOTE = '[Image omitted: could not be resized below the inline image size limit.]';\n\nexport interface ReadTextPart {\n type: 'text';\n text: string;\n}\n\nexport interface ReadImagePart {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type ReadContentPart = ReadTextPart | ReadImagePart;\n\nexport interface ReadImageResizeResult {\n readonly data: string;\n readonly mimeType: string;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly width: number;\n readonly height: number;\n readonly wasResized: boolean;\n}\n\nexport interface ReadImageResizeOperations {\n resize(\n inputBytes: Uint8Array,\n mimeType: string,\n options: { readonly maxWidth: number; readonly maxHeight: number },\n ): Promise<ReadImageResizeResult | null>;\n formatDimensionNote(result: ReadImageResizeResult): string | undefined;\n}\n\nexport function imageLimits(): PiImageSettings {\n return loadPiImageSettings();\n}\n\nexport async function applyImageLimits(\n content: ReadContentPart[],\n limits: PiImageSettings,\n operations: ReadImageResizeOperations,\n): Promise<ReadContentPart[]> {\n if (!limits.autoResize || !content.some((part) => part.type === 'image')) return content;\n const resolved: ReadContentPart[] = [];\n for (const part of content) {\n if (part.type !== 'image') {\n resolved.push(part);\n continue;\n }\n const resized = await operations.resize(Buffer.from(part.data, 'base64'), part.mimeType, {\n maxWidth: limits.maxDimension,\n maxHeight: limits.maxDimension,\n });\n if (!resized) {\n resolved.push({ type: 'text', text: OMITTED_NOTE });\n continue;\n }\n resolved.push({ type: 'image', data: resized.data, mimeType: resized.mimeType });\n const note = operations.formatDimensionNote(resized);\n if (note) resolved.push({ type: 'text', text: note });\n }\n return resolved;\n}\n"],"mappings":"oDAoCA,SAAgB,GAA+B,CAC7C,OAAA,EAAOA,EAAAA,oBAAAA,CAAoB,CAC7B,CAEA,eAAsB,EACpB,EACA,EACA,EAC4B,CAC5B,GAAI,CAAC,EAAO,YAAc,CAAC,EAAQ,KAAM,GAAS,EAAK,OAAS,OAAO,EAAG,OAAO,EACjF,IAAM,EAA8B,CAAC,EACrC,IAAK,IAAM,KAAQ,EAAS,CAC1B,GAAI,EAAK,OAAS,QAAS,CACzB,EAAS,KAAK,CAAI,EAClB,QACF,CACA,IAAM,EAAU,MAAM,EAAW,OAAO,OAAO,KAAK,EAAK,KAAM,QAAQ,EAAG,EAAK,SAAU,CACvF,SAAU,EAAO,aACjB,UAAW,EAAO,YACpB,CAAC,EACD,GAAI,CAAC,EAAS,CACZ,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,0EAAa,CAAC,EAClD,QACF,CACA,EAAS,KAAK,CAAE,KAAM,QAAS,KAAM,EAAQ,KAAM,SAAU,EAAQ,QAAS,CAAC,EAC/E,IAAM,EAAO,EAAW,oBAAoB,CAAO,EAC/C,GAAM,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,CAAK,CAAC,CACtD,CACA,OAAO,CACT"}
@@ -0,0 +1,14 @@
1
+ import "@agimon-ai/doompi-config/pi-config";
2
+ //#region src/services/readImage/index.d.ts
3
+ export interface ReadTextPart {
4
+ type: 'text';
5
+ text: string;
6
+ }
7
+ export interface ReadImagePart {
8
+ type: 'image';
9
+ data: string;
10
+ mimeType: string;
11
+ }
12
+ export type ReadContentPart = ReadTextPart | ReadImagePart;
13
+ //#endregion
14
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/readImage/index.ts"],"mappings":";;iBAIiB;EACf;EACA;;iBAGe;EACf;EACA;EACA;;YAGU,kBAAkB,eAAe"}
@@ -0,0 +1,14 @@
1
+ import "@agimon-ai/doompi-config/pi-config";
2
+ //#region src/services/readImage/index.d.ts
3
+ export interface ReadTextPart {
4
+ type: 'text';
5
+ text: string;
6
+ }
7
+ export interface ReadImagePart {
8
+ type: 'image';
9
+ data: string;
10
+ mimeType: string;
11
+ }
12
+ export type ReadContentPart = ReadTextPart | ReadImagePart;
13
+ //#endregion
14
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/readImage/index.ts"],"mappings":";;iBAIiB;EACf;EACA;;iBAGe;EACf;EACA;EACA;;YAGU,kBAAkB,eAAe"}
@@ -0,0 +1,2 @@
1
+ import{loadPiImageSettings as e}from"@agimon-ai/doompi-config/pi-config";function t(){return e()}async function n(e,t,n){if(!t.autoResize||!e.some(e=>e.type===`image`))return e;let r=[];for(let i of e){if(i.type!==`image`){r.push(i);continue}let e=await n.resize(Buffer.from(i.data,`base64`),i.mimeType,{maxWidth:t.maxDimension,maxHeight:t.maxDimension});if(!e){r.push({type:`text`,text:`[Image omitted: could not be resized below the inline image size limit.]`});continue}r.push({type:`image`,data:e.data,mimeType:e.mimeType});let a=n.formatDimensionNote(e);a&&r.push({type:`text`,text:a})}return r}export{n as applyImageLimits,t as imageLimits};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/services/readImage/index.ts"],"sourcesContent":["import { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/doompi-config/pi-config';\n\nconst OMITTED_NOTE = '[Image omitted: could not be resized below the inline image size limit.]';\n\nexport interface ReadTextPart {\n type: 'text';\n text: string;\n}\n\nexport interface ReadImagePart {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type ReadContentPart = ReadTextPart | ReadImagePart;\n\nexport interface ReadImageResizeResult {\n readonly data: string;\n readonly mimeType: string;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly width: number;\n readonly height: number;\n readonly wasResized: boolean;\n}\n\nexport interface ReadImageResizeOperations {\n resize(\n inputBytes: Uint8Array,\n mimeType: string,\n options: { readonly maxWidth: number; readonly maxHeight: number },\n ): Promise<ReadImageResizeResult | null>;\n formatDimensionNote(result: ReadImageResizeResult): string | undefined;\n}\n\nexport function imageLimits(): PiImageSettings {\n return loadPiImageSettings();\n}\n\nexport async function applyImageLimits(\n content: ReadContentPart[],\n limits: PiImageSettings,\n operations: ReadImageResizeOperations,\n): Promise<ReadContentPart[]> {\n if (!limits.autoResize || !content.some((part) => part.type === 'image')) return content;\n const resolved: ReadContentPart[] = [];\n for (const part of content) {\n if (part.type !== 'image') {\n resolved.push(part);\n continue;\n }\n const resized = await operations.resize(Buffer.from(part.data, 'base64'), part.mimeType, {\n maxWidth: limits.maxDimension,\n maxHeight: limits.maxDimension,\n });\n if (!resized) {\n resolved.push({ type: 'text', text: OMITTED_NOTE });\n continue;\n }\n resolved.push({ type: 'image', data: resized.data, mimeType: resized.mimeType });\n const note = operations.formatDimensionNote(resized);\n if (note) resolved.push({ type: 'text', text: note });\n }\n return resolved;\n}\n"],"mappings":"yEAoCA,SAAgB,GAA+B,CAC7C,OAAO,EAAoB,CAC7B,CAEA,eAAsB,EACpB,EACA,EACA,EAC4B,CAC5B,GAAI,CAAC,EAAO,YAAc,CAAC,EAAQ,KAAM,GAAS,EAAK,OAAS,OAAO,EAAG,OAAO,EACjF,IAAM,EAA8B,CAAC,EACrC,IAAK,IAAM,KAAQ,EAAS,CAC1B,GAAI,EAAK,OAAS,QAAS,CACzB,EAAS,KAAK,CAAI,EAClB,QACF,CACA,IAAM,EAAU,MAAM,EAAW,OAAO,OAAO,KAAK,EAAK,KAAM,QAAQ,EAAG,EAAK,SAAU,CACvF,SAAU,EAAO,aACjB,UAAW,EAAO,YACpB,CAAC,EACD,GAAI,CAAC,EAAS,CACZ,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,0EAAa,CAAC,EAClD,QACF,CACA,EAAS,KAAK,CAAE,KAAM,QAAS,KAAM,EAAQ,KAAM,SAAU,EAAQ,QAAS,CAAC,EAC/E,IAAM,EAAO,EAAW,oBAAoB,CAAO,EAC/C,GAAM,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,CAAK,CAAC,CACtD,CACA,OAAO,CACT"}
@@ -0,0 +1,3 @@
1
+ const e=require("../../schemas/readTool.cjs"),t=require("../readImage/index.cjs");let n=require("node:fs/promises"),r=require("@agimon-ai/doompi-hashline/files"),i=require("@earendil-works/pi-coding-agent"),a=require("@agimon-ai/doompi-hashline");const o={resize:i.resizeImage,formatDimensionNote:e=>(0,i.formatDimensionNote)(e)};function s(e){if(e?.aborted)throw Error(`Operation aborted`)}function c(e){let t=e.toLowerCase().match(/\.([a-z0-9]+)$/u)?.[1];if(t)return{avif:`image/avif`,gif:`image/gif`,jpeg:`image/jpeg`,jpg:`image/jpeg`,png:`image/png`,webp:`image/webp`}[t]}async function l(e,i,a){s(a);let l=await(0,r.resolveReadInputPath)(e.path,i),d=await(0,n.readFile)(l);s(a);let f=c(l);if(f){let e=[{type:`text`,text:`Read image file ${(0,r.displayPath)(l,i)} [${f}]`},{type:`image`,data:d.toString(`base64`),mimeType:f}];return{content:await t.applyImageLimits(e,t.imageLimits(),o)}}return await(0,r.isWritableFile)(l)?u(d,(0,r.displayPath)(l,i),e):{content:[{type:`text`,text:(0,r.decodeUtf8)(d,(0,r.displayPath)(l,i))}]}}function u(e,t,n){let o=(0,a.splitLines)((0,r.decodeUtf8)(e,t)),s=n.offset===void 0?0:n.offset-1;if(s>=o.length)throw Error(`Offset ${n.offset} is beyond end of file (${o.length} lines total).`);let c=n.limit===void 0?o.length:Math.min(o.length,s+n.limit),l=o.slice(s,c),u=(0,a.formatFileHeader)(t,(0,r.computeFileTag)(e)),d=[],f=Buffer.byteLength(u,`utf8`)+1,p=l.map((e,t)=>{let n=s+t+1,r=(0,a.formatTaggedLine)(e,n);return Buffer.byteLength(r,`utf8`)<=i.DEFAULT_MAX_BYTES-f?r:(d.push(n),(0,a.formatTaggedLine)((0,i.truncateLine)(e).text,n,``,e))}),m=(0,i.truncateHead)([u,...p].join(`
2
+ `)),h=m.content,g;if(m.truncated){let e=Math.max(0,m.outputLines-1),t=s+e+1,n=m.truncatedBy===`bytes`?`, ${(0,i.formatSize)(i.DEFAULT_MAX_BYTES)} limit`:``;h+=`\n\n[Showing ${e} anchored lines${n}. Use offset=${t} to continue.]`,g={truncation:m}}else{let e=[];d.length>0&&e.push(`Lines ${d.join(`, `)} shown compactly. Their anchors hash the full original lines`),c<o.length&&e.push(`${o.length-c} more lines in file. Use offset=${c+1} to continue`),e.length>0&&(h+=`\n\n[${e.join(`. `)}.]`)}return{content:[{type:`text`,text:h}],details:g}}function d(e){return e.some(e=>e.type===`image`||e.text?.startsWith(`Read image file`)===!0)}function f(){return{name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,promptGuidelines:[`Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.`,`Continue large reads with offset until the required anchored lines are visible.`],parameters:e.ReadParamsSchema,executionMode:`parallel`,execute:async(e,t,n,r,i)=>l(t,i.cwd,n)}}exports.assertNotAborted=s,exports.createHeadlessReadTool=f,exports.createTaggedReadResult=u,exports.executeHeadlessRead=l,exports.isImagePath=c,exports.isImageRead=d;
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["resizeImage","formatDimensionNote","resolveReadInputPath","readFile","displayPath","applyImageLimits","imageLimits","isWritableFile","decodeUtf8","splitLines","formatFileHeader","computeFileTag","formatTaggedLine","DEFAULT_MAX_BYTES","truncateLine","truncateHead","formatSize","ReadParamsSchema"],"sources":["../../../src/services/readTool/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/doompi-core/headless';\nimport { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/doompi-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/doompi-hashline/files';\nimport {\n DEFAULT_MAX_BYTES,\n formatSize,\n truncateHead,\n truncateLine,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport { formatDimensionNote, resizeImage } from '@earendil-works/pi-coding-agent';\n\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool';\nimport {\n applyImageLimits,\n imageLimits,\n type ReadContentPart,\n type ReadImageResizeOperations,\n type ReadTextPart,\n} from '../readImage';\n\nconst resizeOperations: ReadImageResizeOperations = {\n resize: resizeImage,\n formatDimensionNote: (result) => formatDimensionNote(result),\n};\n\nexport interface ReadToolResult {\n content: ReadContentPart[];\n details?: ReadToolDetails;\n}\n\ninterface ReadTextToolResult {\n content: ReadTextPart[];\n details?: ReadToolDetails;\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n\nexport function isImagePath(filePath: string): string | undefined {\n const extension = filePath.toLowerCase().match(/\\.([a-z0-9]+)$/u)?.[1];\n if (!extension) return undefined;\n const mimeTypes: Record<string, string> = {\n avif: 'image/avif',\n gif: 'image/gif',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n };\n return mimeTypes[extension];\n}\n\nexport async function executeHeadlessRead(\n params: ReadParams,\n cwd: string,\n signal: AbortSignal | undefined,\n): Promise<ReadToolResult> {\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(params.path, cwd);\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n const mimeType = isImagePath(absolutePath);\n if (mimeType) {\n const content: ReadContentPart[] = [\n { type: 'text', text: `Read image file ${displayPath(absolutePath, cwd)} [${mimeType}]` },\n { type: 'image', data: bytes.toString('base64'), mimeType },\n ];\n return { content: await applyImageLimits(content, imageLimits(), resizeOperations) };\n }\n if (!(await isWritableFile(absolutePath))) {\n return { content: [{ type: 'text', text: decodeUtf8(bytes, displayPath(absolutePath, cwd)) }] };\n }\n return createTaggedReadResult(bytes, displayPath(absolutePath, cwd), params);\n}\n\nexport function createTaggedReadResult(bytes: Buffer, path: string, params: ReadParams): ReadTextToolResult {\n const lines = splitLines(decodeUtf8(bytes, path));\n const startIndex = params.offset === undefined ? 0 : params.offset - 1;\n if (startIndex >= lines.length) {\n throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);\n }\n\n const endIndex = params.limit === undefined ? lines.length : Math.min(lines.length, startIndex + params.limit);\n const selected = lines.slice(startIndex, endIndex);\n const header = formatFileHeader(path, computeFileTag(bytes));\n const compactedLines: number[] = [];\n const headerBytes = Buffer.byteLength(header, 'utf8') + 1;\n const tagged = selected.map((line, index) => {\n const lineNumber = startIndex + index + 1;\n const full = formatTaggedLine(line, lineNumber);\n if (Buffer.byteLength(full, 'utf8') <= DEFAULT_MAX_BYTES - headerBytes) return full;\n compactedLines.push(lineNumber);\n return formatTaggedLine(truncateLine(line).text, lineNumber, '', line);\n });\n const truncation = truncateHead([header, ...tagged].join('\\n'));\n let text = truncation.content;\n let details: ReadToolDetails | undefined;\n\n if (truncation.truncated) {\n const shownLines = Math.max(0, truncation.outputLines - 1);\n const nextOffset = startIndex + shownLines + 1;\n const reason = truncation.truncatedBy === 'bytes' ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : '';\n text += `\\n\\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;\n details = { truncation };\n } else {\n const notices: string[] = [];\n if (compactedLines.length > 0) {\n notices.push(`Lines ${compactedLines.join(', ')} shown compactly. Their anchors hash the full original lines`);\n }\n if (endIndex < lines.length) {\n notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);\n }\n if (notices.length > 0) text += `\\n\\n[${notices.join('. ')}.]`;\n }\n\n return { content: [{ type: 'text', text }], details };\n}\n\nexport function isImageRead(content: readonly { readonly type: string; readonly text?: string }[]): boolean {\n return content.some((part) => part.type === 'image' || part.text?.startsWith('Read image file') === true);\n}\n\nexport function createHeadlessReadTool(): DoomHeadlessTool<typeof ReadParamsSchema> {\n return {\n name: 'read',\n label: 'read',\n description:\n 'Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.',\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n execute: async (\n _toolCallId: string,\n params: ReadParams,\n signal: AbortSignal | undefined,\n _onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ) => executeHeadlessRead(params, context.cwd, signal),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { ReadParams, ReadParamsSchema } from "../../schemas/readTool.cjs";
2
+ import { ReadContentPart } from "../readImage/index.cjs";
3
+ import { ReadToolDetails } from "@earendil-works/pi-coding-agent";
4
+ import { DoomHeadlessTool } from "@agimon-ai/doompi-core/headless";
5
+ //#region src/services/readTool/index.d.ts
6
+ export interface ReadToolResult {
7
+ content: ReadContentPart[];
8
+ details?: ReadToolDetails;
9
+ }
10
+ export declare function executeHeadlessRead(params: ReadParams, cwd: string, signal: AbortSignal | undefined): Promise<ReadToolResult>;
11
+ export declare function createHeadlessReadTool(): DoomHeadlessTool<typeof ReadParamsSchema>;
12
+ //#endregion
13
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/readTool/index.ts"],"mappings":";;;;;iBAsCiB;EACf,SAAS;EACT,UAAU;;wBA0BU,oBACpB,QAAQ,YACR,aACA,QAAQ,0BACP,QAAQ;wBAkEK,0BAA0B,wBAAwB"}
@@ -0,0 +1,13 @@
1
+ import { ReadParams, ReadParamsSchema } from "../../schemas/readTool.mjs";
2
+ import { ReadContentPart } from "../readImage/index.mjs";
3
+ import { ReadToolDetails } from "@earendil-works/pi-coding-agent";
4
+ import { DoomHeadlessTool } from "@agimon-ai/doompi-core/headless";
5
+ //#region src/services/readTool/index.d.ts
6
+ export interface ReadToolResult {
7
+ content: ReadContentPart[];
8
+ details?: ReadToolDetails;
9
+ }
10
+ export declare function executeHeadlessRead(params: ReadParams, cwd: string, signal: AbortSignal | undefined): Promise<ReadToolResult>;
11
+ export declare function createHeadlessReadTool(): DoomHeadlessTool<typeof ReadParamsSchema>;
12
+ //#endregion
13
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/readTool/index.ts"],"mappings":";;;;;iBAsCiB;EACf,SAAS;EACT,UAAU;;wBA0BU,oBACpB,QAAQ,YACR,aACA,QAAQ,0BACP,QAAQ;wBAkEK,0BAA0B,wBAAwB"}
@@ -0,0 +1,3 @@
1
+ import{ReadParamsSchema as e}from"../../schemas/readTool.mjs";import{applyImageLimits as t,imageLimits as n}from"../readImage/index.mjs";import{readFile as r}from"node:fs/promises";import{computeFileTag as i,decodeUtf8 as a,displayPath as o,isWritableFile as s,resolveReadInputPath as c}from"@agimon-ai/doompi-hashline/files";import{DEFAULT_MAX_BYTES as l,formatDimensionNote as u,formatSize as d,resizeImage as f,truncateHead as p,truncateLine as m}from"@earendil-works/pi-coding-agent";import{formatFileHeader as h,formatTaggedLine as g,splitLines as _}from"@agimon-ai/doompi-hashline";const v={resize:f,formatDimensionNote:e=>u(e)};function y(e){if(e?.aborted)throw Error(`Operation aborted`)}function b(e){let t=e.toLowerCase().match(/\.([a-z0-9]+)$/u)?.[1];if(t)return{avif:`image/avif`,gif:`image/gif`,jpeg:`image/jpeg`,jpg:`image/jpeg`,png:`image/png`,webp:`image/webp`}[t]}async function x(e,i,l){y(l);let u=await c(e.path,i),d=await r(u);y(l);let f=b(u);if(f){let e=[{type:`text`,text:`Read image file ${o(u,i)} [${f}]`},{type:`image`,data:d.toString(`base64`),mimeType:f}];return{content:await t(e,n(),v)}}return await s(u)?S(d,o(u,i),e):{content:[{type:`text`,text:a(d,o(u,i))}]}}function S(e,t,n){let r=_(a(e,t)),o=n.offset===void 0?0:n.offset-1;if(o>=r.length)throw Error(`Offset ${n.offset} is beyond end of file (${r.length} lines total).`);let s=n.limit===void 0?r.length:Math.min(r.length,o+n.limit),c=r.slice(o,s),u=h(t,i(e)),f=[],v=Buffer.byteLength(u,`utf8`)+1,y=c.map((e,t)=>{let n=o+t+1,r=g(e,n);return Buffer.byteLength(r,`utf8`)<=l-v?r:(f.push(n),g(m(e).text,n,``,e))}),b=p([u,...y].join(`
2
+ `)),x=b.content,S;if(b.truncated){let e=Math.max(0,b.outputLines-1),t=o+e+1,n=b.truncatedBy===`bytes`?`, ${d(l)} limit`:``;x+=`\n\n[Showing ${e} anchored lines${n}. Use offset=${t} to continue.]`,S={truncation:b}}else{let e=[];f.length>0&&e.push(`Lines ${f.join(`, `)} shown compactly. Their anchors hash the full original lines`),s<r.length&&e.push(`${r.length-s} more lines in file. Use offset=${s+1} to continue`),e.length>0&&(x+=`\n\n[${e.join(`. `)}.]`)}return{content:[{type:`text`,text:x}],details:S}}function C(e){return e.some(e=>e.type===`image`||e.text?.startsWith(`Read image file`)===!0)}function w(){return{name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,promptGuidelines:[`Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.`,`Continue large reads with offset until the required anchored lines are visible.`],parameters:e,executionMode:`parallel`,execute:async(e,t,n,r,i)=>x(t,i.cwd,n)}}export{y as assertNotAborted,w as createHeadlessReadTool,S as createTaggedReadResult,x as executeHeadlessRead,b as isImagePath,C as isImageRead};
3
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/services/readTool/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/doompi-core/headless';\nimport { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/doompi-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/doompi-hashline/files';\nimport {\n DEFAULT_MAX_BYTES,\n formatSize,\n truncateHead,\n truncateLine,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport { formatDimensionNote, resizeImage } from '@earendil-works/pi-coding-agent';\n\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool';\nimport {\n applyImageLimits,\n imageLimits,\n type ReadContentPart,\n type ReadImageResizeOperations,\n type ReadTextPart,\n} from '../readImage';\n\nconst resizeOperations: ReadImageResizeOperations = {\n resize: resizeImage,\n formatDimensionNote: (result) => formatDimensionNote(result),\n};\n\nexport interface ReadToolResult {\n content: ReadContentPart[];\n details?: ReadToolDetails;\n}\n\ninterface ReadTextToolResult {\n content: ReadTextPart[];\n details?: ReadToolDetails;\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n\nexport function isImagePath(filePath: string): string | undefined {\n const extension = filePath.toLowerCase().match(/\\.([a-z0-9]+)$/u)?.[1];\n if (!extension) return undefined;\n const mimeTypes: Record<string, string> = {\n avif: 'image/avif',\n gif: 'image/gif',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n };\n return mimeTypes[extension];\n}\n\nexport async function executeHeadlessRead(\n params: ReadParams,\n cwd: string,\n signal: AbortSignal | undefined,\n): Promise<ReadToolResult> {\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(params.path, cwd);\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n const mimeType = isImagePath(absolutePath);\n if (mimeType) {\n const content: ReadContentPart[] = [\n { type: 'text', text: `Read image file ${displayPath(absolutePath, cwd)} [${mimeType}]` },\n { type: 'image', data: bytes.toString('base64'), mimeType },\n ];\n return { content: await applyImageLimits(content, imageLimits(), resizeOperations) };\n }\n if (!(await isWritableFile(absolutePath))) {\n return { content: [{ type: 'text', text: decodeUtf8(bytes, displayPath(absolutePath, cwd)) }] };\n }\n return createTaggedReadResult(bytes, displayPath(absolutePath, cwd), params);\n}\n\nexport function createTaggedReadResult(bytes: Buffer, path: string, params: ReadParams): ReadTextToolResult {\n const lines = splitLines(decodeUtf8(bytes, path));\n const startIndex = params.offset === undefined ? 0 : params.offset - 1;\n if (startIndex >= lines.length) {\n throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);\n }\n\n const endIndex = params.limit === undefined ? lines.length : Math.min(lines.length, startIndex + params.limit);\n const selected = lines.slice(startIndex, endIndex);\n const header = formatFileHeader(path, computeFileTag(bytes));\n const compactedLines: number[] = [];\n const headerBytes = Buffer.byteLength(header, 'utf8') + 1;\n const tagged = selected.map((line, index) => {\n const lineNumber = startIndex + index + 1;\n const full = formatTaggedLine(line, lineNumber);\n if (Buffer.byteLength(full, 'utf8') <= DEFAULT_MAX_BYTES - headerBytes) return full;\n compactedLines.push(lineNumber);\n return formatTaggedLine(truncateLine(line).text, lineNumber, '', line);\n });\n const truncation = truncateHead([header, ...tagged].join('\\n'));\n let text = truncation.content;\n let details: ReadToolDetails | undefined;\n\n if (truncation.truncated) {\n const shownLines = Math.max(0, truncation.outputLines - 1);\n const nextOffset = startIndex + shownLines + 1;\n const reason = truncation.truncatedBy === 'bytes' ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : '';\n text += `\\n\\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;\n details = { truncation };\n } else {\n const notices: string[] = [];\n if (compactedLines.length > 0) {\n notices.push(`Lines ${compactedLines.join(', ')} shown compactly. Their anchors hash the full original lines`);\n }\n if (endIndex < lines.length) {\n notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);\n }\n if (notices.length > 0) text += `\\n\\n[${notices.join('. ')}.]`;\n }\n\n return { content: [{ type: 'text', text }], details };\n}\n\nexport function isImageRead(content: readonly { readonly type: string; readonly text?: string }[]): boolean {\n return content.some((part) => part.type === 'image' || part.text?.startsWith('Read image file') === true);\n}\n\nexport function createHeadlessReadTool(): DoomHeadlessTool<typeof ReadParamsSchema> {\n return {\n name: 'read',\n label: 'read',\n description:\n 'Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.',\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n execute: async (\n _toolCallId: string,\n params: ReadParams,\n signal: AbortSignal | undefined,\n _onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ) => executeHeadlessRead(params, context.cwd, signal),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../schemas/readTool.cjs"),t=require("../services/readImage/index.cjs"),n=require("../services/piReadImage/index.cjs"),r=require("../services/readTool/index.cjs");let i=require("node:fs/promises"),a=require("@agimon-ai/doompi-hashline/files"),o=require("@agimon-ai/doompi-ui/hashlineRendering"),s=require("@earendil-works/pi-coding-agent");function c(c=a.isWritableFile){return{name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${(0,s.formatSize)(s.DEFAULT_MAX_BYTES)}.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,promptGuidelines:[`Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.`,`Continue large reads with offset until the required anchored lines are visible.`],parameters:e.ReadParamsSchema,executionMode:`parallel`,renderShell:`self`,async execute(e,o,l,u,d){let f=o;r.assertNotAborted(l);let p=await(0,a.resolveReadInputPath)(f.path,d.cwd),m=await(0,s.createReadToolDefinition)(d.cwd,{autoResizeImages:!1}).execute(e,{...f,path:p},l,u,d);if(r.isImageRead(m.content))return r.assertNotAborted(l),{...m,content:await n.applyImageLimits(m.content,t.imageLimits())};r.assertNotAborted(l);let h=await c(p);if(r.assertNotAborted(l),!h)return m;let g=await(0,i.readFile)(p);return r.assertNotAborted(l),r.createTaggedReadResult(g,(0,a.displayPath)(p,d.cwd),f)},renderCall(e,t){let n=e,r=[n.offset===void 0?void 0:`from ${n.offset}`,n.limit===void 0?void 0:`${n.limit} lines`].filter(e=>e!==void 0);return(0,o.renderHashlineCall)(`read`,n.path,r,t)},renderResult(e,t,n,r){return(0,o.renderHashlineResult)(e,t,n,r,`read`)}}}exports.createHashlineReadTool=c;
2
+ //# sourceMappingURL=piRead.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piRead.cjs","names":["isWritableFile","formatSize","DEFAULT_MAX_BYTES","ReadParamsSchema","resolveReadInputPath","createReadToolDefinition","isImageRead","applyImageLimits","imageLimits","readFile","createTaggedReadResult","displayPath","renderHashlineCall","renderHashlineResult"],"sources":["../../src/tools/piRead.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport { displayPath, isWritableFile, resolveReadInputPath } from '@agimon-ai/doompi-hashline/files';\nimport { renderHashlineCall, renderHashlineResult } from '@agimon-ai/doompi-ui/hashlineRendering';\nimport {\n createReadToolDefinition,\n DEFAULT_MAX_BYTES,\n formatSize,\n type AgentToolResult,\n type ToolDefinition,\n} from '@earendil-works/pi-coding-agent';\n\nimport { ReadParamsSchema, type ReadParams } from '../schemas/readTool';\nimport { applyImageLimits, imageLimits } from '../services/piReadImage';\nimport { assertNotAborted, createTaggedReadResult, isImageRead } from '../services/readTool';\nexport { assertNotAborted, createTaggedReadResult, isImageRead } from '../services/readTool';\n\ntype WritableCheck = (path: string) => Promise<boolean>;\n\n/** Register the snapshot-bound replacement for Pi's read tool. */\nexport function createHashlineReadTool(\n writable: WritableCheck = isWritableFile,\n): ToolDefinition<typeof ReadParamsSchema> {\n return {\n name: 'read',\n label: 'read',\n description: `Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${formatSize(DEFAULT_MAX_BYTES)}.`,\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n renderShell: 'self',\n async execute(toolCallId, params, signal, onUpdate, ctx) {\n const input = params as ReadParams;\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(input.path, ctx.cwd);\n // Pi resizes at a cap it hardcodes, so it is told not to: the block comes\n // back converted but full size, and the machine's configured cap is\n // applied below. Everything else about the native read is kept.\n const nativeRead = createReadToolDefinition(ctx.cwd, { autoResizeImages: false });\n const nativeResult = await nativeRead.execute(\n toolCallId,\n { ...input, path: absolutePath },\n signal,\n onUpdate,\n ctx,\n );\n if (isImageRead(nativeResult.content)) {\n assertNotAborted(signal);\n return { ...nativeResult, content: await applyImageLimits(nativeResult.content, imageLimits()) };\n }\n\n assertNotAborted(signal);\n const canEdit = await writable(absolutePath);\n assertNotAborted(signal);\n if (!canEdit) return nativeResult;\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n return createTaggedReadResult(\n bytes,\n displayPath(absolutePath, ctx.cwd),\n input,\n ) as unknown as AgentToolResult<unknown>;\n },\n renderCall(args, theme) {\n const input = args as ReadParams;\n const details = [\n input.offset === undefined ? undefined : `from ${input.offset}`,\n input.limit === undefined ? undefined : `${input.limit} lines`,\n ].filter((value): value is string => value !== undefined);\n return renderHashlineCall('read', input.path, details, theme);\n },\n renderResult(result, options, theme, context) {\n return renderHashlineResult(result, options, theme, context, 'read');\n },\n };\n}\n"],"mappings":"oWAoBA,SAAgB,EACd,EAA0BA,EAAAA,eACe,CACzC,MAAO,CACL,KAAM,OACN,MAAO,OACP,YAAa,mKAAA,EAAkKC,EAAAA,WAAAA,CAAWC,EAAAA,iBAAiB,EAAE,GAC7M,cAAe,sDACf,iBAAkB,CAChB,sHACA,iFACF,EACA,WAAYC,EAAAA,iBACZ,cAAe,WACf,YAAa,OACb,MAAM,QAAQ,EAAY,EAAQ,EAAQ,EAAU,EAAK,CACvD,IAAM,EAAQ,EACd,EAAA,iBAAiB,CAAM,EACvB,IAAM,EAAe,MAAA,EAAMC,EAAAA,qBAAAA,CAAqB,EAAM,KAAM,EAAI,GAAG,EAK7D,EAAe,MAAA,EADFC,EAAAA,yBAAAA,CAAyB,EAAI,IAAK,CAAE,iBAAkB,EAAM,CAC3C,CAAC,CAAC,QACpC,EACA,CAAE,GAAG,EAAO,KAAM,CAAa,EAC/B,EACA,EACA,CACF,EACA,GAAIC,EAAAA,YAAY,EAAa,OAAO,EAElC,OADA,EAAA,iBAAiB,CAAM,EAChB,CAAE,GAAG,EAAc,QAAS,MAAMC,EAAAA,iBAAiB,EAAa,QAASC,EAAAA,YAAY,CAAC,CAAE,EAGjG,EAAA,iBAAiB,CAAM,EACvB,IAAM,EAAU,MAAM,EAAS,CAAY,EAE3C,GADA,EAAA,iBAAiB,CAAM,EACnB,CAAC,EAAS,OAAO,EACrB,IAAM,EAAQ,MAAA,EAAMC,EAAAA,SAAAA,CAAS,CAAY,EAEzC,OADA,EAAA,iBAAiB,CAAM,EAChBC,EAAAA,uBACL,GAAA,EACAC,EAAAA,YAAAA,CAAY,EAAc,EAAI,GAAG,EACjC,CACF,CACF,EACA,WAAW,EAAM,EAAO,CACtB,IAAM,EAAQ,EACR,EAAU,CACd,EAAM,SAAW,IAAA,GAAY,IAAA,GAAY,QAAQ,EAAM,SACvD,EAAM,QAAU,IAAA,GAAY,IAAA,GAAY,GAAG,EAAM,MAAM,OACzD,CAAC,CAAC,OAAQ,GAA2B,IAAU,IAAA,EAAS,EACxD,OAAA,EAAOC,EAAAA,mBAAAA,CAAmB,OAAQ,EAAM,KAAM,EAAS,CAAK,CAC9D,EACA,aAAa,EAAQ,EAAS,EAAO,EAAS,CAC5C,OAAA,EAAOC,EAAAA,qBAAAA,CAAqB,EAAQ,EAAS,EAAO,EAAS,MAAM,CACrE,CACF,CACF"}
@@ -0,0 +1,9 @@
1
+ import { ReadParamsSchema } from "../schemas/readTool.cjs";
2
+ import "../services/readTool/index.cjs";
3
+ import { ToolDefinition } from "@earendil-works/pi-coding-agent";
4
+ //#region src/tools/piRead.d.ts
5
+ type WritableCheck = (path: string) => Promise<boolean>;
6
+ /** Register the snapshot-bound replacement for Pi's read tool. */
7
+ export declare function createHashlineReadTool(writable?: WritableCheck): ToolDefinition<typeof ReadParamsSchema>;
8
+ //#endregion
9
+ //# sourceMappingURL=piRead.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piRead.d.cts","names":[],"sources":["../../src/tools/piRead.ts"],"mappings":";;;;KAiBK,iBAAiB,iBAAiB;;wBAGvB,uBACd,WAAU,gBACT,sBAAsB"}
@@ -0,0 +1,9 @@
1
+ import { ReadParamsSchema } from "../schemas/readTool.mjs";
2
+ import "../services/readTool/index.mjs";
3
+ import { ToolDefinition } from "@earendil-works/pi-coding-agent";
4
+ //#region src/tools/piRead.d.ts
5
+ type WritableCheck = (path: string) => Promise<boolean>;
6
+ /** Register the snapshot-bound replacement for Pi's read tool. */
7
+ export declare function createHashlineReadTool(writable?: WritableCheck): ToolDefinition<typeof ReadParamsSchema>;
8
+ //#endregion
9
+ //# sourceMappingURL=piRead.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piRead.d.mts","names":[],"sources":["../../src/tools/piRead.ts"],"mappings":";;;;KAiBK,iBAAiB,iBAAiB;;wBAGvB,uBACd,WAAU,gBACT,sBAAsB"}
@@ -0,0 +1,2 @@
1
+ import{ReadParamsSchema as e}from"../schemas/readTool.mjs";import{imageLimits as t}from"../services/readImage/index.mjs";import{applyImageLimits as n}from"../services/piReadImage/index.mjs";import{assertNotAborted as r,createTaggedReadResult as i,isImageRead as a}from"../services/readTool/index.mjs";import{readFile as o}from"node:fs/promises";import{displayPath as s,isWritableFile as c,resolveReadInputPath as l}from"@agimon-ai/doompi-hashline/files";import{renderHashlineCall as u,renderHashlineResult as d}from"@agimon-ai/doompi-ui/hashlineRendering";import{DEFAULT_MAX_BYTES as f,createReadToolDefinition as p,formatSize as m}from"@earendil-works/pi-coding-agent";function h(h=c){return{name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${m(f)}.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,promptGuidelines:[`Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.`,`Continue large reads with offset until the required anchored lines are visible.`],parameters:e,executionMode:`parallel`,renderShell:`self`,async execute(e,c,u,d,f){let m=c;r(u);let g=await l(m.path,f.cwd),_=await p(f.cwd,{autoResizeImages:!1}).execute(e,{...m,path:g},u,d,f);if(a(_.content))return r(u),{..._,content:await n(_.content,t())};r(u);let v=await h(g);if(r(u),!v)return _;let y=await o(g);return r(u),i(y,s(g,f.cwd),m)},renderCall(e,t){let n=e,r=[n.offset===void 0?void 0:`from ${n.offset}`,n.limit===void 0?void 0:`${n.limit} lines`].filter(e=>e!==void 0);return u(`read`,n.path,r,t)},renderResult(e,t,n,r){return d(e,t,n,r,`read`)}}}export{h as createHashlineReadTool};
2
+ //# sourceMappingURL=piRead.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piRead.mjs","names":[],"sources":["../../src/tools/piRead.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport { displayPath, isWritableFile, resolveReadInputPath } from '@agimon-ai/doompi-hashline/files';\nimport { renderHashlineCall, renderHashlineResult } from '@agimon-ai/doompi-ui/hashlineRendering';\nimport {\n createReadToolDefinition,\n DEFAULT_MAX_BYTES,\n formatSize,\n type AgentToolResult,\n type ToolDefinition,\n} from '@earendil-works/pi-coding-agent';\n\nimport { ReadParamsSchema, type ReadParams } from '../schemas/readTool';\nimport { applyImageLimits, imageLimits } from '../services/piReadImage';\nimport { assertNotAborted, createTaggedReadResult, isImageRead } from '../services/readTool';\nexport { assertNotAborted, createTaggedReadResult, isImageRead } from '../services/readTool';\n\ntype WritableCheck = (path: string) => Promise<boolean>;\n\n/** Register the snapshot-bound replacement for Pi's read tool. */\nexport function createHashlineReadTool(\n writable: WritableCheck = isWritableFile,\n): ToolDefinition<typeof ReadParamsSchema> {\n return {\n name: 'read',\n label: 'read',\n description: `Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${formatSize(DEFAULT_MAX_BYTES)}.`,\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n renderShell: 'self',\n async execute(toolCallId, params, signal, onUpdate, ctx) {\n const input = params as ReadParams;\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(input.path, ctx.cwd);\n // Pi resizes at a cap it hardcodes, so it is told not to: the block comes\n // back converted but full size, and the machine's configured cap is\n // applied below. Everything else about the native read is kept.\n const nativeRead = createReadToolDefinition(ctx.cwd, { autoResizeImages: false });\n const nativeResult = await nativeRead.execute(\n toolCallId,\n { ...input, path: absolutePath },\n signal,\n onUpdate,\n ctx,\n );\n if (isImageRead(nativeResult.content)) {\n assertNotAborted(signal);\n return { ...nativeResult, content: await applyImageLimits(nativeResult.content, imageLimits()) };\n }\n\n assertNotAborted(signal);\n const canEdit = await writable(absolutePath);\n assertNotAborted(signal);\n if (!canEdit) return nativeResult;\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n return createTaggedReadResult(\n bytes,\n displayPath(absolutePath, ctx.cwd),\n input,\n ) as unknown as AgentToolResult<unknown>;\n },\n renderCall(args, theme) {\n const input = args as ReadParams;\n const details = [\n input.offset === undefined ? undefined : `from ${input.offset}`,\n input.limit === undefined ? undefined : `${input.limit} lines`,\n ].filter((value): value is string => value !== undefined);\n return renderHashlineCall('read', input.path, details, theme);\n },\n renderResult(result, options, theme, context) {\n return renderHashlineResult(result, options, theme, context, 'read');\n },\n };\n}\n"],"mappings":"8pBAoBA,SAAgB,EACd,EAA0B,EACe,CACzC,MAAO,CACL,KAAM,OACN,MAAO,OACP,YAAa,kKAAkK,EAAW,CAAiB,EAAE,GAC7M,cAAe,sDACf,iBAAkB,CAChB,sHACA,iFACF,EACA,WAAY,EACZ,cAAe,WACf,YAAa,OACb,MAAM,QAAQ,EAAY,EAAQ,EAAQ,EAAU,EAAK,CACvD,IAAM,EAAQ,EACd,EAAiB,CAAM,EACvB,IAAM,EAAe,MAAM,EAAqB,EAAM,KAAM,EAAI,GAAG,EAK7D,EAAe,MADF,EAAyB,EAAI,IAAK,CAAE,iBAAkB,EAAM,CAC3C,CAAC,CAAC,QACpC,EACA,CAAE,GAAG,EAAO,KAAM,CAAa,EAC/B,EACA,EACA,CACF,EACA,GAAI,EAAY,EAAa,OAAO,EAElC,OADA,EAAiB,CAAM,EAChB,CAAE,GAAG,EAAc,QAAS,MAAM,EAAiB,EAAa,QAAS,EAAY,CAAC,CAAE,EAGjG,EAAiB,CAAM,EACvB,IAAM,EAAU,MAAM,EAAS,CAAY,EAE3C,GADA,EAAiB,CAAM,EACnB,CAAC,EAAS,OAAO,EACrB,IAAM,EAAQ,MAAM,EAAS,CAAY,EAEzC,OADA,EAAiB,CAAM,EAChB,EACL,EACA,EAAY,EAAc,EAAI,GAAG,EACjC,CACF,CACF,EACA,WAAW,EAAM,EAAO,CACtB,IAAM,EAAQ,EACR,EAAU,CACd,EAAM,SAAW,IAAA,GAAY,IAAA,GAAY,QAAQ,EAAM,SACvD,EAAM,QAAU,IAAA,GAAY,IAAA,GAAY,GAAG,EAAM,MAAM,OACzD,CAAC,CAAC,OAAQ,GAA2B,IAAU,IAAA,EAAS,EACxD,OAAO,EAAmB,OAAQ,EAAM,KAAM,EAAS,CAAK,CAC9D,EACA,aAAa,EAAQ,EAAS,EAAO,EAAS,CAC5C,OAAO,EAAqB,EAAQ,EAAS,EAAO,EAAS,MAAM,CACrE,CACF,CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-read",
3
- "version": "0.0.1-alpha.41",
3
+ "version": "0.0.1-alpha.43",
4
4
  "description": "Snapshot-bound hashline read tool for Pi and DoomPi.",
5
5
  "keywords": [
6
6
  "ai",
@@ -27,7 +27,7 @@
27
27
  "files": [
28
28
  "dist",
29
29
  "src/web",
30
- "src/exports/webClient.ts",
30
+ "src/extensions/web.ts",
31
31
  "!src/web/**/*.stories.tsx"
32
32
  ],
33
33
  "type": "module",
@@ -45,6 +45,11 @@
45
45
  "import": "./dist/extensions/pi.mjs",
46
46
  "require": "./dist/extensions/pi.cjs"
47
47
  },
48
+ "./extensions/server": {
49
+ "types": "./dist/extensions/server.d.mts",
50
+ "import": "./dist/extensions/server.mjs",
51
+ "require": "./dist/extensions/server.cjs"
52
+ },
48
53
  "./package.json": "./package.json"
49
54
  },
50
55
  "publishConfig": {
@@ -53,12 +58,11 @@
53
58
  "dependencies": {
54
59
  "@deepseek-ai/cordis": "4.0.2",
55
60
  "typebox": "1.3.25",
56
- "@agimon-ai/doompi-config": "0.0.1-alpha.68",
57
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.68",
58
- "@agimon-ai/doompi-hashline": "0.0.1-alpha.39",
59
- "@agimon-ai/doompi-ui": "0.0.1-alpha.69",
60
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.28",
61
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.31"
61
+ "@agimon-ai/doompi-core": "0.0.1-alpha.71",
62
+ "@agimon-ai/doompi-config": "0.0.1-alpha.70",
63
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.71",
64
+ "@agimon-ai/doompi-hashline": "0.0.1-alpha.40",
65
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.29"
62
66
  },
63
67
  "devDependencies": {
64
68
  "@earendil-works/pi-coding-agent": "0.85.1",
@@ -89,10 +93,24 @@
89
93
  "engines": {
90
94
  "node": ">=22.19.0"
91
95
  },
96
+ "doompiServer": {
97
+ "entry": "./src/extensions/server.ts",
98
+ "dist": "./dist/extensions/server.mjs",
99
+ "scopes": [
100
+ "session"
101
+ ],
102
+ "contracts": {
103
+ "entry": "./src/exports/apiContracts.ts",
104
+ "dist": "./dist/api-contracts.mjs"
105
+ }
106
+ },
92
107
  "doompiWeb": {
93
108
  "pluginId": "read",
94
109
  "channels": [],
95
- "client": "./src/exports/webClient.ts"
110
+ "client": "./src/extensions/web.ts",
111
+ "scopes": [
112
+ "session"
113
+ ]
96
114
  },
97
115
  "pi": {
98
116
  "extensions": [
@@ -0,0 +1,14 @@
1
+ import { defineWebPlugin } from '@agimon-ai/doompi-core/web';
2
+
3
+ import { ReadToolMessage } from '../web/components/ReadToolMessage';
4
+
5
+ /**
6
+ * This package's cockpit presence: the read tool's timeline card, the web
7
+ * half of the TUI's renderCall and renderResult for hashline reads.
8
+ */
9
+ export const webPlugin = defineWebPlugin({
10
+ id: 'read',
11
+ session: {
12
+ toolRenderers: [{ tools: ['read'], message: ReadToolMessage }],
13
+ },
14
+ });
@@ -1,3 +1,4 @@
1
+ import type { ToolMessageRenderProps } from '@agimon-ai/doompi-core/web';
1
2
  import {
2
3
  HashlineLines,
3
4
  hashlineBody,
@@ -9,8 +10,8 @@ import {
9
10
  ToolPathLink,
10
11
  toolTone,
11
12
  } from '@agimon-ai/doompi-web-components';
12
- import type { ToolMessageRenderProps } from '@agimon-ai/doompi-web-contracts';
13
- import { readCallView } from '../lib/readToolView.ts';
13
+
14
+ import { readCallView } from '../lib/readToolView';
14
15
 
15
16
  const IMAGE_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);
16
17
 
@@ -19,5 +19,5 @@
19
19
  "forceConsistentCasingInFileNames": true,
20
20
  "types": ["node"]
21
21
  },
22
- "include": [".", "../types", "../exports/webClient.ts"]
22
+ "include": [".", "../types", "../extensions/web.ts"]
23
23
  }
@@ -1,2 +0,0 @@
1
- const e=require("./readTool.cjs");let t=require("@agimon-ai/doompi-extension-contracts/cordis-host"),n=require("@agimon-ai/doompi-extension-contracts/tool-overrides");const r=`@agimon-ai/doompi-read`;function i(t,i){t.inject([n.DOOM_TOOL_OVERRIDES_SERVICE],t=>{let a=(0,n.requireDoomToolOverrides)(t).claim({source:r,tools:[`read`]});if(a.granted){try{e.registerHashlineReadTool(i)}catch(e){throw a.dispose(),e}return()=>a.dispose()}})}async function a(e){let n=await(0,t.connectDoomCordisHost)(e,r),i=n.root.plugin(o,{pi:e});try{await i}catch(e){try{await i.dispose()}finally{await n.dispose()}throw e}let a;e.on(`session_shutdown`,()=>a??=(async()=>{try{await i.dispose()}finally{await n.dispose()}})())}function o(e,t){i(e,t.pi)}exports.activateDoomPiReadExtension=a,exports.default=a,exports.installDoomPiReadRuntime=i;
2
- //# sourceMappingURL=extension.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.cjs","names":["DOOM_TOOL_OVERRIDES_SERVICE","requireDoomToolOverrides","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_TOOL_OVERRIDES_SERVICE,\n requireDoomToolOverrides,\n} from '@agimon-ai/doompi-extension-contracts/tool-overrides';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerHashlineReadTool } from './readTool.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-read';\n\n/** Install the hashline read tool after claiming Pi's shared read name. */\nexport function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void {\n cordis.inject([DOOM_TOOL_OVERRIDES_SERVICE], (overridesContext) => {\n const registration = requireDoomToolOverrides(overridesContext).claim({\n source: PACKAGE_SOURCE,\n tools: ['read'],\n });\n if (!registration.granted) return;\n try {\n registerHashlineReadTool(pi);\n } catch (error) {\n registration.dispose();\n throw error;\n }\n return () => registration.dispose();\n });\n}\n\n/** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */\nexport async function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin(doomPiReadPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface DoomPiReadPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction doomPiReadPlugin(cordis: Context, config: DoomPiReadPluginConfig): void {\n installDoomPiReadRuntime(cordis, config.pi);\n}\n\nexport default activateDoomPiReadExtension;\n"],"mappings":"uKASA,MAAM,EAAiB,yBAGvB,SAAgB,EAAyB,EAAiB,EAAwB,CAChF,EAAO,OAAO,CAACA,EAAAA,2BAA2B,EAAI,GAAqB,CACjE,IAAM,GAAA,EAAeC,EAAAA,yBAAAA,CAAyB,CAAgB,CAAC,CAAC,MAAM,CACpE,OAAQ,EACR,MAAO,CAAC,MAAM,CAChB,CAAC,EACI,KAAa,QAClB,IAAI,CACF,EAAA,yBAAyB,CAAE,CAC7B,OAAS,EAAO,CAEd,MADA,EAAa,QAAQ,EACf,CACR,CACA,UAAa,EAAa,QAAQ,CADlC,CAEF,CAAC,CACH,CAGA,eAAsB,EAA4B,EAAiC,CACjF,IAAM,EAAa,MAAA,EAAMC,EAAAA,sBAAAA,CAAsB,EAAI,CAAc,EAC3D,EAAQ,EAAW,KAAK,OAAO,EAAkB,CAAE,IAAG,CAAC,EAC7D,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CAEA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF,CAMA,SAAS,EAAiB,EAAiB,EAAsC,CAC/E,EAAyB,EAAQ,EAAO,EAAE,CAC5C"}
@@ -1,10 +0,0 @@
1
- import { Context } from "@deepseek-ai/cordis";
2
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
- //#region src/adapters/pi/extension.d.ts
4
- /** Install the hashline read tool after claiming Pi's shared read name. */
5
- export declare function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void;
6
- /** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */
7
- export declare function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void>;
8
- //#endregion
9
- export { activateDoomPiReadExtension as default };
10
- //# sourceMappingURL=extension.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;wBAYgB,yBAAyB,QAAQ,SAAS,IAAI;;wBAkBxC,4BAA4B,IAAI,eAAe"}
@@ -1,10 +0,0 @@
1
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { Context } from "@deepseek-ai/cordis";
3
- //#region src/adapters/pi/extension.d.ts
4
- /** Install the hashline read tool after claiming Pi's shared read name. */
5
- export declare function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void;
6
- /** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */
7
- export declare function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void>;
8
- //#endregion
9
- export { activateDoomPiReadExtension as default };
10
- //# sourceMappingURL=extension.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;wBAYgB,yBAAyB,QAAQ,SAAS,IAAI;;wBAkBxC,4BAA4B,IAAI,eAAe"}
@@ -1,2 +0,0 @@
1
- import{registerHashlineReadTool as e}from"./readTool.mjs";import{connectDoomCordisHost as t}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_TOOL_OVERRIDES_SERVICE as n,requireDoomToolOverrides as r}from"@agimon-ai/doompi-extension-contracts/tool-overrides";const i=`@agimon-ai/doompi-read`;function a(t,a){t.inject([n],t=>{let n=r(t).claim({source:i,tools:[`read`]});if(n.granted){try{e(a)}catch(e){throw n.dispose(),e}return()=>n.dispose()}})}async function o(e){let n=await t(e,i),r=n.root.plugin(s,{pi:e});try{await r}catch(e){try{await r.dispose()}finally{await n.dispose()}throw e}let a;e.on(`session_shutdown`,()=>a??=(async()=>{try{await r.dispose()}finally{await n.dispose()}})())}function s(e,t){a(e,t.pi)}export{o as activateDoomPiReadExtension,o as default,a as installDoomPiReadRuntime};
2
- //# sourceMappingURL=extension.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_TOOL_OVERRIDES_SERVICE,\n requireDoomToolOverrides,\n} from '@agimon-ai/doompi-extension-contracts/tool-overrides';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerHashlineReadTool } from './readTool.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-read';\n\n/** Install the hashline read tool after claiming Pi's shared read name. */\nexport function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void {\n cordis.inject([DOOM_TOOL_OVERRIDES_SERVICE], (overridesContext) => {\n const registration = requireDoomToolOverrides(overridesContext).claim({\n source: PACKAGE_SOURCE,\n tools: ['read'],\n });\n if (!registration.granted) return;\n try {\n registerHashlineReadTool(pi);\n } catch (error) {\n registration.dispose();\n throw error;\n }\n return () => registration.dispose();\n });\n}\n\n/** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */\nexport async function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin(doomPiReadPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface DoomPiReadPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction doomPiReadPlugin(cordis: Context, config: DoomPiReadPluginConfig): void {\n installDoomPiReadRuntime(cordis, config.pi);\n}\n\nexport default activateDoomPiReadExtension;\n"],"mappings":"qRASA,MAAM,EAAiB,yBAGvB,SAAgB,EAAyB,EAAiB,EAAwB,CAChF,EAAO,OAAO,CAAC,CAA2B,EAAI,GAAqB,CACjE,IAAM,EAAe,EAAyB,CAAgB,CAAC,CAAC,MAAM,CACpE,OAAQ,EACR,MAAO,CAAC,MAAM,CAChB,CAAC,EACI,KAAa,QAClB,IAAI,CACF,EAAyB,CAAE,CAC7B,OAAS,EAAO,CAEd,MADA,EAAa,QAAQ,EACf,CACR,CACA,UAAa,EAAa,QAAQ,CADlC,CAEF,CAAC,CACH,CAGA,eAAsB,EAA4B,EAAiC,CACjF,IAAM,EAAa,MAAM,EAAsB,EAAI,CAAc,EAC3D,EAAQ,EAAW,KAAK,OAAO,EAAkB,CAAE,IAAG,CAAC,EAC7D,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CAEA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF,CAMA,SAAS,EAAiB,EAAiB,EAAsC,CAC/E,EAAyB,EAAQ,EAAO,EAAE,CAC5C"}
@@ -1,2 +0,0 @@
1
- let e=require("@earendil-works/pi-coding-agent"),t=require("@agimon-ai/doompi-config/config/piConfig");function n(e){return e.type===`image`}function r(){return(0,t.loadPiImageSettings)()}async function i(t,r){if(!r.autoResize||!t.some(n))return t;let i=[];for(let a of t){if(!n(a)){i.push(a);continue}let t=await(0,e.resizeImage)(Buffer.from(a.data,`base64`),a.mimeType,{maxWidth:r.maxDimension,maxHeight:r.maxDimension});if(!t){i.push({type:`text`,text:`[Image omitted: could not be resized below the inline image size limit.]`});continue}i.push({type:`image`,data:t.data,mimeType:t.mimeType});let o=(0,e.formatDimensionNote)(t);o&&i.push({type:`text`,text:o})}return i}exports.applyImageLimits=i,exports.imageLimits=r;
2
- //# sourceMappingURL=readImage.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readImage.cjs","names":["loadPiImageSettings","resizeImage","formatDimensionNote"],"sources":["../../../src/adapters/pi/readImage.ts"],"sourcesContent":["/**\n * The image half of the read override.\n *\n * DESIGN PATTERNS:\n * - Pi's read tool already decodes, converts and resizes images, but its only\n * knob is on/off: the 2000px cap is a constant inside its resize core. So the\n * native tool is asked not to resize, and the block it returns is resized\n * here against the machine's configured cap, with Pi's own exported resize so\n * the two surfaces cannot drift apart in filter, format ladder or byte limit.\n * - The limits are read per image read rather than captured at registration,\n * because the cockpit settings page writes them while a session is running.\n *\n * CODING STANDARDS:\n * - Keep Pi's wording for the omission and the coordinate note. A model that\n * learned Pi's phrasing should not have to learn a second one.\n *\n * AVOID:\n * - Dropping an image silently. An image that cannot be brought under the limit\n * is reported in the text, exactly as Pi's own read reports it.\n */\n\nimport { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/doompi-config/config/piConfig';\nimport { formatDimensionNote, resizeImage } from '@earendil-works/pi-coding-agent';\n\n/** Pi's own message for an image no resize pass could bring under the limit. */\nconst OMITTED_NOTE = '[Image omitted: could not be resized below the inline image size limit.]';\n\n/** Structurally Pi's own TextContent and ImageContent, without the pi-ai dependency. */\nexport interface ReadTextPart {\n type: 'text';\n text: string;\n}\n\nexport interface ReadImagePart {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type ReadContentPart = ReadTextPart | ReadImagePart;\n\nfunction isImagePart(part: ReadContentPart): part is ReadImagePart {\n return part.type === 'image';\n}\n\nexport function imageLimits(): PiImageSettings {\n return loadPiImageSettings();\n}\n\n/**\n * Applies the configured cap to every image block, leaving text untouched.\n *\n * Returns the same array when nothing changed, so a caller can hand the native\n * result straight back.\n */\nexport async function applyImageLimits(\n content: ReadContentPart[],\n limits: PiImageSettings,\n): Promise<ReadContentPart[]> {\n if (!limits.autoResize || !content.some(isImagePart)) return content;\n const resolved: ReadContentPart[] = [];\n for (const part of content) {\n if (!isImagePart(part)) {\n resolved.push(part);\n continue;\n }\n const resized = await resizeImage(Buffer.from(part.data, 'base64'), part.mimeType, {\n maxWidth: limits.maxDimension,\n maxHeight: limits.maxDimension,\n });\n if (!resized) {\n resolved.push({ type: 'text', text: OMITTED_NOTE });\n continue;\n }\n resolved.push({ type: 'image', data: resized.data, mimeType: resized.mimeType });\n const note = formatDimensionNote(resized);\n if (note) resolved.push({ type: 'text', text: note });\n }\n return resolved;\n}\n"],"mappings":"uGAyCA,SAAS,EAAY,EAA8C,CACjE,OAAO,EAAK,OAAS,OACvB,CAEA,SAAgB,GAA+B,CAC7C,OAAA,EAAOA,EAAAA,oBAAAA,CAAoB,CAC7B,CAQA,eAAsB,EACpB,EACA,EAC4B,CAC5B,GAAI,CAAC,EAAO,YAAc,CAAC,EAAQ,KAAK,CAAW,EAAG,OAAO,EAC7D,IAAM,EAA8B,CAAC,EACrC,IAAK,IAAM,KAAQ,EAAS,CAC1B,GAAI,CAAC,EAAY,CAAI,EAAG,CACtB,EAAS,KAAK,CAAI,EAClB,QACF,CACA,IAAM,EAAU,MAAA,EAAMC,EAAAA,YAAAA,CAAY,OAAO,KAAK,EAAK,KAAM,QAAQ,EAAG,EAAK,SAAU,CACjF,SAAU,EAAO,aACjB,UAAW,EAAO,YACpB,CAAC,EACD,GAAI,CAAC,EAAS,CACZ,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,0EAAa,CAAC,EAClD,QACF,CACA,EAAS,KAAK,CAAE,KAAM,QAAS,KAAM,EAAQ,KAAM,SAAU,EAAQ,QAAS,CAAC,EAC/E,IAAM,GAAA,EAAOC,EAAAA,oBAAAA,CAAoB,CAAO,EACpC,GAAM,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,CAAK,CAAC,CACtD,CACA,OAAO,CACT"}
@@ -1,2 +0,0 @@
1
- import{formatDimensionNote as e,resizeImage as t}from"@earendil-works/pi-coding-agent";import{loadPiImageSettings as n}from"@agimon-ai/doompi-config/config/piConfig";function r(e){return e.type===`image`}function i(){return n()}async function a(n,i){if(!i.autoResize||!n.some(r))return n;let a=[];for(let o of n){if(!r(o)){a.push(o);continue}let n=await t(Buffer.from(o.data,`base64`),o.mimeType,{maxWidth:i.maxDimension,maxHeight:i.maxDimension});if(!n){a.push({type:`text`,text:`[Image omitted: could not be resized below the inline image size limit.]`});continue}a.push({type:`image`,data:n.data,mimeType:n.mimeType});let s=e(n);s&&a.push({type:`text`,text:s})}return a}export{a as applyImageLimits,i as imageLimits};
2
- //# sourceMappingURL=readImage.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readImage.mjs","names":[],"sources":["../../../src/adapters/pi/readImage.ts"],"sourcesContent":["/**\n * The image half of the read override.\n *\n * DESIGN PATTERNS:\n * - Pi's read tool already decodes, converts and resizes images, but its only\n * knob is on/off: the 2000px cap is a constant inside its resize core. So the\n * native tool is asked not to resize, and the block it returns is resized\n * here against the machine's configured cap, with Pi's own exported resize so\n * the two surfaces cannot drift apart in filter, format ladder or byte limit.\n * - The limits are read per image read rather than captured at registration,\n * because the cockpit settings page writes them while a session is running.\n *\n * CODING STANDARDS:\n * - Keep Pi's wording for the omission and the coordinate note. A model that\n * learned Pi's phrasing should not have to learn a second one.\n *\n * AVOID:\n * - Dropping an image silently. An image that cannot be brought under the limit\n * is reported in the text, exactly as Pi's own read reports it.\n */\n\nimport { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/doompi-config/config/piConfig';\nimport { formatDimensionNote, resizeImage } from '@earendil-works/pi-coding-agent';\n\n/** Pi's own message for an image no resize pass could bring under the limit. */\nconst OMITTED_NOTE = '[Image omitted: could not be resized below the inline image size limit.]';\n\n/** Structurally Pi's own TextContent and ImageContent, without the pi-ai dependency. */\nexport interface ReadTextPart {\n type: 'text';\n text: string;\n}\n\nexport interface ReadImagePart {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type ReadContentPart = ReadTextPart | ReadImagePart;\n\nfunction isImagePart(part: ReadContentPart): part is ReadImagePart {\n return part.type === 'image';\n}\n\nexport function imageLimits(): PiImageSettings {\n return loadPiImageSettings();\n}\n\n/**\n * Applies the configured cap to every image block, leaving text untouched.\n *\n * Returns the same array when nothing changed, so a caller can hand the native\n * result straight back.\n */\nexport async function applyImageLimits(\n content: ReadContentPart[],\n limits: PiImageSettings,\n): Promise<ReadContentPart[]> {\n if (!limits.autoResize || !content.some(isImagePart)) return content;\n const resolved: ReadContentPart[] = [];\n for (const part of content) {\n if (!isImagePart(part)) {\n resolved.push(part);\n continue;\n }\n const resized = await resizeImage(Buffer.from(part.data, 'base64'), part.mimeType, {\n maxWidth: limits.maxDimension,\n maxHeight: limits.maxDimension,\n });\n if (!resized) {\n resolved.push({ type: 'text', text: OMITTED_NOTE });\n continue;\n }\n resolved.push({ type: 'image', data: resized.data, mimeType: resized.mimeType });\n const note = formatDimensionNote(resized);\n if (note) resolved.push({ type: 'text', text: note });\n }\n return resolved;\n}\n"],"mappings":"sKAyCA,SAAS,EAAY,EAA8C,CACjE,OAAO,EAAK,OAAS,OACvB,CAEA,SAAgB,GAA+B,CAC7C,OAAO,EAAoB,CAC7B,CAQA,eAAsB,EACpB,EACA,EAC4B,CAC5B,GAAI,CAAC,EAAO,YAAc,CAAC,EAAQ,KAAK,CAAW,EAAG,OAAO,EAC7D,IAAM,EAA8B,CAAC,EACrC,IAAK,IAAM,KAAQ,EAAS,CAC1B,GAAI,CAAC,EAAY,CAAI,EAAG,CACtB,EAAS,KAAK,CAAI,EAClB,QACF,CACA,IAAM,EAAU,MAAM,EAAY,OAAO,KAAK,EAAK,KAAM,QAAQ,EAAG,EAAK,SAAU,CACjF,SAAU,EAAO,aACjB,UAAW,EAAO,YACpB,CAAC,EACD,GAAI,CAAC,EAAS,CACZ,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,0EAAa,CAAC,EAClD,QACF,CACA,EAAS,KAAK,CAAE,KAAM,QAAS,KAAM,EAAQ,KAAM,SAAU,EAAQ,QAAS,CAAC,EAC/E,IAAM,EAAO,EAAoB,CAAO,EACpC,GAAM,EAAS,KAAK,CAAE,KAAM,OAAQ,KAAM,CAAK,CAAC,CACtD,CACA,OAAO,CACT"}
@@ -1,3 +0,0 @@
1
- const e=require("../../schemas/readTool.cjs"),t=require("./readImage.cjs");let n=require("@agimon-ai/doompi-hashline"),r=require("@agimon-ai/doompi-hashline/files"),i=require("@agimon-ai/doompi-ui/hashlineRendering"),a=require("node:fs/promises"),o=require("@earendil-works/pi-coding-agent");function s(n,s=r.isWritableFile){n.registerTool({name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${(0,o.formatSize)(o.DEFAULT_MAX_BYTES)}.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,promptGuidelines:[`Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.`,`Continue large reads with offset until the required anchored lines are visible.`],parameters:e.ReadParamsSchema,executionMode:`parallel`,renderShell:`self`,async execute(e,n,i,d,f){let p=n;u(i);let m=await(0,r.resolveReadInputPath)(p.path,f.cwd),h=await(0,o.createReadToolDefinition)(f.cwd,{autoResizeImages:!1}).execute(e,{...p,path:m},i,d,f);if(l(h.content))return u(i),{...h,content:await t.applyImageLimits(h.content,t.imageLimits())};u(i);let g=await s(m);if(u(i),!g)return h;let _=await(0,a.readFile)(m);return u(i),c(_,(0,r.displayPath)(m,f.cwd),p)},renderCall(e,t){let n=e,r=[n.offset===void 0?void 0:`from ${n.offset}`,n.limit===void 0?void 0:`${n.limit} lines`].filter(e=>e!==void 0);return(0,i.renderHashlineCall)(`read`,n.path,r,t)},renderResult(e,t,n,r){return(0,i.renderHashlineResult)(e,t,n,r,`read`)}})}function c(e,t,i){let a=(0,n.splitLines)((0,r.decodeUtf8)(e,t)),s=i.offset===void 0?0:i.offset-1;if(s>=a.length)throw Error(`Offset ${i.offset} is beyond end of file (${a.length} lines total).`);let c=i.limit===void 0?a.length:Math.min(a.length,s+i.limit),l=a.slice(s,c),u=(0,n.formatFileHeader)(t,(0,r.computeFileTag)(e)),d=[],f=Buffer.byteLength(u,`utf8`)+1,p=l.map((e,t)=>{let r=s+t+1,i=(0,n.formatTaggedLine)(e,r);return Buffer.byteLength(i,`utf8`)<=o.DEFAULT_MAX_BYTES-f?i:(d.push(r),(0,n.formatTaggedLine)((0,o.truncateLine)(e).text,r,``,e))}),m=(0,o.truncateHead)([u,...p].join(`
2
- `)),h=m.content,g;if(m.truncated){let e=Math.max(0,m.outputLines-1),t=s+e+1,n=m.truncatedBy===`bytes`?`, ${(0,o.formatSize)(o.DEFAULT_MAX_BYTES)} limit`:``;h+=`\n\n[Showing ${e} anchored lines${n}. Use offset=${t} to continue.]`,g={truncation:m}}else{let e=[];d.length>0&&e.push(`Lines ${d.join(`, `)} shown compactly. Their anchors hash the full original lines`),c<a.length&&e.push(`${a.length-c} more lines in file. Use offset=${c+1} to continue`),e.length>0&&(h+=`\n\n[${e.join(`. `)}.]`)}return{content:[{type:`text`,text:h}],details:g}}function l(e){return e.some(e=>e.type===`image`||e.text?.startsWith(`Read image file`)===!0)}function u(e){if(e?.aborted)throw Error(`Operation aborted`)}exports.assertNotAborted=u,exports.createTaggedReadResult=c,exports.isImageRead=l,exports.registerHashlineReadTool=s;
3
- //# sourceMappingURL=readTool.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readTool.cjs","names":["isWritableFile","formatSize","DEFAULT_MAX_BYTES","ReadParamsSchema","resolveReadInputPath","createReadToolDefinition","applyImageLimits","imageLimits","readFile","displayPath","renderHashlineCall","renderHashlineResult","splitLines","decodeUtf8","formatFileHeader","computeFileTag","formatTaggedLine","truncateLine","truncateHead"],"sources":["../../../src/adapters/pi/readTool.ts"],"sourcesContent":["import { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/doompi-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/doompi-hashline/files';\nimport { renderHashlineCall, renderHashlineResult } from '@agimon-ai/doompi-ui/hashlineRendering';\nimport { readFile } from 'node:fs/promises';\nimport {\n createReadToolDefinition,\n DEFAULT_MAX_BYTES,\n formatSize,\n truncateHead,\n truncateLine,\n type ExtensionAPI,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool.ts';\nimport { applyImageLimits, imageLimits } from './readImage.ts';\n\ntype WritableCheck = (path: string) => Promise<boolean>;\n\n/** Register the snapshot-bound replacement for Pi's read tool. */\nexport function registerHashlineReadTool(\n pi: Pick<ExtensionAPI, 'registerTool'>,\n writable: WritableCheck = isWritableFile,\n): void {\n pi.registerTool({\n name: 'read',\n label: 'read',\n description: `Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${formatSize(DEFAULT_MAX_BYTES)}.`,\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n renderShell: 'self',\n async execute(toolCallId, params, signal, onUpdate, ctx) {\n const input = params as ReadParams;\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(input.path, ctx.cwd);\n // Pi resizes at a cap it hardcodes, so it is told not to: the block comes\n // back converted but full size, and the machine's configured cap is\n // applied below. Everything else about the native read is kept.\n const nativeRead = createReadToolDefinition(ctx.cwd, { autoResizeImages: false });\n const nativeResult = await nativeRead.execute(\n toolCallId,\n { ...input, path: absolutePath },\n signal,\n onUpdate,\n ctx,\n );\n if (isImageRead(nativeResult.content)) {\n assertNotAborted(signal);\n return { ...nativeResult, content: await applyImageLimits(nativeResult.content, imageLimits()) };\n }\n\n assertNotAborted(signal);\n const canEdit = await writable(absolutePath);\n assertNotAborted(signal);\n if (!canEdit) return nativeResult;\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n return createTaggedReadResult(bytes, displayPath(absolutePath, ctx.cwd), input);\n },\n renderCall(args, theme) {\n const input = args as ReadParams;\n const details = [\n input.offset === undefined ? undefined : `from ${input.offset}`,\n input.limit === undefined ? undefined : `${input.limit} lines`,\n ].filter((value): value is string => value !== undefined);\n return renderHashlineCall('read', input.path, details, theme);\n },\n renderResult(result, options, theme, context) {\n return renderHashlineResult(result, options, theme, context, 'read');\n },\n });\n}\n\nexport function createTaggedReadResult(\n bytes: Buffer,\n path: string,\n params: ReadParams,\n): { content: [{ type: 'text'; text: string }]; details: ReadToolDetails | undefined } {\n const lines = splitLines(decodeUtf8(bytes, path));\n const startIndex = params.offset === undefined ? 0 : params.offset - 1;\n if (startIndex >= lines.length) {\n throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);\n }\n\n const endIndex = params.limit === undefined ? lines.length : Math.min(lines.length, startIndex + params.limit);\n const selected = lines.slice(startIndex, endIndex);\n const header = formatFileHeader(path, computeFileTag(bytes));\n const compactedLines: number[] = [];\n const headerBytes = Buffer.byteLength(header, 'utf8') + 1;\n const tagged = selected.map((line, index) => {\n const lineNumber = startIndex + index + 1;\n const full = formatTaggedLine(line, lineNumber);\n if (Buffer.byteLength(full, 'utf8') <= DEFAULT_MAX_BYTES - headerBytes) return full;\n compactedLines.push(lineNumber);\n return formatTaggedLine(truncateLine(line).text, lineNumber, '', line);\n });\n const truncation = truncateHead([header, ...tagged].join('\\n'));\n let text = truncation.content;\n let details: ReadToolDetails | undefined;\n\n if (truncation.truncated) {\n const shownLines = Math.max(0, truncation.outputLines - 1);\n const nextOffset = startIndex + shownLines + 1;\n const reason = truncation.truncatedBy === 'bytes' ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : '';\n text += `\\n\\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;\n details = { truncation };\n } else {\n const notices: string[] = [];\n if (compactedLines.length > 0) {\n notices.push(`Lines ${compactedLines.join(', ')} shown compactly. Their anchors hash the full original lines`);\n }\n if (endIndex < lines.length) {\n notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);\n }\n if (notices.length > 0) text += `\\n\\n[${notices.join('. ')}.]`;\n }\n\n return { content: [{ type: 'text', text }], details };\n}\n\nexport function isImageRead(content: readonly { readonly type: string; readonly text?: string }[]): boolean {\n return content.some((part) => part.type === 'image' || part.text?.startsWith('Read image file') === true);\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n"],"mappings":"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"}
@@ -1,8 +0,0 @@
1
- import "../../schemas/readTool.cjs";
2
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
- //#region src/adapters/pi/readTool.d.ts
4
- type WritableCheck = (path: string) => Promise<boolean>;
5
- /** Register the snapshot-bound replacement for Pi's read tool. */
6
- export declare function registerHashlineReadTool(pi: Pick<ExtensionAPI, 'registerTool'>, writable?: WritableCheck): void;
7
- //#endregion
8
- //# sourceMappingURL=readTool.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readTool.d.cts","names":[],"sources":["../../../src/adapters/pi/readTool.ts"],"mappings":";;;KAsBK,iBAAiB,iBAAiB;;wBAGvB,yBACd,IAAI,KAAK,+BACT,WAAU"}
@@ -1,8 +0,0 @@
1
- import "../../schemas/readTool.mjs";
2
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
- //#region src/adapters/pi/readTool.d.ts
4
- type WritableCheck = (path: string) => Promise<boolean>;
5
- /** Register the snapshot-bound replacement for Pi's read tool. */
6
- export declare function registerHashlineReadTool(pi: Pick<ExtensionAPI, 'registerTool'>, writable?: WritableCheck): void;
7
- //#endregion
8
- //# sourceMappingURL=readTool.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readTool.d.mts","names":[],"sources":["../../../src/adapters/pi/readTool.ts"],"mappings":";;;KAsBK,iBAAiB,iBAAiB;;wBAGvB,yBACd,IAAI,KAAK,+BACT,WAAU"}
@@ -1,3 +0,0 @@
1
- import{ReadParamsSchema as e}from"../../schemas/readTool.mjs";import{applyImageLimits as t,imageLimits as n}from"./readImage.mjs";import{formatFileHeader as r,formatTaggedLine as i,splitLines as a}from"@agimon-ai/doompi-hashline";import{computeFileTag as o,decodeUtf8 as s,displayPath as c,isWritableFile as l,resolveReadInputPath as u}from"@agimon-ai/doompi-hashline/files";import{renderHashlineCall as d,renderHashlineResult as f}from"@agimon-ai/doompi-ui/hashlineRendering";import{readFile as p}from"node:fs/promises";import{DEFAULT_MAX_BYTES as m,createReadToolDefinition as h,formatSize as g,truncateHead as _,truncateLine as v}from"@earendil-works/pi-coding-agent";function y(r,i=l){r.registerTool({name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${g(m)}.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,promptGuidelines:[`Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.`,`Continue large reads with offset until the required anchored lines are visible.`],parameters:e,executionMode:`parallel`,renderShell:`self`,async execute(e,r,a,o,s){let l=r;S(a);let d=await u(l.path,s.cwd),f=await h(s.cwd,{autoResizeImages:!1}).execute(e,{...l,path:d},a,o,s);if(x(f.content))return S(a),{...f,content:await t(f.content,n())};S(a);let m=await i(d);if(S(a),!m)return f;let g=await p(d);return S(a),b(g,c(d,s.cwd),l)},renderCall(e,t){let n=e,r=[n.offset===void 0?void 0:`from ${n.offset}`,n.limit===void 0?void 0:`${n.limit} lines`].filter(e=>e!==void 0);return d(`read`,n.path,r,t)},renderResult(e,t,n,r){return f(e,t,n,r,`read`)}})}function b(e,t,n){let c=a(s(e,t)),l=n.offset===void 0?0:n.offset-1;if(l>=c.length)throw Error(`Offset ${n.offset} is beyond end of file (${c.length} lines total).`);let u=n.limit===void 0?c.length:Math.min(c.length,l+n.limit),d=c.slice(l,u),f=r(t,o(e)),p=[],h=Buffer.byteLength(f,`utf8`)+1,y=d.map((e,t)=>{let n=l+t+1,r=i(e,n);return Buffer.byteLength(r,`utf8`)<=m-h?r:(p.push(n),i(v(e).text,n,``,e))}),b=_([f,...y].join(`
2
- `)),x=b.content,S;if(b.truncated){let e=Math.max(0,b.outputLines-1),t=l+e+1,n=b.truncatedBy===`bytes`?`, ${g(m)} limit`:``;x+=`\n\n[Showing ${e} anchored lines${n}. Use offset=${t} to continue.]`,S={truncation:b}}else{let e=[];p.length>0&&e.push(`Lines ${p.join(`, `)} shown compactly. Their anchors hash the full original lines`),u<c.length&&e.push(`${c.length-u} more lines in file. Use offset=${u+1} to continue`),e.length>0&&(x+=`\n\n[${e.join(`. `)}.]`)}return{content:[{type:`text`,text:x}],details:S}}function x(e){return e.some(e=>e.type===`image`||e.text?.startsWith(`Read image file`)===!0)}function S(e){if(e?.aborted)throw Error(`Operation aborted`)}export{S as assertNotAborted,b as createTaggedReadResult,x as isImageRead,y as registerHashlineReadTool};
3
- //# sourceMappingURL=readTool.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readTool.mjs","names":[],"sources":["../../../src/adapters/pi/readTool.ts"],"sourcesContent":["import { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/doompi-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/doompi-hashline/files';\nimport { renderHashlineCall, renderHashlineResult } from '@agimon-ai/doompi-ui/hashlineRendering';\nimport { readFile } from 'node:fs/promises';\nimport {\n createReadToolDefinition,\n DEFAULT_MAX_BYTES,\n formatSize,\n truncateHead,\n truncateLine,\n type ExtensionAPI,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool.ts';\nimport { applyImageLimits, imageLimits } from './readImage.ts';\n\ntype WritableCheck = (path: string) => Promise<boolean>;\n\n/** Register the snapshot-bound replacement for Pi's read tool. */\nexport function registerHashlineReadTool(\n pi: Pick<ExtensionAPI, 'registerTool'>,\n writable: WritableCheck = isWritableFile,\n): void {\n pi.registerTool({\n name: 'read',\n label: 'read',\n description: `Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${formatSize(DEFAULT_MAX_BYTES)}.`,\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n renderShell: 'self',\n async execute(toolCallId, params, signal, onUpdate, ctx) {\n const input = params as ReadParams;\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(input.path, ctx.cwd);\n // Pi resizes at a cap it hardcodes, so it is told not to: the block comes\n // back converted but full size, and the machine's configured cap is\n // applied below. Everything else about the native read is kept.\n const nativeRead = createReadToolDefinition(ctx.cwd, { autoResizeImages: false });\n const nativeResult = await nativeRead.execute(\n toolCallId,\n { ...input, path: absolutePath },\n signal,\n onUpdate,\n ctx,\n );\n if (isImageRead(nativeResult.content)) {\n assertNotAborted(signal);\n return { ...nativeResult, content: await applyImageLimits(nativeResult.content, imageLimits()) };\n }\n\n assertNotAborted(signal);\n const canEdit = await writable(absolutePath);\n assertNotAborted(signal);\n if (!canEdit) return nativeResult;\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n return createTaggedReadResult(bytes, displayPath(absolutePath, ctx.cwd), input);\n },\n renderCall(args, theme) {\n const input = args as ReadParams;\n const details = [\n input.offset === undefined ? undefined : `from ${input.offset}`,\n input.limit === undefined ? undefined : `${input.limit} lines`,\n ].filter((value): value is string => value !== undefined);\n return renderHashlineCall('read', input.path, details, theme);\n },\n renderResult(result, options, theme, context) {\n return renderHashlineResult(result, options, theme, context, 'read');\n },\n });\n}\n\nexport function createTaggedReadResult(\n bytes: Buffer,\n path: string,\n params: ReadParams,\n): { content: [{ type: 'text'; text: string }]; details: ReadToolDetails | undefined } {\n const lines = splitLines(decodeUtf8(bytes, path));\n const startIndex = params.offset === undefined ? 0 : params.offset - 1;\n if (startIndex >= lines.length) {\n throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);\n }\n\n const endIndex = params.limit === undefined ? lines.length : Math.min(lines.length, startIndex + params.limit);\n const selected = lines.slice(startIndex, endIndex);\n const header = formatFileHeader(path, computeFileTag(bytes));\n const compactedLines: number[] = [];\n const headerBytes = Buffer.byteLength(header, 'utf8') + 1;\n const tagged = selected.map((line, index) => {\n const lineNumber = startIndex + index + 1;\n const full = formatTaggedLine(line, lineNumber);\n if (Buffer.byteLength(full, 'utf8') <= DEFAULT_MAX_BYTES - headerBytes) return full;\n compactedLines.push(lineNumber);\n return formatTaggedLine(truncateLine(line).text, lineNumber, '', line);\n });\n const truncation = truncateHead([header, ...tagged].join('\\n'));\n let text = truncation.content;\n let details: ReadToolDetails | undefined;\n\n if (truncation.truncated) {\n const shownLines = Math.max(0, truncation.outputLines - 1);\n const nextOffset = startIndex + shownLines + 1;\n const reason = truncation.truncatedBy === 'bytes' ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : '';\n text += `\\n\\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;\n details = { truncation };\n } else {\n const notices: string[] = [];\n if (compactedLines.length > 0) {\n notices.push(`Lines ${compactedLines.join(', ')} shown compactly. Their anchors hash the full original lines`);\n }\n if (endIndex < lines.length) {\n notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);\n }\n if (notices.length > 0) text += `\\n\\n[${notices.join('. ')}.]`;\n }\n\n return { content: [{ type: 'text', text }], details };\n}\n\nexport function isImageRead(content: readonly { readonly type: string; readonly text?: string }[]): boolean {\n return content.some((part) => part.type === 'image' || part.text?.startsWith('Read image file') === true);\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- export { webPlugin } from '../web/index.ts';
package/src/web/index.ts DELETED
@@ -1,11 +0,0 @@
1
- import { defineWebPlugin } from '@agimon-ai/doompi-web-contracts';
2
- import { ReadToolMessage } from './components/ReadToolMessage.tsx';
3
-
4
- /**
5
- * This package's cockpit presence: the read tool's timeline card, the web
6
- * half of the TUI's renderCall and renderResult for hashline reads.
7
- */
8
- export const webPlugin = defineWebPlugin({
9
- id: 'read',
10
- toolRenderers: [{ tools: ['read'], message: ReadToolMessage }],
11
- });