@ai-agent-forge/plugin-git-repository 0.85.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/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # @agent-forge/plugin-git-repository
2
+
3
+ Git repository capability plugin for [Agent Forge](https://github.com/agent-forge/agent-forge), extracted from the
4
+ host's `src/plugins/git-repository` (D-075 S3). It registers a single read-only tool, `git_repository_inspect`, which
5
+ returns bounded git status, diff summary, or log summary aggregates through a host-owned workspace reference; no diff
6
+ hunk, raw log, branch, or command output is exposed.
7
+
8
+ The plugin has no standalone git binding at load time. Without host-injected resolver/runner seams the tool reports an
9
+ explicit `unsupported` result; the host owns every git command and repository path.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ # from a local checkout
15
+ agent-forge plugin install ./plugins/git-repository
16
+
17
+ # from npm
18
+ agent-forge plugin install @agent-forge/plugin-git-repository
19
+ ```
20
+
21
+ The package ships two manifests: `agent-forge.json` (ecosystem manifest for the deterministic plugin resolver) and
22
+ `plugin.json` (host-load manifest consumed by `loadCapabilityPluginFromManifest`, entry `./dist/entry.js`).
23
+
24
+ ## Library surface
25
+
26
+ `import { createNodeGitRepositoryAdapterV1, createGitRepositoryCapabilityFactoryV1 } from "@agent-forge/plugin-git-repository";`
27
+
28
+ - `createNodeGitRepositoryAdapterV1(options?)` — read-only adapter; requires host-injected `resolver`/`runner` to
29
+ produce results, otherwise returns `unsupported`.
30
+ - `createDeterministicGitRepositoryAdapterV1(fixtures)` — deterministic fixture adapter for tests.
31
+ - `createGitRepositoryCapabilityFactoryV1(adapter)` — registers `git_repository_inspect` on a `CapabilityAPI`.
32
+ - Default export of `dist/entry.js` — the precomposed factory used by external plugin loading.
33
+
34
+ ## Development
35
+
36
+ ```sh
37
+ npm install --ignore-scripts # repo root, creates the workspace symlink
38
+ npm run build # plugins/git-repository (tsgo)
39
+ npm test # vitest; the entry fidelity test requires `npm run build` first
40
+ ```
@@ -0,0 +1,12 @@
1
+ {
2
+ "packageFormatVersion": 1,
3
+ "id": "agent-forge.plugin.git-repository",
4
+ "pluginVersion": "0.84.4",
5
+ "apiVersion": "1",
6
+ "minHostVersion": "0.84.4",
7
+ "capabilityRefs": [{ "id": "git.repository.inspect", "version": 1 }],
8
+ "platform": {
9
+ "os": ["windows", "macos", "linux"],
10
+ "runtime": [{ "name": "node" }]
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { CapabilityPluginSyncFactory } from "@agent-forge/plugin-sdk";
2
+ import type { GitRepositoryAdapterV1 } from "./types.ts";
3
+ /** Registers a single read-only repository inspection tool with opaque input only. */
4
+ export declare function createGitRepositoryCapabilityFactoryV1(runtime: GitRepositoryAdapterV1): CapabilityPluginSyncFactory;
5
+ //# sourceMappingURL=capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAS3E,OAAO,KAAK,EAAE,sBAAsB,EAAyB,MAAM,YAAY,CAAC;AAqFhF,sFAAsF;AACtF,wBAAgB,sCAAsC,CAAC,OAAO,EAAE,sBAAsB,GAAG,2BAA2B,CA0CnH","sourcesContent":["import type { CapabilityPluginSyncFactory } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\nimport { Value } from \"typebox/value\";\nimport {\n\tGIT_REPOSITORY_MAX_ENTRIES_V1,\n\tGIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1,\n\tGIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1,\n\tprojectGitRepositoryResultV1,\n} from \"./node-adapter.ts\";\nimport type { GitRepositoryAdapterV1, GitRepositoryResultV1 } from \"./types.ts\";\n\nconst requestSchema = Type.Object(\n\t{\n\t\tworkspaceRef: Type.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1,\n\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._:-]*$\",\n\t\t}),\n\t\toperation: Type.Union([Type.Literal(\"status\"), Type.Literal(\"diff-summary\"), Type.Literal(\"log-summary\")]),\n\t\tlimit: Type.Optional(Type.Integer({ minimum: 1, maximum: GIT_REPOSITORY_MAX_ENTRIES_V1 })),\n\t\ttimeoutMs: Type.Optional(Type.Integer({ minimum: 1, maximum: 120_000 })),\n\t},\n\t{ additionalProperties: false },\n);\nconst inputSchema = Type.Object({ request: requestSchema }, { additionalProperties: false });\n\nfunction emptySummary(): GitRepositoryResultV1[\"summary\"] {\n\treturn {\n\t\tfilesChanged: 0,\n\t\tadded: 0,\n\t\tmodified: 0,\n\t\tdeleted: 0,\n\t\tuntracked: 0,\n\t\tconflicted: 0,\n\t\tadditions: 0,\n\t\tdeletions: 0,\n\t\tcommits: 0,\n\t};\n}\n\nfunction fallback(operationId: string, status: \"failed\" | \"cancelled\"): GitRepositoryResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus,\n\t\tsummary: emptySummary(),\n\t\tentries: [],\n\t\tentriesTruncated: false,\n\t\terror: {\n\t\t\tversion: 1,\n\t\t\tcode: status === \"cancelled\" ? \"cancelled\" : \"adapter_failed\",\n\t\t\tmessage: status === \"cancelled\" ? \"Git repository read was cancelled\" : \"Git repository adapter failed\",\n\t\t},\n\t};\n}\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new Error(\"Git repository input must be an object\");\n\treturn value as Record<string, unknown>;\n}\n\nfunction invocation(\n\tfirst: unknown,\n\tsecond: unknown,\n): { readonly input: Record<string, unknown>; readonly operationId: string } {\n\tconst candidate = typeof first === \"string\" && first.trim() !== \"\" ? first : \"profile-runtime-tool-call\";\n\treturn {\n\t\tinput: objectInput(first, second),\n\t\toperationId:\n\t\t\tcandidate.length <= GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1 ? candidate : \"profile-runtime-tool-call\",\n\t};\n}\n\nfunction signalFrom(value: unknown): AbortSignal | undefined {\n\treturn value instanceof AbortSignal ? value : undefined;\n}\n\nconst CANCELLATION_V1 = Symbol(\"git-repository-capability-cancelled\");\n\nfunction runWithCancellation(\n\trun: () => Promise<GitRepositoryResultV1>,\n\tsignal: AbortSignal | undefined,\n): Promise<GitRepositoryResultV1 | typeof CANCELLATION_V1> {\n\tif (signal === undefined) return run();\n\tlet onAbort!: () => void;\n\tconst cancellation = new Promise<typeof CANCELLATION_V1>((resolve) => {\n\t\tonAbort = () => resolve(CANCELLATION_V1);\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n\treturn Promise.race([run(), cancellation]).finally(() => signal.removeEventListener(\"abort\", onAbort));\n}\n\n/** Registers a single read-only repository inspection tool with opaque input only. */\nexport function createGitRepositoryCapabilityFactoryV1(runtime: GitRepositoryAdapterV1): CapabilityPluginSyncFactory {\n\treturn (api) => {\n\t\tapi.registerTool({\n\t\t\tname: \"git_repository_inspect\",\n\t\t\tlabel: \"Inspect repository\",\n\t\t\tdescription: \"Read bounded git status, diff summary, or log summary through a host-owned workspace reference\",\n\t\t\tparameters: inputSchema,\n\t\t\texecute: async (first: unknown, second?: unknown, third?: unknown) => {\n\t\t\t\tlet call: { readonly input: Record<string, unknown>; readonly operationId: string };\n\t\t\t\ttry {\n\t\t\t\t\tcall = invocation(first, second);\n\t\t\t\t} catch {\n\t\t\t\t\treturn fallback(\"profile-runtime-tool-call\", \"failed\");\n\t\t\t\t}\n\t\t\t\tconst signal = signalFrom(third);\n\t\t\t\tif (signal?.aborted) return fallback(call.operationId, \"cancelled\");\n\t\t\t\tif (!Value.Check(inputSchema, call.input)) return fallback(call.operationId, \"failed\");\n\t\t\t\tconst request = call.input.request as Record<string, unknown>;\n\t\t\t\ttry {\n\t\t\t\t\tconst output = await runWithCancellation(\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\truntime.run(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\toperationId: call.operationId,\n\t\t\t\t\t\t\t\t\tworkspaceRef: request.workspaceRef as string,\n\t\t\t\t\t\t\t\t\toperation: request.operation as \"status\" | \"diff-summary\" | \"log-summary\",\n\t\t\t\t\t\t\t\t\t...(request.limit === undefined ? {} : { limit: request.limit as number }),\n\t\t\t\t\t\t\t\t\t...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs as number }),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t);\n\t\t\t\t\tif (output === CANCELLATION_V1) return fallback(call.operationId, \"cancelled\");\n\t\t\t\t\treturn projectGitRepositoryResultV1(output, call.operationId) ?? fallback(call.operationId, \"failed\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn fallback(call.operationId, \"failed\");\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t};\n}\n"]}
@@ -0,0 +1,112 @@
1
+ import { Type } from "typebox";
2
+ import { Value } from "typebox/value";
3
+ import { GIT_REPOSITORY_MAX_ENTRIES_V1, GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1, GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1, projectGitRepositoryResultV1, } from "./node-adapter.js";
4
+ const requestSchema = Type.Object({
5
+ workspaceRef: Type.String({
6
+ minLength: 1,
7
+ maxLength: GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1,
8
+ pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
9
+ }),
10
+ operation: Type.Union([Type.Literal("status"), Type.Literal("diff-summary"), Type.Literal("log-summary")]),
11
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: GIT_REPOSITORY_MAX_ENTRIES_V1 })),
12
+ timeoutMs: Type.Optional(Type.Integer({ minimum: 1, maximum: 120_000 })),
13
+ }, { additionalProperties: false });
14
+ const inputSchema = Type.Object({ request: requestSchema }, { additionalProperties: false });
15
+ function emptySummary() {
16
+ return {
17
+ filesChanged: 0,
18
+ added: 0,
19
+ modified: 0,
20
+ deleted: 0,
21
+ untracked: 0,
22
+ conflicted: 0,
23
+ additions: 0,
24
+ deletions: 0,
25
+ commits: 0,
26
+ };
27
+ }
28
+ function fallback(operationId, status) {
29
+ return {
30
+ version: 1,
31
+ operationId,
32
+ status,
33
+ summary: emptySummary(),
34
+ entries: [],
35
+ entriesTruncated: false,
36
+ error: {
37
+ version: 1,
38
+ code: status === "cancelled" ? "cancelled" : "adapter_failed",
39
+ message: status === "cancelled" ? "Git repository read was cancelled" : "Git repository adapter failed",
40
+ },
41
+ };
42
+ }
43
+ function objectInput(first, second) {
44
+ const value = second === undefined ? first : second;
45
+ if (value === null || typeof value !== "object" || Array.isArray(value))
46
+ throw new Error("Git repository input must be an object");
47
+ return value;
48
+ }
49
+ function invocation(first, second) {
50
+ const candidate = typeof first === "string" && first.trim() !== "" ? first : "profile-runtime-tool-call";
51
+ return {
52
+ input: objectInput(first, second),
53
+ operationId: candidate.length <= GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1 ? candidate : "profile-runtime-tool-call",
54
+ };
55
+ }
56
+ function signalFrom(value) {
57
+ return value instanceof AbortSignal ? value : undefined;
58
+ }
59
+ const CANCELLATION_V1 = Symbol("git-repository-capability-cancelled");
60
+ function runWithCancellation(run, signal) {
61
+ if (signal === undefined)
62
+ return run();
63
+ let onAbort;
64
+ const cancellation = new Promise((resolve) => {
65
+ onAbort = () => resolve(CANCELLATION_V1);
66
+ signal.addEventListener("abort", onAbort, { once: true });
67
+ });
68
+ return Promise.race([run(), cancellation]).finally(() => signal.removeEventListener("abort", onAbort));
69
+ }
70
+ /** Registers a single read-only repository inspection tool with opaque input only. */
71
+ export function createGitRepositoryCapabilityFactoryV1(runtime) {
72
+ return (api) => {
73
+ api.registerTool({
74
+ name: "git_repository_inspect",
75
+ label: "Inspect repository",
76
+ description: "Read bounded git status, diff summary, or log summary through a host-owned workspace reference",
77
+ parameters: inputSchema,
78
+ execute: async (first, second, third) => {
79
+ let call;
80
+ try {
81
+ call = invocation(first, second);
82
+ }
83
+ catch {
84
+ return fallback("profile-runtime-tool-call", "failed");
85
+ }
86
+ const signal = signalFrom(third);
87
+ if (signal?.aborted)
88
+ return fallback(call.operationId, "cancelled");
89
+ if (!Value.Check(inputSchema, call.input))
90
+ return fallback(call.operationId, "failed");
91
+ const request = call.input.request;
92
+ try {
93
+ const output = await runWithCancellation(() => runtime.run({
94
+ version: 1,
95
+ operationId: call.operationId,
96
+ workspaceRef: request.workspaceRef,
97
+ operation: request.operation,
98
+ ...(request.limit === undefined ? {} : { limit: request.limit }),
99
+ ...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
100
+ }, signal), signal);
101
+ if (output === CANCELLATION_V1)
102
+ return fallback(call.operationId, "cancelled");
103
+ return projectGitRepositoryResultV1(output, call.operationId) ?? fallback(call.operationId, "failed");
104
+ }
105
+ catch {
106
+ return fallback(call.operationId, "failed");
107
+ }
108
+ },
109
+ });
110
+ };
111
+ }
112
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CapabilityPluginSyncFactory } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\nimport { Value } from \"typebox/value\";\nimport {\n\tGIT_REPOSITORY_MAX_ENTRIES_V1,\n\tGIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1,\n\tGIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1,\n\tprojectGitRepositoryResultV1,\n} from \"./node-adapter.ts\";\nimport type { GitRepositoryAdapterV1, GitRepositoryResultV1 } from \"./types.ts\";\n\nconst requestSchema = Type.Object(\n\t{\n\t\tworkspaceRef: Type.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1,\n\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._:-]*$\",\n\t\t}),\n\t\toperation: Type.Union([Type.Literal(\"status\"), Type.Literal(\"diff-summary\"), Type.Literal(\"log-summary\")]),\n\t\tlimit: Type.Optional(Type.Integer({ minimum: 1, maximum: GIT_REPOSITORY_MAX_ENTRIES_V1 })),\n\t\ttimeoutMs: Type.Optional(Type.Integer({ minimum: 1, maximum: 120_000 })),\n\t},\n\t{ additionalProperties: false },\n);\nconst inputSchema = Type.Object({ request: requestSchema }, { additionalProperties: false });\n\nfunction emptySummary(): GitRepositoryResultV1[\"summary\"] {\n\treturn {\n\t\tfilesChanged: 0,\n\t\tadded: 0,\n\t\tmodified: 0,\n\t\tdeleted: 0,\n\t\tuntracked: 0,\n\t\tconflicted: 0,\n\t\tadditions: 0,\n\t\tdeletions: 0,\n\t\tcommits: 0,\n\t};\n}\n\nfunction fallback(operationId: string, status: \"failed\" | \"cancelled\"): GitRepositoryResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus,\n\t\tsummary: emptySummary(),\n\t\tentries: [],\n\t\tentriesTruncated: false,\n\t\terror: {\n\t\t\tversion: 1,\n\t\t\tcode: status === \"cancelled\" ? \"cancelled\" : \"adapter_failed\",\n\t\t\tmessage: status === \"cancelled\" ? \"Git repository read was cancelled\" : \"Git repository adapter failed\",\n\t\t},\n\t};\n}\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new Error(\"Git repository input must be an object\");\n\treturn value as Record<string, unknown>;\n}\n\nfunction invocation(\n\tfirst: unknown,\n\tsecond: unknown,\n): { readonly input: Record<string, unknown>; readonly operationId: string } {\n\tconst candidate = typeof first === \"string\" && first.trim() !== \"\" ? first : \"profile-runtime-tool-call\";\n\treturn {\n\t\tinput: objectInput(first, second),\n\t\toperationId:\n\t\t\tcandidate.length <= GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1 ? candidate : \"profile-runtime-tool-call\",\n\t};\n}\n\nfunction signalFrom(value: unknown): AbortSignal | undefined {\n\treturn value instanceof AbortSignal ? value : undefined;\n}\n\nconst CANCELLATION_V1 = Symbol(\"git-repository-capability-cancelled\");\n\nfunction runWithCancellation(\n\trun: () => Promise<GitRepositoryResultV1>,\n\tsignal: AbortSignal | undefined,\n): Promise<GitRepositoryResultV1 | typeof CANCELLATION_V1> {\n\tif (signal === undefined) return run();\n\tlet onAbort!: () => void;\n\tconst cancellation = new Promise<typeof CANCELLATION_V1>((resolve) => {\n\t\tonAbort = () => resolve(CANCELLATION_V1);\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n\treturn Promise.race([run(), cancellation]).finally(() => signal.removeEventListener(\"abort\", onAbort));\n}\n\n/** Registers a single read-only repository inspection tool with opaque input only. */\nexport function createGitRepositoryCapabilityFactoryV1(runtime: GitRepositoryAdapterV1): CapabilityPluginSyncFactory {\n\treturn (api) => {\n\t\tapi.registerTool({\n\t\t\tname: \"git_repository_inspect\",\n\t\t\tlabel: \"Inspect repository\",\n\t\t\tdescription: \"Read bounded git status, diff summary, or log summary through a host-owned workspace reference\",\n\t\t\tparameters: inputSchema,\n\t\t\texecute: async (first: unknown, second?: unknown, third?: unknown) => {\n\t\t\t\tlet call: { readonly input: Record<string, unknown>; readonly operationId: string };\n\t\t\t\ttry {\n\t\t\t\t\tcall = invocation(first, second);\n\t\t\t\t} catch {\n\t\t\t\t\treturn fallback(\"profile-runtime-tool-call\", \"failed\");\n\t\t\t\t}\n\t\t\t\tconst signal = signalFrom(third);\n\t\t\t\tif (signal?.aborted) return fallback(call.operationId, \"cancelled\");\n\t\t\t\tif (!Value.Check(inputSchema, call.input)) return fallback(call.operationId, \"failed\");\n\t\t\t\tconst request = call.input.request as Record<string, unknown>;\n\t\t\t\ttry {\n\t\t\t\t\tconst output = await runWithCancellation(\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\truntime.run(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\toperationId: call.operationId,\n\t\t\t\t\t\t\t\t\tworkspaceRef: request.workspaceRef as string,\n\t\t\t\t\t\t\t\t\toperation: request.operation as \"status\" | \"diff-summary\" | \"log-summary\",\n\t\t\t\t\t\t\t\t\t...(request.limit === undefined ? {} : { limit: request.limit as number }),\n\t\t\t\t\t\t\t\t\t...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs as number }),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t);\n\t\t\t\t\tif (output === CANCELLATION_V1) return fallback(call.operationId, \"cancelled\");\n\t\t\t\t\treturn projectGitRepositoryResultV1(output, call.operationId) ?? fallback(call.operationId, \"failed\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn fallback(call.operationId, \"failed\");\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t};\n}\n"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Default-export factory consumed by the host's `loadCapabilityPluginFromManifest`.
3
+ *
4
+ * The adapter is deliberately constructed without host injection: repository
5
+ * resolution and git command execution are host-owned (resolver + runner
6
+ * seams), so the registered `git_repository_inspect` tool reports an explicit
7
+ * `unsupported` result until a host binds a real adapter through its own
8
+ * profile machinery (fail-closed by design, same contract as the embedded
9
+ * build).
10
+ */
11
+ declare const _default: import("@agent-forge/plugin-sdk").CapabilityPluginSyncFactory;
12
+ export default _default;
13
+ //# sourceMappingURL=entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;;AACH,wBAA0F","sourcesContent":["import { createGitRepositoryCapabilityFactoryV1 } from \"./capability.ts\";\nimport { createNodeGitRepositoryAdapterV1 } from \"./node-adapter.ts\";\n\n/**\n * Default-export factory consumed by the host's `loadCapabilityPluginFromManifest`.\n *\n * The adapter is deliberately constructed without host injection: repository\n * resolution and git command execution are host-owned (resolver + runner\n * seams), so the registered `git_repository_inspect` tool reports an explicit\n * `unsupported` result until a host binds a real adapter through its own\n * profile machinery (fail-closed by design, same contract as the embedded\n * build).\n */\nexport default createGitRepositoryCapabilityFactoryV1(createNodeGitRepositoryAdapterV1());\n"]}
package/dist/entry.js ADDED
@@ -0,0 +1,14 @@
1
+ import { createGitRepositoryCapabilityFactoryV1 } from "./capability.js";
2
+ import { createNodeGitRepositoryAdapterV1 } from "./node-adapter.js";
3
+ /**
4
+ * Default-export factory consumed by the host's `loadCapabilityPluginFromManifest`.
5
+ *
6
+ * The adapter is deliberately constructed without host injection: repository
7
+ * resolution and git command execution are host-owned (resolver + runner
8
+ * seams), so the registered `git_repository_inspect` tool reports an explicit
9
+ * `unsupported` result until a host binds a real adapter through its own
10
+ * profile machinery (fail-closed by design, same contract as the embedded
11
+ * build).
12
+ */
13
+ export default createGitRepositoryCapabilityFactoryV1(createNodeGitRepositoryAdapterV1());
14
+ //# sourceMappingURL=entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sCAAsC,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,gCAAgC,EAAE,MAAM,mBAAmB,CAAC;AAErE;;;;;;;;;GASG;AACH,eAAe,sCAAsC,CAAC,gCAAgC,EAAE,CAAC,CAAC","sourcesContent":["import { createGitRepositoryCapabilityFactoryV1 } from \"./capability.ts\";\nimport { createNodeGitRepositoryAdapterV1 } from \"./node-adapter.ts\";\n\n/**\n * Default-export factory consumed by the host's `loadCapabilityPluginFromManifest`.\n *\n * The adapter is deliberately constructed without host injection: repository\n * resolution and git command execution are host-owned (resolver + runner\n * seams), so the registered `git_repository_inspect` tool reports an explicit\n * `unsupported` result until a host binds a real adapter through its own\n * profile machinery (fail-closed by design, same contract as the embedded\n * build).\n */\nexport default createGitRepositoryCapabilityFactoryV1(createNodeGitRepositoryAdapterV1());\n"]}
@@ -0,0 +1,6 @@
1
+ export { createGitRepositoryCapabilityFactoryV1 } from "./capability.ts";
2
+ export { createDeterministicGitRepositoryAdapterV1, createNodeGitRepositoryAdapterV1, GIT_REPOSITORY_MAX_ENTRIES_V1, GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1, GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1, GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1, GIT_REPOSITORY_MAX_TEXT_CHARS_V1, projectGitRepositoryResultV1, sanitizeGitRepositoryTextV1, } from "./node-adapter.ts";
3
+ export { sanitizeBinaryOutput } from "./shell-utils.ts";
4
+ export type * from "./types.ts";
5
+ export type { NodeGitRepositoryAdapterOptionsV1 } from "./types.ts";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sCAAsC,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EACN,yCAAyC,EACzC,gCAAgC,EAChC,6BAA6B,EAC7B,sCAAsC,EACtC,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,4BAA4B,EAC5B,2BAA2B,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,mBAAmB,YAAY,CAAC;AAChC,YAAY,EAAE,iCAAiC,EAAE,MAAM,YAAY,CAAC","sourcesContent":["export { createGitRepositoryCapabilityFactoryV1 } from \"./capability.ts\";\nexport {\n\tcreateDeterministicGitRepositoryAdapterV1,\n\tcreateNodeGitRepositoryAdapterV1,\n\tGIT_REPOSITORY_MAX_ENTRIES_V1,\n\tGIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1,\n\tGIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1,\n\tGIT_REPOSITORY_MAX_OUTPUT_BYTES_V1,\n\tGIT_REPOSITORY_MAX_TEXT_CHARS_V1,\n\tprojectGitRepositoryResultV1,\n\tsanitizeGitRepositoryTextV1,\n} from \"./node-adapter.ts\";\nexport { sanitizeBinaryOutput } from \"./shell-utils.ts\";\nexport type * from \"./types.ts\";\nexport type { NodeGitRepositoryAdapterOptionsV1 } from \"./types.ts\";\n"]}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { createGitRepositoryCapabilityFactoryV1 } from "./capability.js";
2
+ export { createDeterministicGitRepositoryAdapterV1, createNodeGitRepositoryAdapterV1, GIT_REPOSITORY_MAX_ENTRIES_V1, GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1, GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1, GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1, GIT_REPOSITORY_MAX_TEXT_CHARS_V1, projectGitRepositoryResultV1, sanitizeGitRepositoryTextV1, } from "./node-adapter.js";
3
+ export { sanitizeBinaryOutput } from "./shell-utils.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sCAAsC,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EACN,yCAAyC,EACzC,gCAAgC,EAChC,6BAA6B,EAC7B,sCAAsC,EACtC,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,4BAA4B,EAC5B,2BAA2B,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["export { createGitRepositoryCapabilityFactoryV1 } from \"./capability.ts\";\nexport {\n\tcreateDeterministicGitRepositoryAdapterV1,\n\tcreateNodeGitRepositoryAdapterV1,\n\tGIT_REPOSITORY_MAX_ENTRIES_V1,\n\tGIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1,\n\tGIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1,\n\tGIT_REPOSITORY_MAX_OUTPUT_BYTES_V1,\n\tGIT_REPOSITORY_MAX_TEXT_CHARS_V1,\n\tprojectGitRepositoryResultV1,\n\tsanitizeGitRepositoryTextV1,\n} from \"./node-adapter.ts\";\nexport { sanitizeBinaryOutput } from \"./shell-utils.ts\";\nexport type * from \"./types.ts\";\nexport type { NodeGitRepositoryAdapterOptionsV1 } from \"./types.ts\";\n"]}
@@ -0,0 +1,15 @@
1
+ import { type GitRepositoryAdapterV1, type GitRepositoryDeterministicFixtureV1, type GitRepositoryResultV1, type NodeGitRepositoryAdapterOptionsV1 } from "./types.ts";
2
+ export declare const GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1: number;
3
+ export declare const GIT_REPOSITORY_MAX_ENTRIES_V1 = 64;
4
+ export declare const GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1 = 256;
5
+ export declare const GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1 = 256;
6
+ export declare const GIT_REPOSITORY_MAX_TEXT_CHARS_V1 = 512;
7
+ /** Removes credentials and host paths before host-provided text crosses into model context. */
8
+ export declare function sanitizeGitRepositoryTextV1(value: string, maximum?: number): string;
9
+ /** Strictly projects arbitrary adapter output into the bounded public result DTO. */
10
+ export declare function projectGitRepositoryResultV1(value: unknown, operationId: string): GitRepositoryResultV1 | undefined;
11
+ /** Creates a host-bound read-only adapter with no command, cwd, env, or raw-output input surface. */
12
+ export declare function createNodeGitRepositoryAdapterV1(options?: NodeGitRepositoryAdapterOptionsV1): GitRepositoryAdapterV1;
13
+ /** Deterministic fixture seam for unit tests; it never executes a git binary. */
14
+ export declare function createDeterministicGitRepositoryAdapterV1(fixtures: readonly GitRepositoryDeterministicFixtureV1[]): GitRepositoryAdapterV1;
15
+ //# sourceMappingURL=node-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-adapter.d.ts","sourceRoot":"","sources":["../src/node-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,EAGN,KAAK,sBAAsB,EAE3B,KAAK,mCAAmC,EAOxC,KAAK,qBAAqB,EAG1B,KAAK,iCAAiC,EACtC,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,kCAAkC,QAAY,CAAC;AAC5D,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAChD,eAAO,MAAM,wCAAwC,MAAM,CAAC;AAC5D,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAC1D,eAAO,MAAM,gCAAgC,MAAM,CAAC;AA2CpD,+FAA+F;AAC/F,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,SAAmC,GAAG,MAAM,CAW7G;AA4OD,qFAAqF;AACrF,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAyEnH;AA+ED,qGAAqG;AACrG,wBAAgB,gCAAgC,CAC/C,OAAO,GAAE,iCAAsC,GAC7C,sBAAsB,CA2CxB;AAED,iFAAiF;AACjF,wBAAgB,yCAAyC,CACxD,QAAQ,EAAE,SAAS,mCAAmC,EAAE,GACtD,sBAAsB,CAmBxB","sourcesContent":["import type { JsonValue, PluginErrorV1 } from \"@agent-forge/plugin-sdk\";\nimport { projectPluginContextV1 } from \"@agent-forge/plugin-sdk\";\nimport { sanitizeBinaryOutput } from \"./shell-utils.ts\";\nimport {\n\tGIT_REPOSITORY_HOST_ADAPTER_ID_V1,\n\tGIT_REPOSITORY_HOST_ADAPTER_VERSION_V1,\n\ttype GitRepositoryAdapterV1,\n\ttype GitRepositoryCommitEntryV1,\n\ttype GitRepositoryDeterministicFixtureV1,\n\ttype GitRepositoryEntryV1,\n\ttype GitRepositoryFileEntryV1,\n\ttype GitRepositoryFileStatusV1,\n\ttype GitRepositoryHostResultV1,\n\ttype GitRepositoryOperationV1,\n\ttype GitRepositoryRequestV1,\n\ttype GitRepositoryResultV1,\n\ttype GitRepositorySummaryV1,\n\ttype GitRepositoryTargetV1,\n\ttype NodeGitRepositoryAdapterOptionsV1,\n} from \"./types.ts\";\n\nexport const GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1 = 64 * 1024;\nexport const GIT_REPOSITORY_MAX_ENTRIES_V1 = 64;\nexport const GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1 = 256;\nexport const GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1 = 256;\nexport const GIT_REPOSITORY_MAX_TEXT_CHARS_V1 = 512;\n\nconst DEFAULT_TIMEOUT_MS_V1 = 15_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst MAX_HOST_ENTRIES_V1 = 4096;\nconst MAX_COUNT_V1 = 10_000_000;\nconst MAX_LOCATION_CHARS_V1 = 512;\nconst MAX_HOST_SUBJECT_CHARS_V1 = 8192;\nconst MAX_ERROR_MESSAGE_CHARS_V1 = 8192;\nconst OPERATIONS_V1 = new Set<GitRepositoryOperationV1>([\"status\", \"diff-summary\", \"log-summary\"]);\nconst FILE_STATUSES_V1 = new Set<GitRepositoryFileStatusV1>([\n\t\"added\",\n\t\"modified\",\n\t\"deleted\",\n\t\"renamed\",\n\t\"untracked\",\n\t\"conflicted\",\n]);\nconst OPAQUE_REF_V1 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;\nconst RELATIVE_LOCATION_V1 = /^[A-Za-z0-9_][A-Za-z0-9._/-]{0,511}$/u;\nconst COMMIT_REF_V1 = /^[a-f0-9]{7,64}$/u;\nconst ERROR_CODE_V1 = /^[a-z][a-z0-9_]{0,127}$/u;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction hasOnlyKeys(value: Record<string, unknown>, allowed: readonly string[]): boolean {\n\treturn Object.keys(value).every((key) => allowed.includes(key));\n}\n\nfunction safeOpaqueRef(value: string): boolean {\n\treturn value.length <= GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1 && OPAQUE_REF_V1.test(value);\n}\n\nfunction safeRelativeLocation(value: string): boolean {\n\treturn (\n\t\tvalue.length <= MAX_LOCATION_CHARS_V1 &&\n\t\tRELATIVE_LOCATION_V1.test(value) &&\n\t\t!value.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n\t);\n}\n\n/** Removes credentials and host paths before host-provided text crosses into model context. */\nexport function sanitizeGitRepositoryTextV1(value: string, maximum = GIT_REPOSITORY_MAX_TEXT_CHARS_V1): string {\n\tconst sanitized = sanitizeBinaryOutput(value)\n\t\t.replace(\n\t\t\t/((?:api[-_ ]?key|access[-_ ]?token|auth(?:orization)?|bearer|password|passwd|secret|token)\\s*[:=]\\s*)([^\\s,;]+)/giu,\n\t\t\t\"$1[redacted-secret]\",\n\t\t)\n\t\t.replace(/[A-Za-z]:[\\\\/][^\\s\"'<>]*/gu, \"[redacted-path]\")\n\t\t.replace(/\\\\\\\\[^\\s\"'<>]*/gu, \"[redacted-path]\")\n\t\t.replace(/(?<![:/])\\/(?:[^\\s\"'<>/]+\\/)*[^\\s\"'<>/]+/gu, \"[redacted-path]\");\n\tconst characters = [...sanitized];\n\treturn characters.length <= maximum ? sanitized : characters.slice(0, maximum).join(\"\");\n}\n\nfunction validPositiveInteger(value: unknown, maximum: number): value is number {\n\treturn Number.isSafeInteger(value) && (value as number) >= 1 && (value as number) <= maximum;\n}\n\nfunction validCount(value: unknown): value is number {\n\treturn Number.isSafeInteger(value) && (value as number) >= 0 && (value as number) <= MAX_COUNT_V1;\n}\n\nfunction emptySummary(): GitRepositorySummaryV1 {\n\treturn {\n\t\tfilesChanged: 0,\n\t\tadded: 0,\n\t\tmodified: 0,\n\t\tdeleted: 0,\n\t\tuntracked: 0,\n\t\tconflicted: 0,\n\t\tadditions: 0,\n\t\tdeletions: 0,\n\t\tcommits: 0,\n\t};\n}\n\nfunction result(\n\toperationId: string,\n\tstatus: GitRepositoryResultV1[\"status\"],\n\terror: PluginErrorV1,\n): GitRepositoryResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus,\n\t\tsummary: emptySummary(),\n\t\tentries: [],\n\t\tentriesTruncated: false,\n\t\terror,\n\t};\n}\n\nfunction invalidInput(operationId: string, message: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"failed\", { version: 1, code: \"invalid_input\", message });\n}\n\nfunction unsupported(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"unsupported\", {\n\t\tversion: 1,\n\t\tcode: \"git_repository_unavailable\",\n\t\tmessage: \"No git repository adapter is bound by this host\",\n\t});\n}\n\nfunction cancelled(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"cancelled\", {\n\t\tversion: 1,\n\t\tcode: \"cancelled\",\n\t\tmessage: \"Git repository read was cancelled\",\n\t});\n}\n\nfunction timedOut(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"failed\", { version: 1, code: \"timeout\", message: \"Git repository read timed out\" });\n}\n\nfunction adapterFailed(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"failed\", {\n\t\tversion: 1,\n\t\tcode: \"adapter_failed\",\n\t\tmessage: \"Git repository adapter returned an invalid result\",\n\t});\n}\n\nfunction requestOperationId(request: GitRepositoryRequestV1): string {\n\tif (\n\t\ttypeof request.operationId === \"string\" &&\n\t\trequest.operationId.trim() !== \"\" &&\n\t\trequest.operationId.length <= GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1\n\t)\n\t\treturn request.operationId;\n\treturn \"invalid-git-repository-operation\";\n}\n\nfunction validateRequest(request: GitRepositoryRequestV1): string | undefined {\n\tif (request.version !== 1) return \"version must be 1\";\n\tif (requestOperationId(request) === \"invalid-git-repository-operation\") return \"operationId is invalid\";\n\tif (typeof request.workspaceRef !== \"string\" || !safeOpaqueRef(request.workspaceRef))\n\t\treturn \"workspaceRef must be a safe opaque host handle\";\n\tif (typeof request.operation !== \"string\" || !OPERATIONS_V1.has(request.operation as GitRepositoryOperationV1))\n\t\treturn \"operation is invalid\";\n\tif (request.limit !== undefined && !validPositiveInteger(request.limit, GIT_REPOSITORY_MAX_ENTRIES_V1))\n\t\treturn `limit must be a positive integer at most ${GIT_REPOSITORY_MAX_ENTRIES_V1}`;\n\tif (request.timeoutMs !== undefined && !validPositiveInteger(request.timeoutMs, MAX_TIMEOUT_MS_V1))\n\t\treturn `timeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`;\n\treturn undefined;\n}\n\nfunction projectSummary(value: unknown): GitRepositorySummaryV1 | undefined {\n\tconst keys = [\n\t\t\"filesChanged\",\n\t\t\"added\",\n\t\t\"modified\",\n\t\t\"deleted\",\n\t\t\"untracked\",\n\t\t\"conflicted\",\n\t\t\"additions\",\n\t\t\"deletions\",\n\t\t\"commits\",\n\t];\n\tif (!isRecord(value) || !hasOnlyKeys(value, keys) || keys.some((key) => !(key in value))) return undefined;\n\tif (keys.some((key) => !validCount(value[key]))) return undefined;\n\tconst summary = {\n\t\tfilesChanged: value.filesChanged as number,\n\t\tadded: value.added as number,\n\t\tmodified: value.modified as number,\n\t\tdeleted: value.deleted as number,\n\t\tuntracked: value.untracked as number,\n\t\tconflicted: value.conflicted as number,\n\t\tadditions: value.additions as number,\n\t\tdeletions: value.deletions as number,\n\t\tcommits: value.commits as number,\n\t};\n\tif (\n\t\tsummary.added + summary.modified + summary.deleted + summary.untracked + summary.conflicted >\n\t\tsummary.filesChanged\n\t)\n\t\treturn undefined;\n\treturn summary;\n}\n\nfunction projectFileEntry(value: Record<string, unknown>): GitRepositoryFileEntryV1 | undefined {\n\tif (!hasOnlyKeys(value, [\"kind\", \"location\", \"status\", \"additions\", \"deletions\"])) return undefined;\n\tif (\n\t\tvalue.kind !== \"file\" ||\n\t\ttypeof value.location !== \"string\" ||\n\t\t!safeRelativeLocation(value.location) ||\n\t\ttypeof value.status !== \"string\" ||\n\t\t!FILE_STATUSES_V1.has(value.status as GitRepositoryFileStatusV1) ||\n\t\t(value.additions !== undefined && !validCount(value.additions)) ||\n\t\t(value.deletions !== undefined && !validCount(value.deletions))\n\t)\n\t\treturn undefined;\n\treturn {\n\t\tkind: \"file\",\n\t\tlocation: value.location,\n\t\tstatus: value.status as GitRepositoryFileStatusV1,\n\t\t...(value.additions === undefined ? {} : { additions: value.additions as number }),\n\t\t...(value.deletions === undefined ? {} : { deletions: value.deletions as number }),\n\t};\n}\n\nfunction projectCommitEntry(value: Record<string, unknown>): GitRepositoryCommitEntryV1 | undefined {\n\tif (!hasOnlyKeys(value, [\"kind\", \"commitRef\", \"subject\"])) return undefined;\n\tif (\n\t\tvalue.kind !== \"commit\" ||\n\t\ttypeof value.commitRef !== \"string\" ||\n\t\t!COMMIT_REF_V1.test(value.commitRef) ||\n\t\ttypeof value.subject !== \"string\" ||\n\t\tvalue.subject.trim() === \"\" ||\n\t\tvalue.subject.length > MAX_HOST_SUBJECT_CHARS_V1\n\t)\n\t\treturn undefined;\n\treturn { kind: \"commit\", commitRef: value.commitRef, subject: sanitizeGitRepositoryTextV1(value.subject) };\n}\n\nfunction projectEntry(value: unknown): GitRepositoryEntryV1 | undefined {\n\tif (!isRecord(value)) return undefined;\n\treturn value.kind === \"file\"\n\t\t? projectFileEntry(value)\n\t\t: value.kind === \"commit\"\n\t\t\t? projectCommitEntry(value)\n\t\t\t: undefined;\n}\n\nfunction projectError(value: unknown): PluginErrorV1 | undefined {\n\tif (!isRecord(value) || !hasOnlyKeys(value, [\"version\", \"code\", \"message\", \"causeCode\"])) return undefined;\n\tif (\n\t\tvalue.version !== 1 ||\n\t\ttypeof value.code !== \"string\" ||\n\t\t!ERROR_CODE_V1.test(value.code) ||\n\t\ttypeof value.message !== \"string\" ||\n\t\tvalue.message.trim() === \"\" ||\n\t\tvalue.message.length > MAX_ERROR_MESSAGE_CHARS_V1 ||\n\t\t(value.causeCode !== undefined && (typeof value.causeCode !== \"string\" || !ERROR_CODE_V1.test(value.causeCode)))\n\t)\n\t\treturn undefined;\n\treturn {\n\t\tversion: 1,\n\t\tcode: value.code,\n\t\tmessage: sanitizeGitRepositoryTextV1(value.message),\n\t\t...(value.causeCode === undefined ? {} : { causeCode: value.causeCode }),\n\t};\n}\n\nfunction validTarget(value: unknown): value is GitRepositoryTargetV1 {\n\treturn (\n\t\tisRecord(value) &&\n\t\thasOnlyKeys(value, [\"repositoryRef\"]) &&\n\t\ttypeof value.repositoryRef === \"string\" &&\n\t\tsafeOpaqueRef(value.repositoryRef)\n\t);\n}\n\nfunction contextBoundResult(resultValue: GitRepositoryResultV1): GitRepositoryResultV1 | undefined {\n\tfor (let count = resultValue.entries.length; count >= 0; count--) {\n\t\tconst entries = resultValue.entries.slice(0, count);\n\t\tconst candidate: GitRepositoryResultV1 = {\n\t\t\t...resultValue,\n\t\t\tentries,\n\t\t\tentriesTruncated: resultValue.entriesTruncated || count < resultValue.entries.length,\n\t\t};\n\t\tconst projection = projectPluginContextV1(\n\t\t\t{\n\t\t\t\tversion: 1,\n\t\t\t\toperationId: candidate.operationId,\n\t\t\t\titems: [\n\t\t\t\t\tcandidate.summary as unknown as JsonValue,\n\t\t\t\t\t...entries.map((entry) => ({ ...entry }) as unknown as JsonValue),\n\t\t\t\t],\n\t\t\t\t...(candidate.reportArtifactRef === undefined ? {} : { outputArtifactRef: candidate.reportArtifactRef }),\n\t\t\t\ttruncated: candidate.entriesTruncated,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmaxOutputBytes: GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1,\n\t\t\t\tmaxInlineItems: GIT_REPOSITORY_MAX_ENTRIES_V1 + 1,\n\t\t\t\tmaxCellChars: GIT_REPOSITORY_MAX_TEXT_CHARS_V1,\n\t\t\t},\n\t\t);\n\t\tif (\n\t\t\tprojection.status === \"succeeded\" &&\n\t\t\tnew TextEncoder().encode(JSON.stringify(candidate)).byteLength <= GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1\n\t\t)\n\t\t\treturn candidate;\n\t}\n\treturn undefined;\n}\n\n/** Strictly projects arbitrary adapter output into the bounded public result DTO. */\nexport function projectGitRepositoryResultV1(value: unknown, operationId: string): GitRepositoryResultV1 | undefined {\n\tif (\n\t\t!isRecord(value) ||\n\t\t!hasOnlyKeys(value, [\n\t\t\t\"version\",\n\t\t\t\"operationId\",\n\t\t\t\"status\",\n\t\t\t\"summary\",\n\t\t\t\"entries\",\n\t\t\t\"entriesTruncated\",\n\t\t\t\"reportArtifactRef\",\n\t\t\t\"error\",\n\t\t]) ||\n\t\tvalue.version !== 1 ||\n\t\tvalue.operationId !== operationId ||\n\t\ttypeof value.status !== \"string\" ||\n\t\t!Array.isArray(value.entries) ||\n\t\tvalue.entries.length > GIT_REPOSITORY_MAX_ENTRIES_V1 ||\n\t\ttypeof value.entriesTruncated !== \"boolean\"\n\t)\n\t\treturn undefined;\n\tconst summary = projectSummary(value.summary);\n\tif (summary === undefined) return undefined;\n\tif (\n\t\tvalue.reportArtifactRef !== undefined &&\n\t\t(typeof value.reportArtifactRef !== \"string\" || !safeOpaqueRef(value.reportArtifactRef))\n\t)\n\t\treturn undefined;\n\tif (value.status === \"succeeded\") {\n\t\tif (value.error !== undefined) return undefined;\n\t\tconst entries = value.entries.map(projectEntry);\n\t\tif (entries.some((entry) => entry === undefined)) return undefined;\n\t\tconst entryValues = entries as GitRepositoryEntryV1[];\n\t\tif (\n\t\t\tentryValues.filter((entry) => entry.kind === \"file\").length > summary.filesChanged ||\n\t\t\tentryValues.filter((entry) => entry.kind === \"commit\").length > summary.commits\n\t\t)\n\t\t\treturn undefined;\n\t\treturn contextBoundResult({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary,\n\t\t\tentries: entryValues,\n\t\t\tentriesTruncated: value.entriesTruncated,\n\t\t\t...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),\n\t\t});\n\t}\n\tif (\n\t\tvalue.status !== \"failed\" &&\n\t\tvalue.status !== \"cancelled\" &&\n\t\tvalue.status !== \"needs_user\" &&\n\t\tvalue.status !== \"unsupported\"\n\t)\n\t\treturn undefined;\n\tif (\n\t\tvalue.entries.length !== 0 ||\n\t\tvalue.entriesTruncated ||\n\t\tJSON.stringify(summary) !== JSON.stringify(emptySummary())\n\t)\n\t\treturn undefined;\n\tconst error = projectError(value.error);\n\tif (error === undefined) return undefined;\n\treturn contextBoundResult({\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: value.status as GitRepositoryResultV1[\"status\"],\n\t\tsummary,\n\t\tentries: [],\n\t\tentriesTruncated: false,\n\t\t...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),\n\t\terror,\n\t});\n}\n\nfunction completeResult(operationId: string, value: GitRepositoryHostResultV1, limit: number): GitRepositoryResultV1 {\n\tif (!isRecord(value) || !hasOnlyKeys(value, [\"status\", \"summary\", \"entries\", \"reportArtifactRef\"]))\n\t\treturn adapterFailed(operationId);\n\tif (value.status === \"unsupported\" && hasOnlyKeys(value, [\"status\"])) return unsupported(operationId);\n\tif (value.status !== \"completed\" || !Array.isArray(value.entries ?? [])) return adapterFailed(operationId);\n\tconst summary = projectSummary(value.summary);\n\tconst rawEntries = value.entries ?? [];\n\tif (summary === undefined || rawEntries.length > MAX_HOST_ENTRIES_V1) return adapterFailed(operationId);\n\tconst entries = rawEntries.map(projectEntry);\n\tif (entries.some((entry) => entry === undefined)) return adapterFailed(operationId);\n\tif (\n\t\tvalue.reportArtifactRef !== undefined &&\n\t\t(typeof value.reportArtifactRef !== \"string\" || !safeOpaqueRef(value.reportArtifactRef))\n\t)\n\t\treturn adapterFailed(operationId);\n\tconst bounded = (entries as GitRepositoryEntryV1[]).slice(0, limit);\n\tif (\n\t\tbounded.filter((entry) => entry.kind === \"file\").length > summary.filesChanged ||\n\t\tbounded.filter((entry) => entry.kind === \"commit\").length > summary.commits\n\t)\n\t\treturn adapterFailed(operationId);\n\treturn (\n\t\tcontextBoundResult({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary,\n\t\t\tentries: bounded,\n\t\t\tentriesTruncated: rawEntries.length > bounded.length,\n\t\t\t...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),\n\t\t}) ?? adapterFailed(operationId)\n\t);\n}\n\nconst CANCELLATION_V1 = Symbol(\"git-repository-cancelled\");\nconst TIMEOUT_V1 = Symbol(\"git-repository-timeout\");\n\nasync function runBounded<T>(\n\trun: (signal: AbortSignal) => Promise<T>,\n\ttimeoutMs: number,\n\tsignal: AbortSignal | undefined,\n): Promise<T | typeof CANCELLATION_V1 | typeof TIMEOUT_V1 | undefined> {\n\tif (signal?.aborted) return CANCELLATION_V1;\n\tconst controller = new AbortController();\n\tlet stop: typeof CANCELLATION_V1 | typeof TIMEOUT_V1 | undefined;\n\tconst abort = () => {\n\t\tstop ??= CANCELLATION_V1;\n\t\tcontroller.abort();\n\t};\n\tsignal?.addEventListener(\"abort\", abort, { once: true });\n\tconst timeout = setTimeout(() => {\n\t\tstop ??= TIMEOUT_V1;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\tconst completed = Promise.resolve()\n\t\t.then(() => run(controller.signal))\n\t\t.then(\n\t\t\t(value) => value,\n\t\t\t() => undefined,\n\t\t);\n\tconst stopped = new Promise<typeof CANCELLATION_V1 | typeof TIMEOUT_V1>((resolve) => {\n\t\tcontroller.signal.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tif (stop !== undefined) resolve(stop);\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n\ttry {\n\t\treturn await Promise.race([completed, stopped]);\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal?.removeEventListener(\"abort\", abort);\n\t}\n}\n\n/** Creates a host-bound read-only adapter with no command, cwd, env, or raw-output input surface. */\nexport function createNodeGitRepositoryAdapterV1(\n\toptions: NodeGitRepositoryAdapterOptionsV1 = {},\n): GitRepositoryAdapterV1 {\n\tconst defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS_V1;\n\tif (!validPositiveInteger(defaultTimeoutMs, MAX_TIMEOUT_MS_V1))\n\t\tthrow new Error(`defaultTimeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`);\n\treturn {\n\t\tadapterId: GIT_REPOSITORY_HOST_ADAPTER_ID_V1,\n\t\tadapterVersion: GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1,\n\t\tasync run(request, signal) {\n\t\t\tconst operationId = requestOperationId(request);\n\t\t\tconst invalid = validateRequest(request);\n\t\t\tif (invalid !== undefined) return invalidInput(operationId, invalid);\n\t\t\tif (signal?.aborted) return cancelled(operationId);\n\t\t\tif (options.resolver === undefined || options.runner === undefined) return unsupported(operationId);\n\t\t\tconst timeoutMs = request.timeoutMs ?? defaultTimeoutMs;\n\t\t\tconst resolution = await runBounded(\n\t\t\t\t(internalSignal) => options.resolver!.resolve(request, internalSignal).then((target) => ({ target })),\n\t\t\t\ttimeoutMs,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (resolution === CANCELLATION_V1) return cancelled(operationId);\n\t\t\tif (resolution === TIMEOUT_V1) return timedOut(operationId);\n\t\t\tif (resolution === undefined) return adapterFailed(operationId);\n\t\t\tif (resolution.target === undefined) return unsupported(operationId);\n\t\t\tconst target = resolution.target;\n\t\t\tif (!validTarget(target)) return adapterFailed(operationId);\n\t\t\tif (signal?.aborted) return cancelled(operationId);\n\t\t\tconst raw = await runBounded(\n\t\t\t\t(internalSignal) =>\n\t\t\t\t\toptions.runner!.run({\n\t\t\t\t\t\trepository: target,\n\t\t\t\t\t\toperation: request.operation,\n\t\t\t\t\t\tlimit: request.limit ?? GIT_REPOSITORY_MAX_ENTRIES_V1,\n\t\t\t\t\t\tsignal: internalSignal,\n\t\t\t\t\t}),\n\t\t\t\ttimeoutMs,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (raw === CANCELLATION_V1) return cancelled(operationId);\n\t\t\tif (raw === TIMEOUT_V1) return timedOut(operationId);\n\t\t\tif (raw === undefined) return adapterFailed(operationId);\n\t\t\treturn completeResult(operationId, raw, request.limit ?? GIT_REPOSITORY_MAX_ENTRIES_V1);\n\t\t},\n\t};\n}\n\n/** Deterministic fixture seam for unit tests; it never executes a git binary. */\nexport function createDeterministicGitRepositoryAdapterV1(\n\tfixtures: readonly GitRepositoryDeterministicFixtureV1[],\n): GitRepositoryAdapterV1 {\n\tconst targets = new Map<\n\t\tstring,\n\t\t{ readonly target: GitRepositoryTargetV1; readonly result: GitRepositoryHostResultV1 }\n\t>();\n\tconst results = new Map<string, GitRepositoryHostResultV1>();\n\tfor (const [index, fixture] of fixtures.entries()) {\n\t\tconst target = { repositoryRef: `fixture:${index}` };\n\t\ttargets.set(`${fixture.workspaceRef}\\u0000${fixture.operation}`, { target, result: fixture.result });\n\t\tresults.set(target.repositoryRef, fixture.result);\n\t}\n\treturn createNodeGitRepositoryAdapterV1({\n\t\tresolver: {\n\t\t\tresolve: async (request) => targets.get(`${request.workspaceRef}\\u0000${request.operation}`)?.target,\n\t\t},\n\t\trunner: {\n\t\t\trun: async ({ repository }) => results.get(repository.repositoryRef) as GitRepositoryHostResultV1,\n\t\t},\n\t});\n}\n"]}
@@ -0,0 +1,455 @@
1
+ import { projectPluginContextV1 } from "@agent-forge/plugin-sdk";
2
+ import { sanitizeBinaryOutput } from "./shell-utils.js";
3
+ import { GIT_REPOSITORY_HOST_ADAPTER_ID_V1, GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1, } from "./types.js";
4
+ export const GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1 = 64 * 1024;
5
+ export const GIT_REPOSITORY_MAX_ENTRIES_V1 = 64;
6
+ export const GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1 = 256;
7
+ export const GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1 = 256;
8
+ export const GIT_REPOSITORY_MAX_TEXT_CHARS_V1 = 512;
9
+ const DEFAULT_TIMEOUT_MS_V1 = 15_000;
10
+ const MAX_TIMEOUT_MS_V1 = 120_000;
11
+ const MAX_HOST_ENTRIES_V1 = 4096;
12
+ const MAX_COUNT_V1 = 10_000_000;
13
+ const MAX_LOCATION_CHARS_V1 = 512;
14
+ const MAX_HOST_SUBJECT_CHARS_V1 = 8192;
15
+ const MAX_ERROR_MESSAGE_CHARS_V1 = 8192;
16
+ const OPERATIONS_V1 = new Set(["status", "diff-summary", "log-summary"]);
17
+ const FILE_STATUSES_V1 = new Set([
18
+ "added",
19
+ "modified",
20
+ "deleted",
21
+ "renamed",
22
+ "untracked",
23
+ "conflicted",
24
+ ]);
25
+ const OPAQUE_REF_V1 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
26
+ const RELATIVE_LOCATION_V1 = /^[A-Za-z0-9_][A-Za-z0-9._/-]{0,511}$/u;
27
+ const COMMIT_REF_V1 = /^[a-f0-9]{7,64}$/u;
28
+ const ERROR_CODE_V1 = /^[a-z][a-z0-9_]{0,127}$/u;
29
+ function isRecord(value) {
30
+ return value !== null && typeof value === "object" && !Array.isArray(value);
31
+ }
32
+ function hasOnlyKeys(value, allowed) {
33
+ return Object.keys(value).every((key) => allowed.includes(key));
34
+ }
35
+ function safeOpaqueRef(value) {
36
+ return value.length <= GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1 && OPAQUE_REF_V1.test(value);
37
+ }
38
+ function safeRelativeLocation(value) {
39
+ return (value.length <= MAX_LOCATION_CHARS_V1 &&
40
+ RELATIVE_LOCATION_V1.test(value) &&
41
+ !value.split("/").some((segment) => segment === "." || segment === ".."));
42
+ }
43
+ /** Removes credentials and host paths before host-provided text crosses into model context. */
44
+ export function sanitizeGitRepositoryTextV1(value, maximum = GIT_REPOSITORY_MAX_TEXT_CHARS_V1) {
45
+ const sanitized = sanitizeBinaryOutput(value)
46
+ .replace(/((?:api[-_ ]?key|access[-_ ]?token|auth(?:orization)?|bearer|password|passwd|secret|token)\s*[:=]\s*)([^\s,;]+)/giu, "$1[redacted-secret]")
47
+ .replace(/[A-Za-z]:[\\/][^\s"'<>]*/gu, "[redacted-path]")
48
+ .replace(/\\\\[^\s"'<>]*/gu, "[redacted-path]")
49
+ .replace(/(?<![:/])\/(?:[^\s"'<>/]+\/)*[^\s"'<>/]+/gu, "[redacted-path]");
50
+ const characters = [...sanitized];
51
+ return characters.length <= maximum ? sanitized : characters.slice(0, maximum).join("");
52
+ }
53
+ function validPositiveInteger(value, maximum) {
54
+ return Number.isSafeInteger(value) && value >= 1 && value <= maximum;
55
+ }
56
+ function validCount(value) {
57
+ return Number.isSafeInteger(value) && value >= 0 && value <= MAX_COUNT_V1;
58
+ }
59
+ function emptySummary() {
60
+ return {
61
+ filesChanged: 0,
62
+ added: 0,
63
+ modified: 0,
64
+ deleted: 0,
65
+ untracked: 0,
66
+ conflicted: 0,
67
+ additions: 0,
68
+ deletions: 0,
69
+ commits: 0,
70
+ };
71
+ }
72
+ function result(operationId, status, error) {
73
+ return {
74
+ version: 1,
75
+ operationId,
76
+ status,
77
+ summary: emptySummary(),
78
+ entries: [],
79
+ entriesTruncated: false,
80
+ error,
81
+ };
82
+ }
83
+ function invalidInput(operationId, message) {
84
+ return result(operationId, "failed", { version: 1, code: "invalid_input", message });
85
+ }
86
+ function unsupported(operationId) {
87
+ return result(operationId, "unsupported", {
88
+ version: 1,
89
+ code: "git_repository_unavailable",
90
+ message: "No git repository adapter is bound by this host",
91
+ });
92
+ }
93
+ function cancelled(operationId) {
94
+ return result(operationId, "cancelled", {
95
+ version: 1,
96
+ code: "cancelled",
97
+ message: "Git repository read was cancelled",
98
+ });
99
+ }
100
+ function timedOut(operationId) {
101
+ return result(operationId, "failed", { version: 1, code: "timeout", message: "Git repository read timed out" });
102
+ }
103
+ function adapterFailed(operationId) {
104
+ return result(operationId, "failed", {
105
+ version: 1,
106
+ code: "adapter_failed",
107
+ message: "Git repository adapter returned an invalid result",
108
+ });
109
+ }
110
+ function requestOperationId(request) {
111
+ if (typeof request.operationId === "string" &&
112
+ request.operationId.trim() !== "" &&
113
+ request.operationId.length <= GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1)
114
+ return request.operationId;
115
+ return "invalid-git-repository-operation";
116
+ }
117
+ function validateRequest(request) {
118
+ if (request.version !== 1)
119
+ return "version must be 1";
120
+ if (requestOperationId(request) === "invalid-git-repository-operation")
121
+ return "operationId is invalid";
122
+ if (typeof request.workspaceRef !== "string" || !safeOpaqueRef(request.workspaceRef))
123
+ return "workspaceRef must be a safe opaque host handle";
124
+ if (typeof request.operation !== "string" || !OPERATIONS_V1.has(request.operation))
125
+ return "operation is invalid";
126
+ if (request.limit !== undefined && !validPositiveInteger(request.limit, GIT_REPOSITORY_MAX_ENTRIES_V1))
127
+ return `limit must be a positive integer at most ${GIT_REPOSITORY_MAX_ENTRIES_V1}`;
128
+ if (request.timeoutMs !== undefined && !validPositiveInteger(request.timeoutMs, MAX_TIMEOUT_MS_V1))
129
+ return `timeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`;
130
+ return undefined;
131
+ }
132
+ function projectSummary(value) {
133
+ const keys = [
134
+ "filesChanged",
135
+ "added",
136
+ "modified",
137
+ "deleted",
138
+ "untracked",
139
+ "conflicted",
140
+ "additions",
141
+ "deletions",
142
+ "commits",
143
+ ];
144
+ if (!isRecord(value) || !hasOnlyKeys(value, keys) || keys.some((key) => !(key in value)))
145
+ return undefined;
146
+ if (keys.some((key) => !validCount(value[key])))
147
+ return undefined;
148
+ const summary = {
149
+ filesChanged: value.filesChanged,
150
+ added: value.added,
151
+ modified: value.modified,
152
+ deleted: value.deleted,
153
+ untracked: value.untracked,
154
+ conflicted: value.conflicted,
155
+ additions: value.additions,
156
+ deletions: value.deletions,
157
+ commits: value.commits,
158
+ };
159
+ if (summary.added + summary.modified + summary.deleted + summary.untracked + summary.conflicted >
160
+ summary.filesChanged)
161
+ return undefined;
162
+ return summary;
163
+ }
164
+ function projectFileEntry(value) {
165
+ if (!hasOnlyKeys(value, ["kind", "location", "status", "additions", "deletions"]))
166
+ return undefined;
167
+ if (value.kind !== "file" ||
168
+ typeof value.location !== "string" ||
169
+ !safeRelativeLocation(value.location) ||
170
+ typeof value.status !== "string" ||
171
+ !FILE_STATUSES_V1.has(value.status) ||
172
+ (value.additions !== undefined && !validCount(value.additions)) ||
173
+ (value.deletions !== undefined && !validCount(value.deletions)))
174
+ return undefined;
175
+ return {
176
+ kind: "file",
177
+ location: value.location,
178
+ status: value.status,
179
+ ...(value.additions === undefined ? {} : { additions: value.additions }),
180
+ ...(value.deletions === undefined ? {} : { deletions: value.deletions }),
181
+ };
182
+ }
183
+ function projectCommitEntry(value) {
184
+ if (!hasOnlyKeys(value, ["kind", "commitRef", "subject"]))
185
+ return undefined;
186
+ if (value.kind !== "commit" ||
187
+ typeof value.commitRef !== "string" ||
188
+ !COMMIT_REF_V1.test(value.commitRef) ||
189
+ typeof value.subject !== "string" ||
190
+ value.subject.trim() === "" ||
191
+ value.subject.length > MAX_HOST_SUBJECT_CHARS_V1)
192
+ return undefined;
193
+ return { kind: "commit", commitRef: value.commitRef, subject: sanitizeGitRepositoryTextV1(value.subject) };
194
+ }
195
+ function projectEntry(value) {
196
+ if (!isRecord(value))
197
+ return undefined;
198
+ return value.kind === "file"
199
+ ? projectFileEntry(value)
200
+ : value.kind === "commit"
201
+ ? projectCommitEntry(value)
202
+ : undefined;
203
+ }
204
+ function projectError(value) {
205
+ if (!isRecord(value) || !hasOnlyKeys(value, ["version", "code", "message", "causeCode"]))
206
+ return undefined;
207
+ if (value.version !== 1 ||
208
+ typeof value.code !== "string" ||
209
+ !ERROR_CODE_V1.test(value.code) ||
210
+ typeof value.message !== "string" ||
211
+ value.message.trim() === "" ||
212
+ value.message.length > MAX_ERROR_MESSAGE_CHARS_V1 ||
213
+ (value.causeCode !== undefined && (typeof value.causeCode !== "string" || !ERROR_CODE_V1.test(value.causeCode))))
214
+ return undefined;
215
+ return {
216
+ version: 1,
217
+ code: value.code,
218
+ message: sanitizeGitRepositoryTextV1(value.message),
219
+ ...(value.causeCode === undefined ? {} : { causeCode: value.causeCode }),
220
+ };
221
+ }
222
+ function validTarget(value) {
223
+ return (isRecord(value) &&
224
+ hasOnlyKeys(value, ["repositoryRef"]) &&
225
+ typeof value.repositoryRef === "string" &&
226
+ safeOpaqueRef(value.repositoryRef));
227
+ }
228
+ function contextBoundResult(resultValue) {
229
+ for (let count = resultValue.entries.length; count >= 0; count--) {
230
+ const entries = resultValue.entries.slice(0, count);
231
+ const candidate = {
232
+ ...resultValue,
233
+ entries,
234
+ entriesTruncated: resultValue.entriesTruncated || count < resultValue.entries.length,
235
+ };
236
+ const projection = projectPluginContextV1({
237
+ version: 1,
238
+ operationId: candidate.operationId,
239
+ items: [
240
+ candidate.summary,
241
+ ...entries.map((entry) => ({ ...entry })),
242
+ ],
243
+ ...(candidate.reportArtifactRef === undefined ? {} : { outputArtifactRef: candidate.reportArtifactRef }),
244
+ truncated: candidate.entriesTruncated,
245
+ }, {
246
+ maxOutputBytes: GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1,
247
+ maxInlineItems: GIT_REPOSITORY_MAX_ENTRIES_V1 + 1,
248
+ maxCellChars: GIT_REPOSITORY_MAX_TEXT_CHARS_V1,
249
+ });
250
+ if (projection.status === "succeeded" &&
251
+ new TextEncoder().encode(JSON.stringify(candidate)).byteLength <= GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1)
252
+ return candidate;
253
+ }
254
+ return undefined;
255
+ }
256
+ /** Strictly projects arbitrary adapter output into the bounded public result DTO. */
257
+ export function projectGitRepositoryResultV1(value, operationId) {
258
+ if (!isRecord(value) ||
259
+ !hasOnlyKeys(value, [
260
+ "version",
261
+ "operationId",
262
+ "status",
263
+ "summary",
264
+ "entries",
265
+ "entriesTruncated",
266
+ "reportArtifactRef",
267
+ "error",
268
+ ]) ||
269
+ value.version !== 1 ||
270
+ value.operationId !== operationId ||
271
+ typeof value.status !== "string" ||
272
+ !Array.isArray(value.entries) ||
273
+ value.entries.length > GIT_REPOSITORY_MAX_ENTRIES_V1 ||
274
+ typeof value.entriesTruncated !== "boolean")
275
+ return undefined;
276
+ const summary = projectSummary(value.summary);
277
+ if (summary === undefined)
278
+ return undefined;
279
+ if (value.reportArtifactRef !== undefined &&
280
+ (typeof value.reportArtifactRef !== "string" || !safeOpaqueRef(value.reportArtifactRef)))
281
+ return undefined;
282
+ if (value.status === "succeeded") {
283
+ if (value.error !== undefined)
284
+ return undefined;
285
+ const entries = value.entries.map(projectEntry);
286
+ if (entries.some((entry) => entry === undefined))
287
+ return undefined;
288
+ const entryValues = entries;
289
+ if (entryValues.filter((entry) => entry.kind === "file").length > summary.filesChanged ||
290
+ entryValues.filter((entry) => entry.kind === "commit").length > summary.commits)
291
+ return undefined;
292
+ return contextBoundResult({
293
+ version: 1,
294
+ operationId,
295
+ status: "succeeded",
296
+ summary,
297
+ entries: entryValues,
298
+ entriesTruncated: value.entriesTruncated,
299
+ ...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),
300
+ });
301
+ }
302
+ if (value.status !== "failed" &&
303
+ value.status !== "cancelled" &&
304
+ value.status !== "needs_user" &&
305
+ value.status !== "unsupported")
306
+ return undefined;
307
+ if (value.entries.length !== 0 ||
308
+ value.entriesTruncated ||
309
+ JSON.stringify(summary) !== JSON.stringify(emptySummary()))
310
+ return undefined;
311
+ const error = projectError(value.error);
312
+ if (error === undefined)
313
+ return undefined;
314
+ return contextBoundResult({
315
+ version: 1,
316
+ operationId,
317
+ status: value.status,
318
+ summary,
319
+ entries: [],
320
+ entriesTruncated: false,
321
+ ...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),
322
+ error,
323
+ });
324
+ }
325
+ function completeResult(operationId, value, limit) {
326
+ if (!isRecord(value) || !hasOnlyKeys(value, ["status", "summary", "entries", "reportArtifactRef"]))
327
+ return adapterFailed(operationId);
328
+ if (value.status === "unsupported" && hasOnlyKeys(value, ["status"]))
329
+ return unsupported(operationId);
330
+ if (value.status !== "completed" || !Array.isArray(value.entries ?? []))
331
+ return adapterFailed(operationId);
332
+ const summary = projectSummary(value.summary);
333
+ const rawEntries = value.entries ?? [];
334
+ if (summary === undefined || rawEntries.length > MAX_HOST_ENTRIES_V1)
335
+ return adapterFailed(operationId);
336
+ const entries = rawEntries.map(projectEntry);
337
+ if (entries.some((entry) => entry === undefined))
338
+ return adapterFailed(operationId);
339
+ if (value.reportArtifactRef !== undefined &&
340
+ (typeof value.reportArtifactRef !== "string" || !safeOpaqueRef(value.reportArtifactRef)))
341
+ return adapterFailed(operationId);
342
+ const bounded = entries.slice(0, limit);
343
+ if (bounded.filter((entry) => entry.kind === "file").length > summary.filesChanged ||
344
+ bounded.filter((entry) => entry.kind === "commit").length > summary.commits)
345
+ return adapterFailed(operationId);
346
+ return (contextBoundResult({
347
+ version: 1,
348
+ operationId,
349
+ status: "succeeded",
350
+ summary,
351
+ entries: bounded,
352
+ entriesTruncated: rawEntries.length > bounded.length,
353
+ ...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),
354
+ }) ?? adapterFailed(operationId));
355
+ }
356
+ const CANCELLATION_V1 = Symbol("git-repository-cancelled");
357
+ const TIMEOUT_V1 = Symbol("git-repository-timeout");
358
+ async function runBounded(run, timeoutMs, signal) {
359
+ if (signal?.aborted)
360
+ return CANCELLATION_V1;
361
+ const controller = new AbortController();
362
+ let stop;
363
+ const abort = () => {
364
+ stop ??= CANCELLATION_V1;
365
+ controller.abort();
366
+ };
367
+ signal?.addEventListener("abort", abort, { once: true });
368
+ const timeout = setTimeout(() => {
369
+ stop ??= TIMEOUT_V1;
370
+ controller.abort();
371
+ }, timeoutMs);
372
+ const completed = Promise.resolve()
373
+ .then(() => run(controller.signal))
374
+ .then((value) => value, () => undefined);
375
+ const stopped = new Promise((resolve) => {
376
+ controller.signal.addEventListener("abort", () => {
377
+ if (stop !== undefined)
378
+ resolve(stop);
379
+ }, { once: true });
380
+ });
381
+ try {
382
+ return await Promise.race([completed, stopped]);
383
+ }
384
+ finally {
385
+ clearTimeout(timeout);
386
+ signal?.removeEventListener("abort", abort);
387
+ }
388
+ }
389
+ /** Creates a host-bound read-only adapter with no command, cwd, env, or raw-output input surface. */
390
+ export function createNodeGitRepositoryAdapterV1(options = {}) {
391
+ const defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS_V1;
392
+ if (!validPositiveInteger(defaultTimeoutMs, MAX_TIMEOUT_MS_V1))
393
+ throw new Error(`defaultTimeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`);
394
+ return {
395
+ adapterId: GIT_REPOSITORY_HOST_ADAPTER_ID_V1,
396
+ adapterVersion: GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1,
397
+ async run(request, signal) {
398
+ const operationId = requestOperationId(request);
399
+ const invalid = validateRequest(request);
400
+ if (invalid !== undefined)
401
+ return invalidInput(operationId, invalid);
402
+ if (signal?.aborted)
403
+ return cancelled(operationId);
404
+ if (options.resolver === undefined || options.runner === undefined)
405
+ return unsupported(operationId);
406
+ const timeoutMs = request.timeoutMs ?? defaultTimeoutMs;
407
+ const resolution = await runBounded((internalSignal) => options.resolver.resolve(request, internalSignal).then((target) => ({ target })), timeoutMs, signal);
408
+ if (resolution === CANCELLATION_V1)
409
+ return cancelled(operationId);
410
+ if (resolution === TIMEOUT_V1)
411
+ return timedOut(operationId);
412
+ if (resolution === undefined)
413
+ return adapterFailed(operationId);
414
+ if (resolution.target === undefined)
415
+ return unsupported(operationId);
416
+ const target = resolution.target;
417
+ if (!validTarget(target))
418
+ return adapterFailed(operationId);
419
+ if (signal?.aborted)
420
+ return cancelled(operationId);
421
+ const raw = await runBounded((internalSignal) => options.runner.run({
422
+ repository: target,
423
+ operation: request.operation,
424
+ limit: request.limit ?? GIT_REPOSITORY_MAX_ENTRIES_V1,
425
+ signal: internalSignal,
426
+ }), timeoutMs, signal);
427
+ if (raw === CANCELLATION_V1)
428
+ return cancelled(operationId);
429
+ if (raw === TIMEOUT_V1)
430
+ return timedOut(operationId);
431
+ if (raw === undefined)
432
+ return adapterFailed(operationId);
433
+ return completeResult(operationId, raw, request.limit ?? GIT_REPOSITORY_MAX_ENTRIES_V1);
434
+ },
435
+ };
436
+ }
437
+ /** Deterministic fixture seam for unit tests; it never executes a git binary. */
438
+ export function createDeterministicGitRepositoryAdapterV1(fixtures) {
439
+ const targets = new Map();
440
+ const results = new Map();
441
+ for (const [index, fixture] of fixtures.entries()) {
442
+ const target = { repositoryRef: `fixture:${index}` };
443
+ targets.set(`${fixture.workspaceRef}\u0000${fixture.operation}`, { target, result: fixture.result });
444
+ results.set(target.repositoryRef, fixture.result);
445
+ }
446
+ return createNodeGitRepositoryAdapterV1({
447
+ resolver: {
448
+ resolve: async (request) => targets.get(`${request.workspaceRef}\u0000${request.operation}`)?.target,
449
+ },
450
+ runner: {
451
+ run: async ({ repository }) => results.get(repository.repositoryRef),
452
+ },
453
+ });
454
+ }
455
+ //# sourceMappingURL=node-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-adapter.js","sourceRoot":"","sources":["../src/node-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonValue, PluginErrorV1 } from \"@agent-forge/plugin-sdk\";\nimport { projectPluginContextV1 } from \"@agent-forge/plugin-sdk\";\nimport { sanitizeBinaryOutput } from \"./shell-utils.ts\";\nimport {\n\tGIT_REPOSITORY_HOST_ADAPTER_ID_V1,\n\tGIT_REPOSITORY_HOST_ADAPTER_VERSION_V1,\n\ttype GitRepositoryAdapterV1,\n\ttype GitRepositoryCommitEntryV1,\n\ttype GitRepositoryDeterministicFixtureV1,\n\ttype GitRepositoryEntryV1,\n\ttype GitRepositoryFileEntryV1,\n\ttype GitRepositoryFileStatusV1,\n\ttype GitRepositoryHostResultV1,\n\ttype GitRepositoryOperationV1,\n\ttype GitRepositoryRequestV1,\n\ttype GitRepositoryResultV1,\n\ttype GitRepositorySummaryV1,\n\ttype GitRepositoryTargetV1,\n\ttype NodeGitRepositoryAdapterOptionsV1,\n} from \"./types.ts\";\n\nexport const GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1 = 64 * 1024;\nexport const GIT_REPOSITORY_MAX_ENTRIES_V1 = 64;\nexport const GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1 = 256;\nexport const GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1 = 256;\nexport const GIT_REPOSITORY_MAX_TEXT_CHARS_V1 = 512;\n\nconst DEFAULT_TIMEOUT_MS_V1 = 15_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst MAX_HOST_ENTRIES_V1 = 4096;\nconst MAX_COUNT_V1 = 10_000_000;\nconst MAX_LOCATION_CHARS_V1 = 512;\nconst MAX_HOST_SUBJECT_CHARS_V1 = 8192;\nconst MAX_ERROR_MESSAGE_CHARS_V1 = 8192;\nconst OPERATIONS_V1 = new Set<GitRepositoryOperationV1>([\"status\", \"diff-summary\", \"log-summary\"]);\nconst FILE_STATUSES_V1 = new Set<GitRepositoryFileStatusV1>([\n\t\"added\",\n\t\"modified\",\n\t\"deleted\",\n\t\"renamed\",\n\t\"untracked\",\n\t\"conflicted\",\n]);\nconst OPAQUE_REF_V1 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;\nconst RELATIVE_LOCATION_V1 = /^[A-Za-z0-9_][A-Za-z0-9._/-]{0,511}$/u;\nconst COMMIT_REF_V1 = /^[a-f0-9]{7,64}$/u;\nconst ERROR_CODE_V1 = /^[a-z][a-z0-9_]{0,127}$/u;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction hasOnlyKeys(value: Record<string, unknown>, allowed: readonly string[]): boolean {\n\treturn Object.keys(value).every((key) => allowed.includes(key));\n}\n\nfunction safeOpaqueRef(value: string): boolean {\n\treturn value.length <= GIT_REPOSITORY_MAX_OPAQUE_REF_CHARS_V1 && OPAQUE_REF_V1.test(value);\n}\n\nfunction safeRelativeLocation(value: string): boolean {\n\treturn (\n\t\tvalue.length <= MAX_LOCATION_CHARS_V1 &&\n\t\tRELATIVE_LOCATION_V1.test(value) &&\n\t\t!value.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n\t);\n}\n\n/** Removes credentials and host paths before host-provided text crosses into model context. */\nexport function sanitizeGitRepositoryTextV1(value: string, maximum = GIT_REPOSITORY_MAX_TEXT_CHARS_V1): string {\n\tconst sanitized = sanitizeBinaryOutput(value)\n\t\t.replace(\n\t\t\t/((?:api[-_ ]?key|access[-_ ]?token|auth(?:orization)?|bearer|password|passwd|secret|token)\\s*[:=]\\s*)([^\\s,;]+)/giu,\n\t\t\t\"$1[redacted-secret]\",\n\t\t)\n\t\t.replace(/[A-Za-z]:[\\\\/][^\\s\"'<>]*/gu, \"[redacted-path]\")\n\t\t.replace(/\\\\\\\\[^\\s\"'<>]*/gu, \"[redacted-path]\")\n\t\t.replace(/(?<![:/])\\/(?:[^\\s\"'<>/]+\\/)*[^\\s\"'<>/]+/gu, \"[redacted-path]\");\n\tconst characters = [...sanitized];\n\treturn characters.length <= maximum ? sanitized : characters.slice(0, maximum).join(\"\");\n}\n\nfunction validPositiveInteger(value: unknown, maximum: number): value is number {\n\treturn Number.isSafeInteger(value) && (value as number) >= 1 && (value as number) <= maximum;\n}\n\nfunction validCount(value: unknown): value is number {\n\treturn Number.isSafeInteger(value) && (value as number) >= 0 && (value as number) <= MAX_COUNT_V1;\n}\n\nfunction emptySummary(): GitRepositorySummaryV1 {\n\treturn {\n\t\tfilesChanged: 0,\n\t\tadded: 0,\n\t\tmodified: 0,\n\t\tdeleted: 0,\n\t\tuntracked: 0,\n\t\tconflicted: 0,\n\t\tadditions: 0,\n\t\tdeletions: 0,\n\t\tcommits: 0,\n\t};\n}\n\nfunction result(\n\toperationId: string,\n\tstatus: GitRepositoryResultV1[\"status\"],\n\terror: PluginErrorV1,\n): GitRepositoryResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus,\n\t\tsummary: emptySummary(),\n\t\tentries: [],\n\t\tentriesTruncated: false,\n\t\terror,\n\t};\n}\n\nfunction invalidInput(operationId: string, message: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"failed\", { version: 1, code: \"invalid_input\", message });\n}\n\nfunction unsupported(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"unsupported\", {\n\t\tversion: 1,\n\t\tcode: \"git_repository_unavailable\",\n\t\tmessage: \"No git repository adapter is bound by this host\",\n\t});\n}\n\nfunction cancelled(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"cancelled\", {\n\t\tversion: 1,\n\t\tcode: \"cancelled\",\n\t\tmessage: \"Git repository read was cancelled\",\n\t});\n}\n\nfunction timedOut(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"failed\", { version: 1, code: \"timeout\", message: \"Git repository read timed out\" });\n}\n\nfunction adapterFailed(operationId: string): GitRepositoryResultV1 {\n\treturn result(operationId, \"failed\", {\n\t\tversion: 1,\n\t\tcode: \"adapter_failed\",\n\t\tmessage: \"Git repository adapter returned an invalid result\",\n\t});\n}\n\nfunction requestOperationId(request: GitRepositoryRequestV1): string {\n\tif (\n\t\ttypeof request.operationId === \"string\" &&\n\t\trequest.operationId.trim() !== \"\" &&\n\t\trequest.operationId.length <= GIT_REPOSITORY_MAX_OPERATION_ID_CHARS_V1\n\t)\n\t\treturn request.operationId;\n\treturn \"invalid-git-repository-operation\";\n}\n\nfunction validateRequest(request: GitRepositoryRequestV1): string | undefined {\n\tif (request.version !== 1) return \"version must be 1\";\n\tif (requestOperationId(request) === \"invalid-git-repository-operation\") return \"operationId is invalid\";\n\tif (typeof request.workspaceRef !== \"string\" || !safeOpaqueRef(request.workspaceRef))\n\t\treturn \"workspaceRef must be a safe opaque host handle\";\n\tif (typeof request.operation !== \"string\" || !OPERATIONS_V1.has(request.operation as GitRepositoryOperationV1))\n\t\treturn \"operation is invalid\";\n\tif (request.limit !== undefined && !validPositiveInteger(request.limit, GIT_REPOSITORY_MAX_ENTRIES_V1))\n\t\treturn `limit must be a positive integer at most ${GIT_REPOSITORY_MAX_ENTRIES_V1}`;\n\tif (request.timeoutMs !== undefined && !validPositiveInteger(request.timeoutMs, MAX_TIMEOUT_MS_V1))\n\t\treturn `timeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`;\n\treturn undefined;\n}\n\nfunction projectSummary(value: unknown): GitRepositorySummaryV1 | undefined {\n\tconst keys = [\n\t\t\"filesChanged\",\n\t\t\"added\",\n\t\t\"modified\",\n\t\t\"deleted\",\n\t\t\"untracked\",\n\t\t\"conflicted\",\n\t\t\"additions\",\n\t\t\"deletions\",\n\t\t\"commits\",\n\t];\n\tif (!isRecord(value) || !hasOnlyKeys(value, keys) || keys.some((key) => !(key in value))) return undefined;\n\tif (keys.some((key) => !validCount(value[key]))) return undefined;\n\tconst summary = {\n\t\tfilesChanged: value.filesChanged as number,\n\t\tadded: value.added as number,\n\t\tmodified: value.modified as number,\n\t\tdeleted: value.deleted as number,\n\t\tuntracked: value.untracked as number,\n\t\tconflicted: value.conflicted as number,\n\t\tadditions: value.additions as number,\n\t\tdeletions: value.deletions as number,\n\t\tcommits: value.commits as number,\n\t};\n\tif (\n\t\tsummary.added + summary.modified + summary.deleted + summary.untracked + summary.conflicted >\n\t\tsummary.filesChanged\n\t)\n\t\treturn undefined;\n\treturn summary;\n}\n\nfunction projectFileEntry(value: Record<string, unknown>): GitRepositoryFileEntryV1 | undefined {\n\tif (!hasOnlyKeys(value, [\"kind\", \"location\", \"status\", \"additions\", \"deletions\"])) return undefined;\n\tif (\n\t\tvalue.kind !== \"file\" ||\n\t\ttypeof value.location !== \"string\" ||\n\t\t!safeRelativeLocation(value.location) ||\n\t\ttypeof value.status !== \"string\" ||\n\t\t!FILE_STATUSES_V1.has(value.status as GitRepositoryFileStatusV1) ||\n\t\t(value.additions !== undefined && !validCount(value.additions)) ||\n\t\t(value.deletions !== undefined && !validCount(value.deletions))\n\t)\n\t\treturn undefined;\n\treturn {\n\t\tkind: \"file\",\n\t\tlocation: value.location,\n\t\tstatus: value.status as GitRepositoryFileStatusV1,\n\t\t...(value.additions === undefined ? {} : { additions: value.additions as number }),\n\t\t...(value.deletions === undefined ? {} : { deletions: value.deletions as number }),\n\t};\n}\n\nfunction projectCommitEntry(value: Record<string, unknown>): GitRepositoryCommitEntryV1 | undefined {\n\tif (!hasOnlyKeys(value, [\"kind\", \"commitRef\", \"subject\"])) return undefined;\n\tif (\n\t\tvalue.kind !== \"commit\" ||\n\t\ttypeof value.commitRef !== \"string\" ||\n\t\t!COMMIT_REF_V1.test(value.commitRef) ||\n\t\ttypeof value.subject !== \"string\" ||\n\t\tvalue.subject.trim() === \"\" ||\n\t\tvalue.subject.length > MAX_HOST_SUBJECT_CHARS_V1\n\t)\n\t\treturn undefined;\n\treturn { kind: \"commit\", commitRef: value.commitRef, subject: sanitizeGitRepositoryTextV1(value.subject) };\n}\n\nfunction projectEntry(value: unknown): GitRepositoryEntryV1 | undefined {\n\tif (!isRecord(value)) return undefined;\n\treturn value.kind === \"file\"\n\t\t? projectFileEntry(value)\n\t\t: value.kind === \"commit\"\n\t\t\t? projectCommitEntry(value)\n\t\t\t: undefined;\n}\n\nfunction projectError(value: unknown): PluginErrorV1 | undefined {\n\tif (!isRecord(value) || !hasOnlyKeys(value, [\"version\", \"code\", \"message\", \"causeCode\"])) return undefined;\n\tif (\n\t\tvalue.version !== 1 ||\n\t\ttypeof value.code !== \"string\" ||\n\t\t!ERROR_CODE_V1.test(value.code) ||\n\t\ttypeof value.message !== \"string\" ||\n\t\tvalue.message.trim() === \"\" ||\n\t\tvalue.message.length > MAX_ERROR_MESSAGE_CHARS_V1 ||\n\t\t(value.causeCode !== undefined && (typeof value.causeCode !== \"string\" || !ERROR_CODE_V1.test(value.causeCode)))\n\t)\n\t\treturn undefined;\n\treturn {\n\t\tversion: 1,\n\t\tcode: value.code,\n\t\tmessage: sanitizeGitRepositoryTextV1(value.message),\n\t\t...(value.causeCode === undefined ? {} : { causeCode: value.causeCode }),\n\t};\n}\n\nfunction validTarget(value: unknown): value is GitRepositoryTargetV1 {\n\treturn (\n\t\tisRecord(value) &&\n\t\thasOnlyKeys(value, [\"repositoryRef\"]) &&\n\t\ttypeof value.repositoryRef === \"string\" &&\n\t\tsafeOpaqueRef(value.repositoryRef)\n\t);\n}\n\nfunction contextBoundResult(resultValue: GitRepositoryResultV1): GitRepositoryResultV1 | undefined {\n\tfor (let count = resultValue.entries.length; count >= 0; count--) {\n\t\tconst entries = resultValue.entries.slice(0, count);\n\t\tconst candidate: GitRepositoryResultV1 = {\n\t\t\t...resultValue,\n\t\t\tentries,\n\t\t\tentriesTruncated: resultValue.entriesTruncated || count < resultValue.entries.length,\n\t\t};\n\t\tconst projection = projectPluginContextV1(\n\t\t\t{\n\t\t\t\tversion: 1,\n\t\t\t\toperationId: candidate.operationId,\n\t\t\t\titems: [\n\t\t\t\t\tcandidate.summary as unknown as JsonValue,\n\t\t\t\t\t...entries.map((entry) => ({ ...entry }) as unknown as JsonValue),\n\t\t\t\t],\n\t\t\t\t...(candidate.reportArtifactRef === undefined ? {} : { outputArtifactRef: candidate.reportArtifactRef }),\n\t\t\t\ttruncated: candidate.entriesTruncated,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmaxOutputBytes: GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1,\n\t\t\t\tmaxInlineItems: GIT_REPOSITORY_MAX_ENTRIES_V1 + 1,\n\t\t\t\tmaxCellChars: GIT_REPOSITORY_MAX_TEXT_CHARS_V1,\n\t\t\t},\n\t\t);\n\t\tif (\n\t\t\tprojection.status === \"succeeded\" &&\n\t\t\tnew TextEncoder().encode(JSON.stringify(candidate)).byteLength <= GIT_REPOSITORY_MAX_OUTPUT_BYTES_V1\n\t\t)\n\t\t\treturn candidate;\n\t}\n\treturn undefined;\n}\n\n/** Strictly projects arbitrary adapter output into the bounded public result DTO. */\nexport function projectGitRepositoryResultV1(value: unknown, operationId: string): GitRepositoryResultV1 | undefined {\n\tif (\n\t\t!isRecord(value) ||\n\t\t!hasOnlyKeys(value, [\n\t\t\t\"version\",\n\t\t\t\"operationId\",\n\t\t\t\"status\",\n\t\t\t\"summary\",\n\t\t\t\"entries\",\n\t\t\t\"entriesTruncated\",\n\t\t\t\"reportArtifactRef\",\n\t\t\t\"error\",\n\t\t]) ||\n\t\tvalue.version !== 1 ||\n\t\tvalue.operationId !== operationId ||\n\t\ttypeof value.status !== \"string\" ||\n\t\t!Array.isArray(value.entries) ||\n\t\tvalue.entries.length > GIT_REPOSITORY_MAX_ENTRIES_V1 ||\n\t\ttypeof value.entriesTruncated !== \"boolean\"\n\t)\n\t\treturn undefined;\n\tconst summary = projectSummary(value.summary);\n\tif (summary === undefined) return undefined;\n\tif (\n\t\tvalue.reportArtifactRef !== undefined &&\n\t\t(typeof value.reportArtifactRef !== \"string\" || !safeOpaqueRef(value.reportArtifactRef))\n\t)\n\t\treturn undefined;\n\tif (value.status === \"succeeded\") {\n\t\tif (value.error !== undefined) return undefined;\n\t\tconst entries = value.entries.map(projectEntry);\n\t\tif (entries.some((entry) => entry === undefined)) return undefined;\n\t\tconst entryValues = entries as GitRepositoryEntryV1[];\n\t\tif (\n\t\t\tentryValues.filter((entry) => entry.kind === \"file\").length > summary.filesChanged ||\n\t\t\tentryValues.filter((entry) => entry.kind === \"commit\").length > summary.commits\n\t\t)\n\t\t\treturn undefined;\n\t\treturn contextBoundResult({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary,\n\t\t\tentries: entryValues,\n\t\t\tentriesTruncated: value.entriesTruncated,\n\t\t\t...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),\n\t\t});\n\t}\n\tif (\n\t\tvalue.status !== \"failed\" &&\n\t\tvalue.status !== \"cancelled\" &&\n\t\tvalue.status !== \"needs_user\" &&\n\t\tvalue.status !== \"unsupported\"\n\t)\n\t\treturn undefined;\n\tif (\n\t\tvalue.entries.length !== 0 ||\n\t\tvalue.entriesTruncated ||\n\t\tJSON.stringify(summary) !== JSON.stringify(emptySummary())\n\t)\n\t\treturn undefined;\n\tconst error = projectError(value.error);\n\tif (error === undefined) return undefined;\n\treturn contextBoundResult({\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: value.status as GitRepositoryResultV1[\"status\"],\n\t\tsummary,\n\t\tentries: [],\n\t\tentriesTruncated: false,\n\t\t...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),\n\t\terror,\n\t});\n}\n\nfunction completeResult(operationId: string, value: GitRepositoryHostResultV1, limit: number): GitRepositoryResultV1 {\n\tif (!isRecord(value) || !hasOnlyKeys(value, [\"status\", \"summary\", \"entries\", \"reportArtifactRef\"]))\n\t\treturn adapterFailed(operationId);\n\tif (value.status === \"unsupported\" && hasOnlyKeys(value, [\"status\"])) return unsupported(operationId);\n\tif (value.status !== \"completed\" || !Array.isArray(value.entries ?? [])) return adapterFailed(operationId);\n\tconst summary = projectSummary(value.summary);\n\tconst rawEntries = value.entries ?? [];\n\tif (summary === undefined || rawEntries.length > MAX_HOST_ENTRIES_V1) return adapterFailed(operationId);\n\tconst entries = rawEntries.map(projectEntry);\n\tif (entries.some((entry) => entry === undefined)) return adapterFailed(operationId);\n\tif (\n\t\tvalue.reportArtifactRef !== undefined &&\n\t\t(typeof value.reportArtifactRef !== \"string\" || !safeOpaqueRef(value.reportArtifactRef))\n\t)\n\t\treturn adapterFailed(operationId);\n\tconst bounded = (entries as GitRepositoryEntryV1[]).slice(0, limit);\n\tif (\n\t\tbounded.filter((entry) => entry.kind === \"file\").length > summary.filesChanged ||\n\t\tbounded.filter((entry) => entry.kind === \"commit\").length > summary.commits\n\t)\n\t\treturn adapterFailed(operationId);\n\treturn (\n\t\tcontextBoundResult({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary,\n\t\t\tentries: bounded,\n\t\t\tentriesTruncated: rawEntries.length > bounded.length,\n\t\t\t...(value.reportArtifactRef === undefined ? {} : { reportArtifactRef: value.reportArtifactRef }),\n\t\t}) ?? adapterFailed(operationId)\n\t);\n}\n\nconst CANCELLATION_V1 = Symbol(\"git-repository-cancelled\");\nconst TIMEOUT_V1 = Symbol(\"git-repository-timeout\");\n\nasync function runBounded<T>(\n\trun: (signal: AbortSignal) => Promise<T>,\n\ttimeoutMs: number,\n\tsignal: AbortSignal | undefined,\n): Promise<T | typeof CANCELLATION_V1 | typeof TIMEOUT_V1 | undefined> {\n\tif (signal?.aborted) return CANCELLATION_V1;\n\tconst controller = new AbortController();\n\tlet stop: typeof CANCELLATION_V1 | typeof TIMEOUT_V1 | undefined;\n\tconst abort = () => {\n\t\tstop ??= CANCELLATION_V1;\n\t\tcontroller.abort();\n\t};\n\tsignal?.addEventListener(\"abort\", abort, { once: true });\n\tconst timeout = setTimeout(() => {\n\t\tstop ??= TIMEOUT_V1;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\tconst completed = Promise.resolve()\n\t\t.then(() => run(controller.signal))\n\t\t.then(\n\t\t\t(value) => value,\n\t\t\t() => undefined,\n\t\t);\n\tconst stopped = new Promise<typeof CANCELLATION_V1 | typeof TIMEOUT_V1>((resolve) => {\n\t\tcontroller.signal.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tif (stop !== undefined) resolve(stop);\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n\ttry {\n\t\treturn await Promise.race([completed, stopped]);\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal?.removeEventListener(\"abort\", abort);\n\t}\n}\n\n/** Creates a host-bound read-only adapter with no command, cwd, env, or raw-output input surface. */\nexport function createNodeGitRepositoryAdapterV1(\n\toptions: NodeGitRepositoryAdapterOptionsV1 = {},\n): GitRepositoryAdapterV1 {\n\tconst defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS_V1;\n\tif (!validPositiveInteger(defaultTimeoutMs, MAX_TIMEOUT_MS_V1))\n\t\tthrow new Error(`defaultTimeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`);\n\treturn {\n\t\tadapterId: GIT_REPOSITORY_HOST_ADAPTER_ID_V1,\n\t\tadapterVersion: GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1,\n\t\tasync run(request, signal) {\n\t\t\tconst operationId = requestOperationId(request);\n\t\t\tconst invalid = validateRequest(request);\n\t\t\tif (invalid !== undefined) return invalidInput(operationId, invalid);\n\t\t\tif (signal?.aborted) return cancelled(operationId);\n\t\t\tif (options.resolver === undefined || options.runner === undefined) return unsupported(operationId);\n\t\t\tconst timeoutMs = request.timeoutMs ?? defaultTimeoutMs;\n\t\t\tconst resolution = await runBounded(\n\t\t\t\t(internalSignal) => options.resolver!.resolve(request, internalSignal).then((target) => ({ target })),\n\t\t\t\ttimeoutMs,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (resolution === CANCELLATION_V1) return cancelled(operationId);\n\t\t\tif (resolution === TIMEOUT_V1) return timedOut(operationId);\n\t\t\tif (resolution === undefined) return adapterFailed(operationId);\n\t\t\tif (resolution.target === undefined) return unsupported(operationId);\n\t\t\tconst target = resolution.target;\n\t\t\tif (!validTarget(target)) return adapterFailed(operationId);\n\t\t\tif (signal?.aborted) return cancelled(operationId);\n\t\t\tconst raw = await runBounded(\n\t\t\t\t(internalSignal) =>\n\t\t\t\t\toptions.runner!.run({\n\t\t\t\t\t\trepository: target,\n\t\t\t\t\t\toperation: request.operation,\n\t\t\t\t\t\tlimit: request.limit ?? GIT_REPOSITORY_MAX_ENTRIES_V1,\n\t\t\t\t\t\tsignal: internalSignal,\n\t\t\t\t\t}),\n\t\t\t\ttimeoutMs,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (raw === CANCELLATION_V1) return cancelled(operationId);\n\t\t\tif (raw === TIMEOUT_V1) return timedOut(operationId);\n\t\t\tif (raw === undefined) return adapterFailed(operationId);\n\t\t\treturn completeResult(operationId, raw, request.limit ?? GIT_REPOSITORY_MAX_ENTRIES_V1);\n\t\t},\n\t};\n}\n\n/** Deterministic fixture seam for unit tests; it never executes a git binary. */\nexport function createDeterministicGitRepositoryAdapterV1(\n\tfixtures: readonly GitRepositoryDeterministicFixtureV1[],\n): GitRepositoryAdapterV1 {\n\tconst targets = new Map<\n\t\tstring,\n\t\t{ readonly target: GitRepositoryTargetV1; readonly result: GitRepositoryHostResultV1 }\n\t>();\n\tconst results = new Map<string, GitRepositoryHostResultV1>();\n\tfor (const [index, fixture] of fixtures.entries()) {\n\t\tconst target = { repositoryRef: `fixture:${index}` };\n\t\ttargets.set(`${fixture.workspaceRef}\\u0000${fixture.operation}`, { target, result: fixture.result });\n\t\tresults.set(target.repositoryRef, fixture.result);\n\t}\n\treturn createNodeGitRepositoryAdapterV1({\n\t\tresolver: {\n\t\t\tresolve: async (request) => targets.get(`${request.workspaceRef}\\u0000${request.operation}`)?.target,\n\t\t},\n\t\trunner: {\n\t\t\trun: async ({ repository }) => results.get(repository.repositoryRef) as GitRepositoryHostResultV1,\n\t\t},\n\t});\n}\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Inlined from agent-forge `src/utils/shell.ts` (D-075 S3 extraction) so this
3
+ * package keeps its dependency closure limited to the plugin SDK and typebox.
4
+ * Semantics are unchanged.
5
+ */
6
+ /**
7
+ * Sanitize binary output for display/storage.
8
+ * Removes characters that crash string-width or cause display issues:
9
+ * - Control characters (except tab, newline, carriage return)
10
+ * - Lone surrogates
11
+ * - Unicode Format characters (crash string-width due to a bug)
12
+ * - Characters with undefined code points
13
+ */
14
+ export declare function sanitizeBinaryOutput(str: string): string;
15
+ //# sourceMappingURL=shell-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-utils.d.ts","sourceRoot":"","sources":["../src/shell-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA8BxD","sourcesContent":["/**\n * Inlined from agent-forge `src/utils/shell.ts` (D-075 S3 extraction) so this\n * package keeps its dependency closure limited to the plugin SDK and typebox.\n * Semantics are unchanged.\n */\n\n/**\n * Sanitize binary output for display/storage.\n * Removes characters that crash string-width or cause display issues:\n * - Control characters (except tab, newline, carriage return)\n * - Lone surrogates\n * - Unicode Format characters (crash string-width due to a bug)\n * - Characters with undefined code points\n */\nexport function sanitizeBinaryOutput(str: string): string {\n\t// Use Array.from to properly iterate over code points (not code units)\n\t// This handles surrogate pairs correctly and catches edge cases where\n\t// codePointAt() might return undefined\n\treturn Array.from(str)\n\t\t.filter((char) => {\n\t\t\t// Filter out characters that cause string-width to crash\n\t\t\t// This includes:\n\t\t\t// - Unicode format characters\n\t\t\t// - Lone surrogates (already filtered by Array.from)\n\t\t\t// - Control chars except \\t \\n \\r\n\t\t\t// - Characters with undefined code points\n\n\t\t\tconst code = char.codePointAt(0);\n\n\t\t\t// v8 ignore next -- Array.from iterating over a legal string, codePointAt will not return undefined\n\t\t\tif (code === undefined) return false;\n\n\t\t\t// Allow tab, newline, carriage return\n\t\t\tif (code === 0x09 || code === 0x0a || code === 0x0d) return true;\n\n\t\t\t// Filter out control characters (0x00-0x1F, except 0x09, 0x0a, 0x0d)\n\t\t\tif (code <= 0x1f) return false;\n\n\t\t\t// Filter out Unicode format characters\n\t\t\tif (code >= 0xfff9 && code <= 0xfffb) return false;\n\n\t\t\treturn true;\n\t\t})\n\t\t.join(\"\");\n}\n"]}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Inlined from agent-forge `src/utils/shell.ts` (D-075 S3 extraction) so this
3
+ * package keeps its dependency closure limited to the plugin SDK and typebox.
4
+ * Semantics are unchanged.
5
+ */
6
+ /**
7
+ * Sanitize binary output for display/storage.
8
+ * Removes characters that crash string-width or cause display issues:
9
+ * - Control characters (except tab, newline, carriage return)
10
+ * - Lone surrogates
11
+ * - Unicode Format characters (crash string-width due to a bug)
12
+ * - Characters with undefined code points
13
+ */
14
+ export function sanitizeBinaryOutput(str) {
15
+ // Use Array.from to properly iterate over code points (not code units)
16
+ // This handles surrogate pairs correctly and catches edge cases where
17
+ // codePointAt() might return undefined
18
+ return Array.from(str)
19
+ .filter((char) => {
20
+ // Filter out characters that cause string-width to crash
21
+ // This includes:
22
+ // - Unicode format characters
23
+ // - Lone surrogates (already filtered by Array.from)
24
+ // - Control chars except \t \n \r
25
+ // - Characters with undefined code points
26
+ const code = char.codePointAt(0);
27
+ // v8 ignore next -- Array.from iterating over a legal string, codePointAt will not return undefined
28
+ if (code === undefined)
29
+ return false;
30
+ // Allow tab, newline, carriage return
31
+ if (code === 0x09 || code === 0x0a || code === 0x0d)
32
+ return true;
33
+ // Filter out control characters (0x00-0x1F, except 0x09, 0x0a, 0x0d)
34
+ if (code <= 0x1f)
35
+ return false;
36
+ // Filter out Unicode format characters
37
+ if (code >= 0xfff9 && code <= 0xfffb)
38
+ return false;
39
+ return true;
40
+ })
41
+ .join("");
42
+ }
43
+ //# sourceMappingURL=shell-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-utils.js","sourceRoot":"","sources":["../src/shell-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAU;IACzD,uEAAuE;IACvE,sEAAsE;IACtE,uCAAuC;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SACpB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACjB,yDAAyD;QACzD,iBAAiB;QACjB,8BAA8B;QAC9B,qDAAqD;QACrD,kCAAkC;QAClC,0CAA0C;QAE1C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QAEjC,oGAAoG;QACpG,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAErC,sCAAsC;QACtC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAEjE,qEAAqE;QACrE,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,KAAK,CAAC;QAE/B,uCAAuC;QACvC,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM;YAAE,OAAO,KAAK,CAAC;QAEnD,OAAO,IAAI,CAAC;IAAA,CACZ,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,CACX","sourcesContent":["/**\n * Inlined from agent-forge `src/utils/shell.ts` (D-075 S3 extraction) so this\n * package keeps its dependency closure limited to the plugin SDK and typebox.\n * Semantics are unchanged.\n */\n\n/**\n * Sanitize binary output for display/storage.\n * Removes characters that crash string-width or cause display issues:\n * - Control characters (except tab, newline, carriage return)\n * - Lone surrogates\n * - Unicode Format characters (crash string-width due to a bug)\n * - Characters with undefined code points\n */\nexport function sanitizeBinaryOutput(str: string): string {\n\t// Use Array.from to properly iterate over code points (not code units)\n\t// This handles surrogate pairs correctly and catches edge cases where\n\t// codePointAt() might return undefined\n\treturn Array.from(str)\n\t\t.filter((char) => {\n\t\t\t// Filter out characters that cause string-width to crash\n\t\t\t// This includes:\n\t\t\t// - Unicode format characters\n\t\t\t// - Lone surrogates (already filtered by Array.from)\n\t\t\t// - Control chars except \\t \\n \\r\n\t\t\t// - Characters with undefined code points\n\n\t\t\tconst code = char.codePointAt(0);\n\n\t\t\t// v8 ignore next -- Array.from iterating over a legal string, codePointAt will not return undefined\n\t\t\tif (code === undefined) return false;\n\n\t\t\t// Allow tab, newline, carriage return\n\t\t\tif (code === 0x09 || code === 0x0a || code === 0x0d) return true;\n\n\t\t\t// Filter out control characters (0x00-0x1F, except 0x09, 0x0a, 0x0d)\n\t\t\tif (code <= 0x1f) return false;\n\n\t\t\t// Filter out Unicode format characters\n\t\t\tif (code >= 0xfff9 && code <= 0xfffb) return false;\n\n\t\t\treturn true;\n\t\t})\n\t\t.join(\"\");\n}\n"]}
@@ -0,0 +1,108 @@
1
+ import type { PluginErrorV1, PluginResultStatusV1 } from "@agent-forge/plugin-sdk";
2
+ export declare const GIT_REPOSITORY_PLUGIN_ID_V1: "agent-forge.plugin.git-repository";
3
+ export declare const GIT_REPOSITORY_CAPABILITY_VERSION_V1: 1;
4
+ export declare const GIT_REPOSITORY_HOST_ADAPTER_ID_V1: "node-git-repository";
5
+ export declare const GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1: "1.0.0";
6
+ /** Fixed read-only operations. The host owns every git command and repository path. */
7
+ export type GitRepositoryOperationV1 = "status" | "diff-summary" | "log-summary";
8
+ /** The only model-to-host request shape. `workspaceRef` is never a filesystem path. */
9
+ export interface GitRepositoryRequestV1 {
10
+ readonly version: 1;
11
+ readonly operationId: string;
12
+ readonly workspaceRef: string;
13
+ readonly operation: GitRepositoryOperationV1;
14
+ readonly limit?: number;
15
+ readonly timeoutMs?: number;
16
+ }
17
+ /** Aggregate metadata only; no diff hunk, raw log, branch, or command output is exposed. */
18
+ export interface GitRepositorySummaryV1 {
19
+ readonly filesChanged: number;
20
+ readonly added: number;
21
+ readonly modified: number;
22
+ readonly deleted: number;
23
+ readonly untracked: number;
24
+ readonly conflicted: number;
25
+ readonly additions: number;
26
+ readonly deletions: number;
27
+ readonly commits: number;
28
+ }
29
+ export type GitRepositoryFileStatusV1 = "added" | "modified" | "deleted" | "renamed" | "untracked" | "conflicted";
30
+ /** A relative repository location, never an absolute host path. */
31
+ export interface GitRepositoryFileEntryV1 {
32
+ readonly kind: "file";
33
+ readonly location: string;
34
+ readonly status: GitRepositoryFileStatusV1;
35
+ readonly additions?: number;
36
+ readonly deletions?: number;
37
+ }
38
+ /** Commit subjects are bounded and sanitized; commit bodies and raw log output stay host-owned. */
39
+ export interface GitRepositoryCommitEntryV1 {
40
+ readonly kind: "commit";
41
+ readonly commitRef: string;
42
+ readonly subject: string;
43
+ }
44
+ export type GitRepositoryEntryV1 = GitRepositoryFileEntryV1 | GitRepositoryCommitEntryV1;
45
+ export interface GitRepositoryResultV1 {
46
+ readonly version: 1;
47
+ readonly operationId: string;
48
+ readonly status: PluginResultStatusV1;
49
+ readonly summary: GitRepositorySummaryV1;
50
+ readonly entries: readonly GitRepositoryEntryV1[];
51
+ readonly entriesTruncated: boolean;
52
+ /** Opaque host handle for a complete report; never a filesystem path. */
53
+ readonly reportArtifactRef?: string;
54
+ readonly error?: PluginErrorV1;
55
+ }
56
+ /** Host-private resolved repository identity. It intentionally has no cwd or path field. */
57
+ export interface GitRepositoryTargetV1 {
58
+ readonly repositoryRef: string;
59
+ }
60
+ export interface GitRepositoryResolverV1 {
61
+ resolve(request: GitRepositoryRequestV1, signal?: AbortSignal): Promise<GitRepositoryTargetV1 | undefined>;
62
+ }
63
+ /**
64
+ * The runner owns command selection, argv, cwd, environment, credentials, and
65
+ * raw git output. None of those are represented in this plugin API.
66
+ */
67
+ export interface GitRepositoryRunnerV1 {
68
+ run(input: {
69
+ readonly repository: GitRepositoryTargetV1;
70
+ readonly operation: GitRepositoryOperationV1;
71
+ readonly limit: number;
72
+ readonly signal: AbortSignal;
73
+ }): Promise<GitRepositoryHostResultV1>;
74
+ }
75
+ /** Host output is deliberately summary-shaped before strict adapter projection. */
76
+ export type GitRepositoryHostResultV1 = {
77
+ readonly status: "completed";
78
+ readonly summary: unknown;
79
+ readonly entries?: readonly unknown[];
80
+ readonly reportArtifactRef?: unknown;
81
+ } | {
82
+ readonly status: "unsupported";
83
+ };
84
+ export interface NodeGitRepositoryAdapterOptionsV1 {
85
+ readonly resolver?: GitRepositoryResolverV1;
86
+ readonly runner?: GitRepositoryRunnerV1;
87
+ readonly defaultTimeoutMs?: number;
88
+ }
89
+ export interface GitRepositoryAdapterV1 {
90
+ readonly adapterId: string;
91
+ readonly adapterVersion: string;
92
+ run(request: GitRepositoryRequestV1, signal?: AbortSignal): Promise<GitRepositoryResultV1>;
93
+ }
94
+ export interface GitRepositoryDeterministicFixtureV1 {
95
+ readonly workspaceRef: string;
96
+ readonly operation: GitRepositoryOperationV1;
97
+ readonly result: GitRepositoryHostResultV1;
98
+ }
99
+ export interface GitRepositoryPluginManifestV1 {
100
+ readonly id: typeof GIT_REPOSITORY_PLUGIN_ID_V1;
101
+ readonly pluginVersion: string;
102
+ readonly apiVersion: "1";
103
+ readonly capabilityRefs: readonly [{
104
+ readonly id: "git.repository.inspect";
105
+ readonly version: 1;
106
+ }];
107
+ }
108
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PluginErrorV1, PluginResultStatusV1 } from \"@agent-forge/plugin-sdk\";\n\nexport const GIT_REPOSITORY_PLUGIN_ID_V1 = \"agent-forge.plugin.git-repository\" as const;\nexport const GIT_REPOSITORY_CAPABILITY_VERSION_V1 = 1 as const;\nexport const GIT_REPOSITORY_HOST_ADAPTER_ID_V1 = \"node-git-repository\" as const;\nexport const GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1 = \"1.0.0\" as const;\n\n/** Fixed read-only operations. The host owns every git command and repository path. */\nexport type GitRepositoryOperationV1 = \"status\" | \"diff-summary\" | \"log-summary\";\n\n/** The only model-to-host request shape. `workspaceRef` is never a filesystem path. */\nexport interface GitRepositoryRequestV1 {\n\treadonly version: 1;\n\treadonly operationId: string;\n\treadonly workspaceRef: string;\n\treadonly operation: GitRepositoryOperationV1;\n\treadonly limit?: number;\n\treadonly timeoutMs?: number;\n}\n\n/** Aggregate metadata only; no diff hunk, raw log, branch, or command output is exposed. */\nexport interface GitRepositorySummaryV1 {\n\treadonly filesChanged: number;\n\treadonly added: number;\n\treadonly modified: number;\n\treadonly deleted: number;\n\treadonly untracked: number;\n\treadonly conflicted: number;\n\treadonly additions: number;\n\treadonly deletions: number;\n\treadonly commits: number;\n}\n\nexport type GitRepositoryFileStatusV1 = \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"untracked\" | \"conflicted\";\n\n/** A relative repository location, never an absolute host path. */\nexport interface GitRepositoryFileEntryV1 {\n\treadonly kind: \"file\";\n\treadonly location: string;\n\treadonly status: GitRepositoryFileStatusV1;\n\treadonly additions?: number;\n\treadonly deletions?: number;\n}\n\n/** Commit subjects are bounded and sanitized; commit bodies and raw log output stay host-owned. */\nexport interface GitRepositoryCommitEntryV1 {\n\treadonly kind: \"commit\";\n\treadonly commitRef: string;\n\treadonly subject: string;\n}\n\nexport type GitRepositoryEntryV1 = GitRepositoryFileEntryV1 | GitRepositoryCommitEntryV1;\n\nexport interface GitRepositoryResultV1 {\n\treadonly version: 1;\n\treadonly operationId: string;\n\treadonly status: PluginResultStatusV1;\n\treadonly summary: GitRepositorySummaryV1;\n\treadonly entries: readonly GitRepositoryEntryV1[];\n\treadonly entriesTruncated: boolean;\n\t/** Opaque host handle for a complete report; never a filesystem path. */\n\treadonly reportArtifactRef?: string;\n\treadonly error?: PluginErrorV1;\n}\n\n/** Host-private resolved repository identity. It intentionally has no cwd or path field. */\nexport interface GitRepositoryTargetV1 {\n\treadonly repositoryRef: string;\n}\n\nexport interface GitRepositoryResolverV1 {\n\tresolve(request: GitRepositoryRequestV1, signal?: AbortSignal): Promise<GitRepositoryTargetV1 | undefined>;\n}\n\n/**\n * The runner owns command selection, argv, cwd, environment, credentials, and\n * raw git output. None of those are represented in this plugin API.\n */\nexport interface GitRepositoryRunnerV1 {\n\trun(input: {\n\t\treadonly repository: GitRepositoryTargetV1;\n\t\treadonly operation: GitRepositoryOperationV1;\n\t\treadonly limit: number;\n\t\treadonly signal: AbortSignal;\n\t}): Promise<GitRepositoryHostResultV1>;\n}\n\n/** Host output is deliberately summary-shaped before strict adapter projection. */\nexport type GitRepositoryHostResultV1 =\n\t| {\n\t\t\treadonly status: \"completed\";\n\t\t\treadonly summary: unknown;\n\t\t\treadonly entries?: readonly unknown[];\n\t\t\treadonly reportArtifactRef?: unknown;\n\t }\n\t| { readonly status: \"unsupported\" };\n\nexport interface NodeGitRepositoryAdapterOptionsV1 {\n\treadonly resolver?: GitRepositoryResolverV1;\n\treadonly runner?: GitRepositoryRunnerV1;\n\treadonly defaultTimeoutMs?: number;\n}\n\nexport interface GitRepositoryAdapterV1 {\n\treadonly adapterId: string;\n\treadonly adapterVersion: string;\n\trun(request: GitRepositoryRequestV1, signal?: AbortSignal): Promise<GitRepositoryResultV1>;\n}\n\nexport interface GitRepositoryDeterministicFixtureV1 {\n\treadonly workspaceRef: string;\n\treadonly operation: GitRepositoryOperationV1;\n\treadonly result: GitRepositoryHostResultV1;\n}\n\nexport interface GitRepositoryPluginManifestV1 {\n\treadonly id: typeof GIT_REPOSITORY_PLUGIN_ID_V1;\n\treadonly pluginVersion: string;\n\treadonly apiVersion: \"1\";\n\treadonly capabilityRefs: readonly [{ readonly id: \"git.repository.inspect\"; readonly version: 1 }];\n}\n"]}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ export const GIT_REPOSITORY_PLUGIN_ID_V1 = "agent-forge.plugin.git-repository";
2
+ export const GIT_REPOSITORY_CAPABILITY_VERSION_V1 = 1;
3
+ export const GIT_REPOSITORY_HOST_ADAPTER_ID_V1 = "node-git-repository";
4
+ export const GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1 = "1.0.0";
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,2BAA2B,GAAG,mCAA4C,CAAC;AACxF,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAU,CAAC;AAC/D,MAAM,CAAC,MAAM,iCAAiC,GAAG,qBAA8B,CAAC;AAChF,MAAM,CAAC,MAAM,sCAAsC,GAAG,OAAgB,CAAC","sourcesContent":["import type { PluginErrorV1, PluginResultStatusV1 } from \"@agent-forge/plugin-sdk\";\n\nexport const GIT_REPOSITORY_PLUGIN_ID_V1 = \"agent-forge.plugin.git-repository\" as const;\nexport const GIT_REPOSITORY_CAPABILITY_VERSION_V1 = 1 as const;\nexport const GIT_REPOSITORY_HOST_ADAPTER_ID_V1 = \"node-git-repository\" as const;\nexport const GIT_REPOSITORY_HOST_ADAPTER_VERSION_V1 = \"1.0.0\" as const;\n\n/** Fixed read-only operations. The host owns every git command and repository path. */\nexport type GitRepositoryOperationV1 = \"status\" | \"diff-summary\" | \"log-summary\";\n\n/** The only model-to-host request shape. `workspaceRef` is never a filesystem path. */\nexport interface GitRepositoryRequestV1 {\n\treadonly version: 1;\n\treadonly operationId: string;\n\treadonly workspaceRef: string;\n\treadonly operation: GitRepositoryOperationV1;\n\treadonly limit?: number;\n\treadonly timeoutMs?: number;\n}\n\n/** Aggregate metadata only; no diff hunk, raw log, branch, or command output is exposed. */\nexport interface GitRepositorySummaryV1 {\n\treadonly filesChanged: number;\n\treadonly added: number;\n\treadonly modified: number;\n\treadonly deleted: number;\n\treadonly untracked: number;\n\treadonly conflicted: number;\n\treadonly additions: number;\n\treadonly deletions: number;\n\treadonly commits: number;\n}\n\nexport type GitRepositoryFileStatusV1 = \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"untracked\" | \"conflicted\";\n\n/** A relative repository location, never an absolute host path. */\nexport interface GitRepositoryFileEntryV1 {\n\treadonly kind: \"file\";\n\treadonly location: string;\n\treadonly status: GitRepositoryFileStatusV1;\n\treadonly additions?: number;\n\treadonly deletions?: number;\n}\n\n/** Commit subjects are bounded and sanitized; commit bodies and raw log output stay host-owned. */\nexport interface GitRepositoryCommitEntryV1 {\n\treadonly kind: \"commit\";\n\treadonly commitRef: string;\n\treadonly subject: string;\n}\n\nexport type GitRepositoryEntryV1 = GitRepositoryFileEntryV1 | GitRepositoryCommitEntryV1;\n\nexport interface GitRepositoryResultV1 {\n\treadonly version: 1;\n\treadonly operationId: string;\n\treadonly status: PluginResultStatusV1;\n\treadonly summary: GitRepositorySummaryV1;\n\treadonly entries: readonly GitRepositoryEntryV1[];\n\treadonly entriesTruncated: boolean;\n\t/** Opaque host handle for a complete report; never a filesystem path. */\n\treadonly reportArtifactRef?: string;\n\treadonly error?: PluginErrorV1;\n}\n\n/** Host-private resolved repository identity. It intentionally has no cwd or path field. */\nexport interface GitRepositoryTargetV1 {\n\treadonly repositoryRef: string;\n}\n\nexport interface GitRepositoryResolverV1 {\n\tresolve(request: GitRepositoryRequestV1, signal?: AbortSignal): Promise<GitRepositoryTargetV1 | undefined>;\n}\n\n/**\n * The runner owns command selection, argv, cwd, environment, credentials, and\n * raw git output. None of those are represented in this plugin API.\n */\nexport interface GitRepositoryRunnerV1 {\n\trun(input: {\n\t\treadonly repository: GitRepositoryTargetV1;\n\t\treadonly operation: GitRepositoryOperationV1;\n\t\treadonly limit: number;\n\t\treadonly signal: AbortSignal;\n\t}): Promise<GitRepositoryHostResultV1>;\n}\n\n/** Host output is deliberately summary-shaped before strict adapter projection. */\nexport type GitRepositoryHostResultV1 =\n\t| {\n\t\t\treadonly status: \"completed\";\n\t\t\treadonly summary: unknown;\n\t\t\treadonly entries?: readonly unknown[];\n\t\t\treadonly reportArtifactRef?: unknown;\n\t }\n\t| { readonly status: \"unsupported\" };\n\nexport interface NodeGitRepositoryAdapterOptionsV1 {\n\treadonly resolver?: GitRepositoryResolverV1;\n\treadonly runner?: GitRepositoryRunnerV1;\n\treadonly defaultTimeoutMs?: number;\n}\n\nexport interface GitRepositoryAdapterV1 {\n\treadonly adapterId: string;\n\treadonly adapterVersion: string;\n\trun(request: GitRepositoryRequestV1, signal?: AbortSignal): Promise<GitRepositoryResultV1>;\n}\n\nexport interface GitRepositoryDeterministicFixtureV1 {\n\treadonly workspaceRef: string;\n\treadonly operation: GitRepositoryOperationV1;\n\treadonly result: GitRepositoryHostResultV1;\n}\n\nexport interface GitRepositoryPluginManifestV1 {\n\treadonly id: typeof GIT_REPOSITORY_PLUGIN_ID_V1;\n\treadonly pluginVersion: string;\n\treadonly apiVersion: \"1\";\n\treadonly capabilityRefs: readonly [{ readonly id: \"git.repository.inspect\"; readonly version: 1 }];\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@ai-agent-forge/plugin-git-repository",
3
+ "version": "0.85.0",
4
+ "description": "Git repository capability plugin for Agent Forge (read-only git_repository_inspect tool)",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "agent-forge.json",
17
+ "plugin.json",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "clean": "shx rm -rf dist",
22
+ "build": "tsgo -p tsconfig.build.json",
23
+ "test": "vitest --run",
24
+ "prepublishOnly": "npm run clean && npm run build"
25
+ },
26
+ "keywords": [
27
+ "agent-forge",
28
+ "plugin",
29
+ "git",
30
+ "repository"
31
+ ],
32
+ "author": "Agent Forge contributors",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/agent-forge/agent-forge.git",
37
+ "directory": "plugins/git-repository"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/agent-forge/agent-forge/issues"
41
+ },
42
+ "homepage": "https://github.com/agent-forge/agent-forge#readme",
43
+ "engines": {
44
+ "node": ">=22.19.0"
45
+ },
46
+ "dependencies": {
47
+ "@agent-forge/plugin-sdk": "^0.85.0",
48
+ "typebox": "1.3.27"
49
+ },
50
+ "devDependencies": {
51
+ "@agent-forge/agent-forge": "^0.85.0",
52
+ "shx": "0.4.0",
53
+ "vitest": "4.1.9"
54
+ }
55
+ }
package/plugin.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "agent-forge.plugin.git-repository",
3
+ "version": "0.84.4",
4
+ "apiVersion": "1-draft",
5
+ "entry": "./dist/entry.js",
6
+ "hostVersion": ">=0.84.4",
7
+ "requiredCapabilities": ["tools"],
8
+ "provides": [{ "id": "git.repository.inspect", "version": 1, "kind": "contribution" }]
9
+ }