@agimon-ai/po-read 0.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +61 -0
- package/dist/apiContracts.cjs +17 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +13 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/extensions/mcp-ui.mjs +22 -0
- package/dist/extensions/mcp-ui.mjs.map +1 -0
- package/dist/extensions/mcp.cjs +184 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +179 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/server.cjs +26 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +22 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +134 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index.cjs +5 -0
- package/dist/index.d.mts +34 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/readTool-DjM6DFYl.cjs +169 -0
- package/dist/readTool-DjM6DFYl.cjs.map +1 -0
- package/dist/readTool-epk_n4AS.mjs +152 -0
- package/dist/readTool-epk_n4AS.mjs.map +1 -0
- package/package.json +134 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vuong Ngo
|
|
4
|
+
Copyright (c) 2026 pulseaiclub
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @agimon-ai/po-read
|
|
2
|
+
|
|
3
|
+
Snapshot-bound hashline `read` tool for Po.
|
|
4
|
+
|
|
5
|
+
Part of the [Po distribution](https://www.npmjs.com/package/@agimon-ai/po).
|
|
6
|
+
|
|
7
|
+
Writable text reads attach an exact file tag and
|
|
8
|
+
a stable three-letter anchor to every returned line. Non-writable files retain Pi's native output,
|
|
9
|
+
which avoids spending model context on anchors that cannot be edited. The full hashline protocol
|
|
10
|
+
remains available to the model, while Po's renderer hides the protocol tags and anchors from
|
|
11
|
+
people and preserves syntax highlighting. Image reads retain Pi's native attachment behavior.
|
|
12
|
+
|
|
13
|
+
> **Alpha:** tool and protocol contracts may change between releases.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- Node.js 24 or newer
|
|
18
|
+
|
|
19
|
+
## Tool protocol
|
|
20
|
+
|
|
21
|
+
A writable text result starts with one file header, followed by tagged lines:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
@file src/example.ts#A1b2C3d4
|
|
25
|
+
1#abc|const value = 1;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The eight-character file tag is the base64url SHA-256 prefix of the exact file bytes. The
|
|
29
|
+
three-letter line anchor is a whitespace-insensitive FNV-1a64 tag. Large reads retain Pi's output
|
|
30
|
+
limits and include an offset hint when more lines are available.
|
|
31
|
+
|
|
32
|
+
## Attribution
|
|
33
|
+
|
|
34
|
+
The hashline protocol and FNV anchor approach are adapted from
|
|
35
|
+
[Phi's writetool](https://github.com/pulseaiclub/phi/tree/main/internal/tools/writetool), licensed
|
|
36
|
+
under MIT by pulseaiclub. Po uses an exact-byte SHA-256 file tag as an intentional compatibility
|
|
37
|
+
boundary.
|
|
38
|
+
|
|
39
|
+
## Public API
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { ReadParamsSchema, createHeadlessReadTool } from '@agimon-ai/po-read';
|
|
43
|
+
import type { ReadParams } from '@agimon-ai/po-read';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pnpm build
|
|
50
|
+
pnpm typecheck
|
|
51
|
+
pnpm test
|
|
52
|
+
pnpm lint
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Maintained by [Agimon](https://agimon.ai/about).
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
MIT
|
|
60
|
+
|
|
61
|
+
The server entry declares the native tool. Tool declarations consume logic from named service folders; flat `src/exports` files expose reusable factories and schemas.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
//#region src/schemas/apiContracts.ts
|
|
6
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
7
|
+
const apiContracts = (0, require("@agimon-ai/po-core/apiContracts").defineApiContract)({
|
|
8
|
+
version: 1,
|
|
9
|
+
http: [],
|
|
10
|
+
sockets: [],
|
|
11
|
+
dynamic: []
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
exports.apiContracts = apiContracts;
|
|
15
|
+
exports.default = apiContracts;
|
|
16
|
+
|
|
17
|
+
//# 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/po-core/apiContracts';\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,gBAAA,4CAAeA,CAAAA,CAAAA,kBAAAA,CAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,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.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineApiContract } from "@agimon-ai/po-core/apiContracts";
|
|
2
|
+
//#region src/schemas/apiContracts.ts
|
|
3
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
4
|
+
const apiContracts = defineApiContract({
|
|
5
|
+
version: 1,
|
|
6
|
+
http: [],
|
|
7
|
+
sockets: [],
|
|
8
|
+
dynamic: []
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { apiContracts, apiContracts as default };
|
|
12
|
+
|
|
13
|
+
//# 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/po-core/apiContracts';\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,eAAe,kBAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineMcpWidget } from "@agimon-ai/po-core/web";
|
|
2
|
+
import { McpToolFields, McpToolFrame, McpToolOutput } from "@agimon-ai/po-web-components";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/read.mcp.tsx
|
|
5
|
+
function ReadMcpWidget(props) {
|
|
6
|
+
return /* @__PURE__ */ jsxs(McpToolFrame, {
|
|
7
|
+
...props,
|
|
8
|
+
title: "Read file",
|
|
9
|
+
children: [/* @__PURE__ */ jsx(McpToolFields, { fields: [
|
|
10
|
+
["Path", props.args.path],
|
|
11
|
+
["Offset", props.args.offset],
|
|
12
|
+
["Limit", props.args.limit]
|
|
13
|
+
] }), /* @__PURE__ */ jsx(McpToolOutput, { result: props.result })]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region generated/mcp-ui.ts
|
|
18
|
+
const widgets = { "@agimon-ai/po-read/read": defineMcpWidget(ReadMcpWidget) };
|
|
19
|
+
//#endregion
|
|
20
|
+
export { widgets };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=mcp-ui.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-ui.mjs","names":["Widget0"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/tool/read.mcp.tsx","../../generated/mcp-ui.ts"],"sourcesContent":["import { defineMcpWidget, type DoomMcpWidgetProps } from '@agimon-ai/po-core/web';\nimport { McpToolFrame, McpToolFields, McpToolOutput } from '@agimon-ai/po-web-components';\n\nfunction ReadMcpWidget(props: DoomMcpWidgetProps) {\n return (\n <McpToolFrame {...props} title=\"Read file\">\n <McpToolFields\n fields={[\n ['Path', props.args.path],\n ['Offset', props.args.offset],\n ['Limit', props.args.limit],\n ]}\n />\n <McpToolOutput result={props.result} />\n </McpToolFrame>\n );\n}\n\nexport default defineMcpWidget(ReadMcpWidget);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport type { ComponentType } from 'react';\nimport type { DoomMcpWidgetProps } from '@agimon-ai/po-core/mcpWidget';\nimport Widget0 from \"../src/extensions/workspaces/sessions/(frontend)/tool/read.mcp\";\n\nexport const widgets: Readonly<Record<string, ComponentType<DoomMcpWidgetProps>>> = {\n \"@agimon-ai/po-read/read\": Widget0,\n};\n"],"mappings":";;;;AAGA,SAAS,cAAc,OAA2B;CAChD,OACE,qBAAC,cAAD;EAAc,GAAI;EAAO,OAAM;EAA/B,UAAA,CACE,oBAAC,eAAD,EACE,QAAQ;GACN,CAAC,QAAQ,MAAM,KAAK,IAAI;GACxB,CAAC,UAAU,MAAM,KAAK,MAAM;GAC5B,CAAC,SAAS,MAAM,KAAK,KAAK;EAC5B,EACD,CAAA,GACD,oBAAC,eAAD,EAAe,QAAQ,MAAM,OAAS,CAAA,CAC1B;;AAElB;;;ACXA,MAAa,UAAuE,EAClF,2BDYa,gBAAgB,aCZFA,EAC7B"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
let _agimon_ai_po_core_mcpFacet = require("@agimon-ai/po-core/mcpFacet");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
7
|
+
let _agimon_ai_po_hashline = require("@agimon-ai/po-hashline");
|
|
8
|
+
let _agimon_ai_po_hashline_files = require("@agimon-ai/po-hashline/files");
|
|
9
|
+
let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent");
|
|
10
|
+
let typebox = require("typebox");
|
|
11
|
+
let _agimon_ai_po_config_piConfig = require("@agimon-ai/po-config/piConfig");
|
|
12
|
+
//#region src/schemas/readTool.ts
|
|
13
|
+
const ReadParamsSchema = typebox.Type.Object({
|
|
14
|
+
path: typebox.Type.String({ description: "Path to the file to read." }),
|
|
15
|
+
offset: typebox.Type.Optional(typebox.Type.Integer({
|
|
16
|
+
minimum: 1,
|
|
17
|
+
description: "One-based line offset."
|
|
18
|
+
})),
|
|
19
|
+
limit: typebox.Type.Optional(typebox.Type.Integer({
|
|
20
|
+
minimum: 1,
|
|
21
|
+
description: "Maximum number of lines to return."
|
|
22
|
+
}))
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/services/readImage/index.ts
|
|
26
|
+
const OMITTED_NOTE = "[Image omitted: could not be resized below the inline image size limit.]";
|
|
27
|
+
function imageLimits() {
|
|
28
|
+
return (0, _agimon_ai_po_config_piConfig.loadPiImageSettings)();
|
|
29
|
+
}
|
|
30
|
+
async function applyImageLimits(content, limits, operations) {
|
|
31
|
+
if (!limits.autoResize || !content.some((part) => part.type === "image")) return content;
|
|
32
|
+
const resolved = [];
|
|
33
|
+
for (const part of content) {
|
|
34
|
+
if (part.type !== "image") {
|
|
35
|
+
resolved.push(part);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const resized = await operations.resize(Buffer.from(part.data, "base64"), part.mimeType, {
|
|
39
|
+
maxWidth: limits.maxDimension,
|
|
40
|
+
maxHeight: limits.maxDimension
|
|
41
|
+
});
|
|
42
|
+
if (!resized) {
|
|
43
|
+
resolved.push({
|
|
44
|
+
type: "text",
|
|
45
|
+
text: OMITTED_NOTE
|
|
46
|
+
});
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
resolved.push({
|
|
50
|
+
type: "image",
|
|
51
|
+
data: resized.data,
|
|
52
|
+
mimeType: resized.mimeType
|
|
53
|
+
});
|
|
54
|
+
const note = operations.formatDimensionNote(resized);
|
|
55
|
+
if (note) resolved.push({
|
|
56
|
+
type: "text",
|
|
57
|
+
text: note
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return resolved;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/services/readTool/index.ts
|
|
64
|
+
const resizeOperations = {
|
|
65
|
+
resize: _earendil_works_pi_coding_agent.resizeImage,
|
|
66
|
+
formatDimensionNote: (result) => (0, _earendil_works_pi_coding_agent.formatDimensionNote)(result)
|
|
67
|
+
};
|
|
68
|
+
function assertNotAborted(signal) {
|
|
69
|
+
if (signal?.aborted) throw new Error("Operation aborted");
|
|
70
|
+
}
|
|
71
|
+
function isImagePath(filePath) {
|
|
72
|
+
const extension = filePath.toLowerCase().match(/\.([a-z0-9]+)$/u)?.[1];
|
|
73
|
+
if (!extension) return void 0;
|
|
74
|
+
return {
|
|
75
|
+
avif: "image/avif",
|
|
76
|
+
gif: "image/gif",
|
|
77
|
+
jpeg: "image/jpeg",
|
|
78
|
+
jpg: "image/jpeg",
|
|
79
|
+
png: "image/png",
|
|
80
|
+
webp: "image/webp"
|
|
81
|
+
}[extension];
|
|
82
|
+
}
|
|
83
|
+
async function executeHeadlessRead(params, cwd, signal) {
|
|
84
|
+
assertNotAborted(signal);
|
|
85
|
+
const absolutePath = await (0, _agimon_ai_po_hashline_files.resolveReadInputPath)(params.path, cwd);
|
|
86
|
+
const bytes = await (0, node_fs_promises.readFile)(absolutePath);
|
|
87
|
+
assertNotAborted(signal);
|
|
88
|
+
const mimeType = isImagePath(absolutePath);
|
|
89
|
+
if (mimeType) return { content: await applyImageLimits([{
|
|
90
|
+
type: "text",
|
|
91
|
+
text: `Read image file ${(0, _agimon_ai_po_hashline_files.displayPath)(absolutePath, cwd)} [${mimeType}]`
|
|
92
|
+
}, {
|
|
93
|
+
type: "image",
|
|
94
|
+
data: bytes.toString("base64"),
|
|
95
|
+
mimeType
|
|
96
|
+
}], imageLimits(), resizeOperations) };
|
|
97
|
+
if (!await (0, _agimon_ai_po_hashline_files.isWritableFile)(absolutePath)) return { content: [{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: (0, _agimon_ai_po_hashline_files.decodeUtf8)(bytes, (0, _agimon_ai_po_hashline_files.displayPath)(absolutePath, cwd))
|
|
100
|
+
}] };
|
|
101
|
+
return createTaggedReadResult(bytes, (0, _agimon_ai_po_hashline_files.displayPath)(absolutePath, cwd), params);
|
|
102
|
+
}
|
|
103
|
+
function createTaggedReadResult(bytes, path, params) {
|
|
104
|
+
const lines = (0, _agimon_ai_po_hashline.splitLines)((0, _agimon_ai_po_hashline_files.decodeUtf8)(bytes, path));
|
|
105
|
+
const startIndex = params.offset === void 0 ? 0 : params.offset - 1;
|
|
106
|
+
if (startIndex >= lines.length) throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);
|
|
107
|
+
const endIndex = params.limit === void 0 ? lines.length : Math.min(lines.length, startIndex + params.limit);
|
|
108
|
+
const selected = lines.slice(startIndex, endIndex);
|
|
109
|
+
const header = (0, _agimon_ai_po_hashline.formatFileHeader)(path, (0, _agimon_ai_po_hashline_files.computeFileTag)(bytes));
|
|
110
|
+
const compactedLines = [];
|
|
111
|
+
const headerBytes = Buffer.byteLength(header, "utf8") + 1;
|
|
112
|
+
const tagged = selected.map((line, index) => {
|
|
113
|
+
const lineNumber = startIndex + index + 1;
|
|
114
|
+
const full = (0, _agimon_ai_po_hashline.formatTaggedLine)(line, lineNumber);
|
|
115
|
+
if (Buffer.byteLength(full, "utf8") <= _earendil_works_pi_coding_agent.DEFAULT_MAX_BYTES - headerBytes) return full;
|
|
116
|
+
compactedLines.push(lineNumber);
|
|
117
|
+
return (0, _agimon_ai_po_hashline.formatTaggedLine)((0, _earendil_works_pi_coding_agent.truncateLine)(line).text, lineNumber, "", line);
|
|
118
|
+
});
|
|
119
|
+
const truncation = (0, _earendil_works_pi_coding_agent.truncateHead)([header, ...tagged].join("\n"));
|
|
120
|
+
let text = truncation.content;
|
|
121
|
+
let details;
|
|
122
|
+
if (truncation.truncated) {
|
|
123
|
+
const shownLines = Math.max(0, truncation.outputLines - 1);
|
|
124
|
+
const nextOffset = startIndex + shownLines + 1;
|
|
125
|
+
const reason = truncation.truncatedBy === "bytes" ? `, ${(0, _earendil_works_pi_coding_agent.formatSize)(_earendil_works_pi_coding_agent.DEFAULT_MAX_BYTES)} limit` : "";
|
|
126
|
+
text += `\n\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;
|
|
127
|
+
details = { truncation };
|
|
128
|
+
} else {
|
|
129
|
+
const notices = [];
|
|
130
|
+
if (compactedLines.length > 0) notices.push(`Lines ${compactedLines.join(", ")} shown compactly. Their anchors hash the full original lines`);
|
|
131
|
+
if (endIndex < lines.length) notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);
|
|
132
|
+
if (notices.length > 0) text += `\n\n[${notices.join(". ")}.]`;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
content: [{
|
|
136
|
+
type: "text",
|
|
137
|
+
text
|
|
138
|
+
}],
|
|
139
|
+
details
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function createHeadlessReadTool() {
|
|
143
|
+
return {
|
|
144
|
+
name: "read",
|
|
145
|
+
label: "read",
|
|
146
|
+
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.",
|
|
147
|
+
promptSnippet: "Read file contents with snapshot-bound line anchors",
|
|
148
|
+
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."],
|
|
149
|
+
parameters: ReadParamsSchema,
|
|
150
|
+
executionMode: "parallel",
|
|
151
|
+
execute: async (_toolCallId, params, signal, _onUpdate, context) => executeHeadlessRead(params, context.cwd, signal)
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts
|
|
156
|
+
var read_mcp_default = (0, _agimon_ai_po_core_mcpFacet.defineMcpTool)(() => ({
|
|
157
|
+
...createHeadlessReadTool(),
|
|
158
|
+
description: "Read a text file from the Po session's repository on the host machine (open, view, cat). Returns a file hash and line anchors such as 5#abc that this server's edit tool requires. Use offset and limit for large files; text is truncated at 2000 lines or 50KB."
|
|
159
|
+
}));
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region generated/mcp.ts
|
|
162
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
163
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
164
|
+
...identity,
|
|
165
|
+
...value
|
|
166
|
+
};
|
|
167
|
+
const withWidget = (key, value) => value === void 0 ? value : {
|
|
168
|
+
...value,
|
|
169
|
+
_meta: {
|
|
170
|
+
...value._meta,
|
|
171
|
+
["po/widget"]: key
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const mcp = (0, _agimon_ai_po_core_mcpFacet.defineMcpPlugin)({
|
|
175
|
+
name: "@agimon-ai/po-read",
|
|
176
|
+
session: (context) => ({ get tools() {
|
|
177
|
+
return [withWidget("@agimon-ai/po-read/read", via({ name: "read" }, at(read_mcp_default, context)))];
|
|
178
|
+
} })
|
|
179
|
+
});
|
|
180
|
+
//#endregion
|
|
181
|
+
exports.default = mcp;
|
|
182
|
+
exports.mcp = mcp;
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=mcp.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.cjs","names":["Type","loadPiImageSettings","resizeImage","formatDimensionNote","resolveReadInputPath","readFile","displayPath","isWritableFile","decodeUtf8","splitLines","formatFileHeader","computeFileTag","formatTaggedLine","DEFAULT_MAX_BYTES","truncateLine","truncateHead","formatSize","defineMcpTool","defineMcpPlugin","toolRead"],"sources":["../../src/schemas/readTool.ts","../../src/services/readImage/index.ts","../../src/services/readTool/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\nexport const ReadParamsSchema = Type.Object({\n path: Type.String({ description: 'Path to the file to read.' }),\n offset: Type.Optional(Type.Integer({ minimum: 1, description: 'One-based line offset.' })),\n limit: Type.Optional(Type.Integer({ minimum: 1, description: 'Maximum number of lines to return.' })),\n});\n\nexport type ReadParams = Static<typeof ReadParamsSchema>;\n","import { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/po-config/piConfig';\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","import { readFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/po-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/po-hashline/files';\nimport {\n DEFAULT_MAX_BYTES,\n formatDimensionNote,\n formatSize,\n resizeImage,\n truncateHead,\n truncateLine,\n type ReadToolDetails,\n} 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","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createHeadlessReadTool } from '../../../../../services/readTool';\n\nexport default defineMcpTool(() => ({\n ...createHeadlessReadTool(),\n description:\n \"Read a text file from the Po session's repository on the host machine (open, view, cat). Returns a file hash and line anchors such as 5#abc that this server's edit tool requires. Use offset and limit for large files; text is truncated at 2000 lines or 50KB.\",\n}));\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport toolRead from '../src/extensions/workspaces/sessions/(backend)/tool/read.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-read',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-read/read\", via({ name: 'read' }, at(toolRead, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;;;;;;;;;;AAEA,MAAa,mBAAmBA,QAAAA,KAAK,OAAO;CAC1C,MAAMA,QAAAA,KAAK,OAAO,EAAE,aAAa,4BAA4B,CAAC;CAC9D,QAAQA,QAAAA,KAAK,SAASA,QAAAA,KAAK,QAAQ;EAAE,SAAS;EAAG,aAAa;CAAyB,CAAC,CAAC;CACzF,OAAOA,QAAAA,KAAK,SAASA,QAAAA,KAAK,QAAQ;EAAE,SAAS;EAAG,aAAa;CAAqC,CAAC,CAAC;AACtG,CAAC;;;ACJD,MAAM,eAAe;AAkCrB,SAAgB,cAA+B;CAC7C,QAAA,GAAOC,8BAAAA,oBAAAA,CAAoB;AAC7B;AAEA,eAAsB,iBACpB,SACA,QACA,YAC4B;CAC5B,IAAI,CAAC,OAAO,cAAc,CAAC,QAAQ,MAAM,SAAS,KAAK,SAAS,OAAO,GAAG,OAAO;CACjF,MAAM,WAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,SAAS;EAC1B,IAAI,KAAK,SAAS,SAAS;GACzB,SAAS,KAAK,IAAI;GAClB;EACF;EACA,MAAM,UAAU,MAAM,WAAW,OAAO,OAAO,KAAK,KAAK,MAAM,QAAQ,GAAG,KAAK,UAAU;GACvF,UAAU,OAAO;GACjB,WAAW,OAAO;EACpB,CAAC;EACD,IAAI,CAAC,SAAS;GACZ,SAAS,KAAK;IAAE,MAAM;IAAQ,MAAM;GAAa,CAAC;GAClD;EACF;EACA,SAAS,KAAK;GAAE,MAAM;GAAS,MAAM,QAAQ;GAAM,UAAU,QAAQ;EAAS,CAAC;EAC/E,MAAM,OAAO,WAAW,oBAAoB,OAAO;EACnD,IAAI,MAAM,SAAS,KAAK;GAAE,MAAM;GAAQ,MAAM;EAAK,CAAC;CACtD;CACA,OAAO;AACT;;;AC/BA,MAAM,mBAA8C;CAClD,QAAQC,gCAAAA;CACR,sBAAsB,YAAA,GAAWC,gCAAAA,oBAAAA,CAAoB,MAAM;AAC7D;AAYA,SAAgB,iBAAiB,QAAuC;CACtE,IAAI,QAAQ,SAAS,MAAM,IAAI,MAAM,mBAAmB;AAC1D;AAEA,SAAgB,YAAY,UAAsC;CAChE,MAAM,YAAY,SAAS,YAAY,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG;CACpE,IAAI,CAAC,WAAW,OAAO,KAAA;CASvB,OAAO;EAPL,MAAM;EACN,KAAK;EACL,MAAM;EACN,KAAK;EACL,KAAK;EACL,MAAM;CAEO,EAAE;AACnB;AAEA,eAAsB,oBACpB,QACA,KACA,QACyB;CACzB,iBAAiB,MAAM;CACvB,MAAM,eAAe,OAAA,GAAMC,6BAAAA,qBAAAA,CAAqB,OAAO,MAAM,GAAG;CAChE,MAAM,QAAQ,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,YAAY;CACzC,iBAAiB,MAAM;CACvB,MAAM,WAAW,YAAY,YAAY;CACzC,IAAI,UAKF,OAAO,EAAE,SAAS,MAAM,iBAAiB,CAHvC;EAAE,MAAM;EAAQ,MAAM,oBAAA,GAAmBC,6BAAAA,YAAAA,CAAY,cAAc,GAAG,EAAE,IAAI,SAAS;CAAG,GACxF;EAAE,MAAM;EAAS,MAAM,MAAM,SAAS,QAAQ;EAAG;CAAS,CAEnB,GAAS,YAAY,GAAG,gBAAgB,EAAE;CAErF,IAAI,CAAE,OAAA,GAAMC,6BAAAA,eAAAA,CAAe,YAAY,GACrC,OAAO,EAAE,SAAS,CAAC;EAAE,MAAM;EAAQ,OAAA,GAAMC,6BAAAA,WAAAA,CAAW,QAAA,GAAOF,6BAAAA,YAAAA,CAAY,cAAc,GAAG,CAAC;CAAE,CAAC,EAAE;CAEhG,OAAO,uBAAuB,QAAA,GAAOA,6BAAAA,YAAAA,CAAY,cAAc,GAAG,GAAG,MAAM;AAC7E;AAEA,SAAgB,uBAAuB,OAAe,MAAc,QAAwC;CAC1G,MAAM,SAAA,GAAQG,uBAAAA,WAAAA,EAAAA,GAAWD,6BAAAA,WAAAA,CAAW,OAAO,IAAI,CAAC;CAChD,MAAM,aAAa,OAAO,WAAW,KAAA,IAAY,IAAI,OAAO,SAAS;CACrE,IAAI,cAAc,MAAM,QACtB,MAAM,IAAI,MAAM,UAAU,OAAO,OAAO,0BAA0B,MAAM,OAAO,eAAe;CAGhG,MAAM,WAAW,OAAO,UAAU,KAAA,IAAY,MAAM,SAAS,KAAK,IAAI,MAAM,QAAQ,aAAa,OAAO,KAAK;CAC7G,MAAM,WAAW,MAAM,MAAM,YAAY,QAAQ;CACjD,MAAM,UAAA,GAASE,uBAAAA,iBAAAA,CAAiB,OAAA,GAAMC,6BAAAA,eAAAA,CAAe,KAAK,CAAC;CAC3D,MAAM,iBAA2B,CAAC;CAClC,MAAM,cAAc,OAAO,WAAW,QAAQ,MAAM,IAAI;CACxD,MAAM,SAAS,SAAS,KAAK,MAAM,UAAU;EAC3C,MAAM,aAAa,aAAa,QAAQ;EACxC,MAAM,QAAA,GAAOC,uBAAAA,iBAAAA,CAAiB,MAAM,UAAU;EAC9C,IAAI,OAAO,WAAW,MAAM,MAAM,KAAKC,gCAAAA,oBAAoB,aAAa,OAAO;EAC/E,eAAe,KAAK,UAAU;EAC9B,QAAA,GAAOD,uBAAAA,iBAAAA,EAAAA,GAAiBE,gCAAAA,aAAAA,CAAa,IAAI,CAAC,CAAC,MAAM,YAAY,IAAI,IAAI;CACvE,CAAC;CACD,MAAM,cAAA,GAAaC,gCAAAA,aAAAA,CAAa,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC;CAC9D,IAAI,OAAO,WAAW;CACtB,IAAI;CAEJ,IAAI,WAAW,WAAW;EACxB,MAAM,aAAa,KAAK,IAAI,GAAG,WAAW,cAAc,CAAC;EACzD,MAAM,aAAa,aAAa,aAAa;EAC7C,MAAM,SAAS,WAAW,gBAAgB,UAAU,MAAA,GAAKC,gCAAAA,WAAAA,CAAWH,gCAAAA,iBAAiB,EAAE,UAAU;EACjG,QAAQ,gBAAgB,WAAW,iBAAiB,OAAO,eAAe,WAAW;EACrF,UAAU,EAAE,WAAW;CACzB,OAAO;EACL,MAAM,UAAoB,CAAC;EAC3B,IAAI,eAAe,SAAS,GAC1B,QAAQ,KAAK,SAAS,eAAe,KAAK,IAAI,EAAE,6DAA6D;EAE/G,IAAI,WAAW,MAAM,QACnB,QAAQ,KAAK,GAAG,MAAM,SAAS,SAAS,kCAAkC,WAAW,EAAE,aAAa;EAEtG,IAAI,QAAQ,SAAS,GAAG,QAAQ,QAAQ,QAAQ,KAAK,IAAI,EAAE;CAC7D;CAEA,OAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ;EAAK,CAAC;EAAG;CAAQ;AACtD;AAMA,SAAgB,yBAAoE;CAClF,OAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EACF,eAAe;EACf,kBAAkB,CAChB,uHACA,iFACF;EACA,YAAY;EACZ,eAAe;EACf,SAAS,OACP,aACA,QACA,QACA,WACA,YACG,oBAAoB,QAAQ,QAAQ,KAAK,MAAM;CACtD;AACF;;;AC1JA,IAAA,oBAAA,GAAeI,4BAAAA,cAAAA,QAAqB;CAClC,GAAG,uBAAuB;CAC1B,aACE;AACJ,EAAE;;;ACFF,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAM,cAAiB,KAAa,UAAgB,UAAU,KAAA,IAAY,QAAS;CACjF,GAAG;CAAO,OAAO;EAAE,GAAI,MAA6B;GAAQ,cAAc;CAAI;AAChF;AAEA,MAAa,OAAA,GAAMC,4BAAAA,gBAAAA,CAAgB;CACjC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAAuC;EAAE,OAAO,CAAC,WAAW,2BAA2B,IAAI,EAAE,MAAM,OAAO,GAAG,GAAGC,kBAAU,OAAO,CAAC,CAAC,CAAC;CAAG,EAC7I;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.mts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAea,2CAAG"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { defineMcpPlugin, defineMcpTool } from "@agimon-ai/po-core/mcpFacet";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { formatFileHeader, formatTaggedLine, splitLines } from "@agimon-ai/po-hashline";
|
|
4
|
+
import { computeFileTag, decodeUtf8, displayPath, isWritableFile, resolveReadInputPath } from "@agimon-ai/po-hashline/files";
|
|
5
|
+
import { DEFAULT_MAX_BYTES, formatDimensionNote, formatSize, resizeImage, truncateHead, truncateLine } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { Type } from "typebox";
|
|
7
|
+
import { loadPiImageSettings } from "@agimon-ai/po-config/piConfig";
|
|
8
|
+
//#region src/schemas/readTool.ts
|
|
9
|
+
const ReadParamsSchema = Type.Object({
|
|
10
|
+
path: Type.String({ description: "Path to the file to read." }),
|
|
11
|
+
offset: Type.Optional(Type.Integer({
|
|
12
|
+
minimum: 1,
|
|
13
|
+
description: "One-based line offset."
|
|
14
|
+
})),
|
|
15
|
+
limit: Type.Optional(Type.Integer({
|
|
16
|
+
minimum: 1,
|
|
17
|
+
description: "Maximum number of lines to return."
|
|
18
|
+
}))
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/services/readImage/index.ts
|
|
22
|
+
const OMITTED_NOTE = "[Image omitted: could not be resized below the inline image size limit.]";
|
|
23
|
+
function imageLimits() {
|
|
24
|
+
return loadPiImageSettings();
|
|
25
|
+
}
|
|
26
|
+
async function applyImageLimits(content, limits, operations) {
|
|
27
|
+
if (!limits.autoResize || !content.some((part) => part.type === "image")) return content;
|
|
28
|
+
const resolved = [];
|
|
29
|
+
for (const part of content) {
|
|
30
|
+
if (part.type !== "image") {
|
|
31
|
+
resolved.push(part);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const resized = await operations.resize(Buffer.from(part.data, "base64"), part.mimeType, {
|
|
35
|
+
maxWidth: limits.maxDimension,
|
|
36
|
+
maxHeight: limits.maxDimension
|
|
37
|
+
});
|
|
38
|
+
if (!resized) {
|
|
39
|
+
resolved.push({
|
|
40
|
+
type: "text",
|
|
41
|
+
text: OMITTED_NOTE
|
|
42
|
+
});
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
resolved.push({
|
|
46
|
+
type: "image",
|
|
47
|
+
data: resized.data,
|
|
48
|
+
mimeType: resized.mimeType
|
|
49
|
+
});
|
|
50
|
+
const note = operations.formatDimensionNote(resized);
|
|
51
|
+
if (note) resolved.push({
|
|
52
|
+
type: "text",
|
|
53
|
+
text: note
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return resolved;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/services/readTool/index.ts
|
|
60
|
+
const resizeOperations = {
|
|
61
|
+
resize: resizeImage,
|
|
62
|
+
formatDimensionNote: (result) => formatDimensionNote(result)
|
|
63
|
+
};
|
|
64
|
+
function assertNotAborted(signal) {
|
|
65
|
+
if (signal?.aborted) throw new Error("Operation aborted");
|
|
66
|
+
}
|
|
67
|
+
function isImagePath(filePath) {
|
|
68
|
+
const extension = filePath.toLowerCase().match(/\.([a-z0-9]+)$/u)?.[1];
|
|
69
|
+
if (!extension) return void 0;
|
|
70
|
+
return {
|
|
71
|
+
avif: "image/avif",
|
|
72
|
+
gif: "image/gif",
|
|
73
|
+
jpeg: "image/jpeg",
|
|
74
|
+
jpg: "image/jpeg",
|
|
75
|
+
png: "image/png",
|
|
76
|
+
webp: "image/webp"
|
|
77
|
+
}[extension];
|
|
78
|
+
}
|
|
79
|
+
async function executeHeadlessRead(params, cwd, signal) {
|
|
80
|
+
assertNotAborted(signal);
|
|
81
|
+
const absolutePath = await resolveReadInputPath(params.path, cwd);
|
|
82
|
+
const bytes = await readFile(absolutePath);
|
|
83
|
+
assertNotAborted(signal);
|
|
84
|
+
const mimeType = isImagePath(absolutePath);
|
|
85
|
+
if (mimeType) return { content: await applyImageLimits([{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: `Read image file ${displayPath(absolutePath, cwd)} [${mimeType}]`
|
|
88
|
+
}, {
|
|
89
|
+
type: "image",
|
|
90
|
+
data: bytes.toString("base64"),
|
|
91
|
+
mimeType
|
|
92
|
+
}], imageLimits(), resizeOperations) };
|
|
93
|
+
if (!await isWritableFile(absolutePath)) return { content: [{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: decodeUtf8(bytes, displayPath(absolutePath, cwd))
|
|
96
|
+
}] };
|
|
97
|
+
return createTaggedReadResult(bytes, displayPath(absolutePath, cwd), params);
|
|
98
|
+
}
|
|
99
|
+
function createTaggedReadResult(bytes, path, params) {
|
|
100
|
+
const lines = splitLines(decodeUtf8(bytes, path));
|
|
101
|
+
const startIndex = params.offset === void 0 ? 0 : params.offset - 1;
|
|
102
|
+
if (startIndex >= lines.length) throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);
|
|
103
|
+
const endIndex = params.limit === void 0 ? lines.length : Math.min(lines.length, startIndex + params.limit);
|
|
104
|
+
const selected = lines.slice(startIndex, endIndex);
|
|
105
|
+
const header = formatFileHeader(path, computeFileTag(bytes));
|
|
106
|
+
const compactedLines = [];
|
|
107
|
+
const headerBytes = Buffer.byteLength(header, "utf8") + 1;
|
|
108
|
+
const tagged = selected.map((line, index) => {
|
|
109
|
+
const lineNumber = startIndex + index + 1;
|
|
110
|
+
const full = formatTaggedLine(line, lineNumber);
|
|
111
|
+
if (Buffer.byteLength(full, "utf8") <= DEFAULT_MAX_BYTES - headerBytes) return full;
|
|
112
|
+
compactedLines.push(lineNumber);
|
|
113
|
+
return formatTaggedLine(truncateLine(line).text, lineNumber, "", line);
|
|
114
|
+
});
|
|
115
|
+
const truncation = truncateHead([header, ...tagged].join("\n"));
|
|
116
|
+
let text = truncation.content;
|
|
117
|
+
let details;
|
|
118
|
+
if (truncation.truncated) {
|
|
119
|
+
const shownLines = Math.max(0, truncation.outputLines - 1);
|
|
120
|
+
const nextOffset = startIndex + shownLines + 1;
|
|
121
|
+
const reason = truncation.truncatedBy === "bytes" ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : "";
|
|
122
|
+
text += `\n\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;
|
|
123
|
+
details = { truncation };
|
|
124
|
+
} else {
|
|
125
|
+
const notices = [];
|
|
126
|
+
if (compactedLines.length > 0) notices.push(`Lines ${compactedLines.join(", ")} shown compactly. Their anchors hash the full original lines`);
|
|
127
|
+
if (endIndex < lines.length) notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);
|
|
128
|
+
if (notices.length > 0) text += `\n\n[${notices.join(". ")}.]`;
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
content: [{
|
|
132
|
+
type: "text",
|
|
133
|
+
text
|
|
134
|
+
}],
|
|
135
|
+
details
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function createHeadlessReadTool() {
|
|
139
|
+
return {
|
|
140
|
+
name: "read",
|
|
141
|
+
label: "read",
|
|
142
|
+
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.",
|
|
143
|
+
promptSnippet: "Read file contents with snapshot-bound line anchors",
|
|
144
|
+
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."],
|
|
145
|
+
parameters: ReadParamsSchema,
|
|
146
|
+
executionMode: "parallel",
|
|
147
|
+
execute: async (_toolCallId, params, signal, _onUpdate, context) => executeHeadlessRead(params, context.cwd, signal)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts
|
|
152
|
+
var read_mcp_default = defineMcpTool(() => ({
|
|
153
|
+
...createHeadlessReadTool(),
|
|
154
|
+
description: "Read a text file from the Po session's repository on the host machine (open, view, cat). Returns a file hash and line anchors such as 5#abc that this server's edit tool requires. Use offset and limit for large files; text is truncated at 2000 lines or 50KB."
|
|
155
|
+
}));
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region generated/mcp.ts
|
|
158
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
159
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
160
|
+
...identity,
|
|
161
|
+
...value
|
|
162
|
+
};
|
|
163
|
+
const withWidget = (key, value) => value === void 0 ? value : {
|
|
164
|
+
...value,
|
|
165
|
+
_meta: {
|
|
166
|
+
...value._meta,
|
|
167
|
+
["po/widget"]: key
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const mcp = defineMcpPlugin({
|
|
171
|
+
name: "@agimon-ai/po-read",
|
|
172
|
+
session: (context) => ({ get tools() {
|
|
173
|
+
return [withWidget("@agimon-ai/po-read/read", via({ name: "read" }, at(read_mcp_default, context)))];
|
|
174
|
+
} })
|
|
175
|
+
});
|
|
176
|
+
//#endregion
|
|
177
|
+
export { mcp as default, mcp };
|
|
178
|
+
|
|
179
|
+
//# sourceMappingURL=mcp.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.mjs","names":["toolRead"],"sources":["../../src/schemas/readTool.ts","../../src/services/readImage/index.ts","../../src/services/readTool/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\nexport const ReadParamsSchema = Type.Object({\n path: Type.String({ description: 'Path to the file to read.' }),\n offset: Type.Optional(Type.Integer({ minimum: 1, description: 'One-based line offset.' })),\n limit: Type.Optional(Type.Integer({ minimum: 1, description: 'Maximum number of lines to return.' })),\n});\n\nexport type ReadParams = Static<typeof ReadParamsSchema>;\n","import { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/po-config/piConfig';\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","import { readFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/po-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/po-hashline/files';\nimport {\n DEFAULT_MAX_BYTES,\n formatDimensionNote,\n formatSize,\n resizeImage,\n truncateHead,\n truncateLine,\n type ReadToolDetails,\n} 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","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createHeadlessReadTool } from '../../../../../services/readTool';\n\nexport default defineMcpTool(() => ({\n ...createHeadlessReadTool(),\n description:\n \"Read a text file from the Po session's repository on the host machine (open, view, cat). Returns a file hash and line anchors such as 5#abc that this server's edit tool requires. Use offset and limit for large files; text is truncated at 2000 lines or 50KB.\",\n}));\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport toolRead from '../src/extensions/workspaces/sessions/(backend)/tool/read.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-read',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-read/read\", via({ name: 'read' }, at(toolRead, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|