@agentproto/governance-engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeremy André and agentproto contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * @agentproto/governance-engine v0.1.0-alpha
5
+ * Reference engine for agentgovernance/v1 — audit chain + sign artifact +
6
+ * pending-signatures index, all routed through IGovernanceFilesystem so the
7
+ * same code runs against Node fs, Supabase Storage, S3, or in-memory.
8
+ */
9
+
10
+ var DEFAULT_ANCHOR_EVERY_LINES = 1e3;
11
+ var governanceConfigSchema = z.object({
12
+ workspaceRoot: z.string().min(1),
13
+ genesisSeed: z.string().regex(/^[a-f0-9]{64}$/),
14
+ hmacSecret: z.string().min(1),
15
+ filesystem: z.custom().optional(),
16
+ anchorSink: z.custom().optional(),
17
+ anchorEveryLines: z.number().int().positive().optional()
18
+ });
19
+ var governanceToolContextSchema = z.object({
20
+ governanceConfig: governanceConfigSchema
21
+ });
22
+
23
+ export { DEFAULT_ANCHOR_EVERY_LINES, governanceToolContextSchema };
24
+ //# sourceMappingURL=chunk-M7I5HTGN.mjs.map
25
+ //# sourceMappingURL=chunk-M7I5HTGN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workspace-config.ts"],"names":[],"mappings":";;;;;;;;;AAiDO,IAAM,0BAAA,GAA6B;AAUnC,IAAM,sBAAA,GAAsD,EAAE,MAAA,CAAO;AAAA,EAC1E,aAAA,EAAe,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC/B,WAAA,EAAa,CAAA,CAAE,MAAA,EAAO,CAAE,MAAM,gBAAgB,CAAA;AAAA,EAC9C,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC5B,UAAA,EAAY,CAAA,CAAE,MAAA,EAA8B,CAAE,QAAA,EAAS;AAAA,EACvD,UAAA,EAAY,CAAA,CAAE,MAAA,EAAmB,CAAE,QAAA,EAAS;AAAA,EAC5C,gBAAA,EAAkB,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,GAAW,QAAA;AAChD,CAAC,CAAA;AAMM,IAAM,2BAAA,GAA8B,EAAE,MAAA,CAAO;AAAA,EAClD,gBAAA,EAAkB;AACpB,CAAC","file":"chunk-M7I5HTGN.mjs","sourcesContent":["/**\n * GovernanceConfig — runtime configuration the FS-only services need.\n *\n * Apps resolve genesis seed + HMAC secret from a vault / secrets manager and\n * pass an instance into the runtime services. The package itself does NOT\n * read environment variables or secret stores — it stays portable.\n */\n\nimport { z } from \"zod\"\nimport type { IGovernanceFilesystem } from \"./filesystem.js\"\n\nexport interface GovernanceConfig {\n /** Absolute path to the workspace root. */\n workspaceRoot: string\n\n /** Hex-encoded 64-char workspace genesis seed. */\n genesisSeed: string\n\n /** HMAC secret used for chain signatures. */\n hmacSecret: string\n\n /**\n * Filesystem adapter. Defaults to `NodeGovernanceFilesystem` (node:fs)\n * when omitted. Apps with non-POSIX storage (Supabase Storage, S3,\n * in-memory test fixtures) supply a custom implementation here.\n */\n filesystem?: IGovernanceFilesystem\n\n /** Optional: invoked when an audit-log line crosses an anchor threshold. */\n anchorSink?: AnchorSink\n\n /** Anchor every N lines (default 1000). */\n anchorEveryLines?: number\n}\n\nexport interface AnchorPayload {\n /** Audit-log file path (workspace-relative). */\n logPath: string\n /** 0-based line index that triggered the anchor (the line that crossed the threshold). */\n lineIndex: number\n /** The signature at that line — what gets anchored. */\n signature: string\n /** ISO timestamp when the anchor was emitted. */\n emittedAt: string\n}\n\nexport type AnchorSink = (payload: AnchorPayload) => Promise<void>\n\n/** Default anchor cadence. */\nexport const DEFAULT_ANCHOR_EVERY_LINES = 1000\n\n/**\n * Zod schema for GovernanceConfig — used by AIP-14 tool `contextSchema`\n * fields so the host-injected config is validated at the tool boundary.\n *\n * Function/interface fields (`filesystem`, `anchorSink`) are typed via\n * `z.custom` since zod can't deep-validate them; their presence is\n * checked by the runtime helpers downstream.\n */\nexport const governanceConfigSchema: z.ZodType<GovernanceConfig> = z.object({\n workspaceRoot: z.string().min(1),\n genesisSeed: z.string().regex(/^[a-f0-9]{64}$/),\n hmacSecret: z.string().min(1),\n filesystem: z.custom<IGovernanceFilesystem>().optional(),\n anchorSink: z.custom<AnchorSink>().optional(),\n anchorEveryLines: z.number().int().positive().optional(),\n})\n\n/**\n * Standard `contextSchema` shape for governance tools — the host MUST\n * inject `governanceConfig` at invocation time.\n */\nexport const governanceToolContextSchema = z.object({\n governanceConfig: governanceConfigSchema,\n})\n\nexport type GovernanceToolContext = z.infer<typeof governanceToolContextSchema>\n"]}
@@ -0,0 +1,147 @@
1
+ import { governanceToolContextSchema } from './chunk-M7I5HTGN.mjs';
2
+ import { z } from 'zod';
3
+ import { defineTool } from '@agentproto/tool';
4
+
5
+ /**
6
+ * @agentproto/governance-engine v0.1.0-alpha
7
+ * Reference engine for agentgovernance/v1 — audit chain + sign artifact +
8
+ * pending-signatures index, all routed through IGovernanceFilesystem so the
9
+ * same code runs against Node fs, Supabase Storage, S3, or in-memory.
10
+ */
11
+ var signArtifactTool = defineTool({
12
+ id: "governance.sign-artifact",
13
+ description: "Sign an artifact: hash the artifact bytes, write a signature.json next to it, append a hash-chained `signature.created` event to the audit log. The runtime always re-hashes from disk; supplying `expectedDocumentHash` asserts the expected hash and rejects on mismatch (does NOT substitute).",
14
+ version: "0.1.0",
15
+ inputSchema: z.object({
16
+ artifact: z.string().describe(
17
+ "AIP-27 Ref compact form for the artifact, in the `file` collection. Example: `local:engagements/acme/proposal.md`."
18
+ ),
19
+ signer: z.string().describe(
20
+ "AIP-27 Ref compact form for the signer, in the `identity` collection. Example: `operator:atlas`, `email:counterparty@example.com`."
21
+ ),
22
+ signerKind: z.enum([
23
+ "operator",
24
+ "user",
25
+ "counterparty",
26
+ "agent",
27
+ "external"
28
+ ]),
29
+ signerEmail: z.email().optional(),
30
+ method: z.enum([
31
+ "typed_name",
32
+ "agent_confirm",
33
+ "click_through",
34
+ "esign_external"
35
+ ]),
36
+ evidence: z.unknown().describe(
37
+ "Method-specific evidence object whose `kind` MUST equal `method`. Shape per AIP-7 signature evidence types."
38
+ ),
39
+ expectedDocumentHash: z.string().regex(/^[a-f0-9]{64}$/).optional().describe(
40
+ "Optional caller-asserted SHA-256 of the artifact bytes. Rejects on mismatch with the actual on-disk hash. Never substitutes."
41
+ ),
42
+ idempotencyKey: z.string().min(1).optional()
43
+ }),
44
+ outputSchema: z.object({
45
+ signaturePath: z.string(),
46
+ auditLogPath: z.string(),
47
+ auditLineIndex: z.number().int().nonnegative(),
48
+ documentHash: z.string().regex(/^[a-f0-9]{64}$/)
49
+ }),
50
+ contextSchema: governanceToolContextSchema,
51
+ mutates: ["fs:write"],
52
+ approval: "on-mutate",
53
+ riskLevel: 2
54
+ });
55
+ var recordAuditEventTool = defineTool({
56
+ id: "governance.record-audit-event",
57
+ description: "Append a hash-chained event to the workspace (or engagement) audit log. The runtime serialises concurrent appends per log path and computes the chain signature against the prior tail. Use for any non-signature event that needs to be auditable.",
58
+ version: "0.1.0",
59
+ inputSchema: z.object({
60
+ scopeDir: z.string().optional().describe(
61
+ "Workspace-relative folder containing audit-log.jsonl. Default: 'audit'. Engagement-scoped example: 'engagements/2026-acme/audit'."
62
+ ),
63
+ actorKind: z.enum(["operator", "user", "agent", "system", "counterparty"]),
64
+ actor: z.string().nullable().describe(
65
+ "AIP-27 Ref compact form for the actor (identity collection), or null for system events. Example: 'operator:atlas'."
66
+ ),
67
+ entityType: z.string().min(1),
68
+ entity: z.string().describe(
69
+ "AIP-27 Ref compact form for the entity the event is about. May be a file ref ('local:...') or an identity ref ('operator:...')."
70
+ ),
71
+ action: z.string().regex(/^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$/, {
72
+ message: "action must be '<entity>.<verb>' lowercase, e.g., 'policy.evaluated'"
73
+ }),
74
+ payload: z.record(z.string(), z.unknown()).optional(),
75
+ requestId: z.string().optional(),
76
+ traceId: z.string().optional(),
77
+ idempotencyKey: z.string().min(1).optional()
78
+ }),
79
+ outputSchema: z.object({
80
+ logPath: z.string(),
81
+ lineIndex: z.number().int().nonnegative(),
82
+ anchored: z.boolean(),
83
+ signature: z.string().regex(/^[a-f0-9]{64}$/)
84
+ }),
85
+ contextSchema: governanceToolContextSchema,
86
+ mutates: ["fs:write"],
87
+ approval: "auto",
88
+ riskLevel: 1
89
+ });
90
+ var requestSignaturesTool = defineTool({
91
+ id: "governance.request-signatures",
92
+ description: "Register that an artifact needs signatures from one or more signers. Each signer becomes queryable via governance.list-pending-signatures. The pending-signatures index is regeneratable from artifact frontmatter; this tool is the cache-write side of that contract.",
93
+ version: "0.1.0",
94
+ inputSchema: z.object({
95
+ artifact: z.string().describe(
96
+ "AIP-27 Ref compact form, file collection. Example: 'local:engagements/acme/proposal.md'."
97
+ ),
98
+ requiredSignatures: z.array(
99
+ z.object({
100
+ signer: z.string().describe("AIP-27 Ref compact form, identity collection."),
101
+ method: z.enum([
102
+ "typed_name",
103
+ "agent_confirm",
104
+ "click_through",
105
+ "esign_external"
106
+ ]).optional(),
107
+ weight: z.number().int().positive().optional(),
108
+ deadline: z.iso.datetime().optional()
109
+ })
110
+ ).min(1)
111
+ }),
112
+ outputSchema: z.object({
113
+ added: z.number().int().nonnegative()
114
+ }),
115
+ contextSchema: governanceToolContextSchema,
116
+ mutates: ["fs:write"],
117
+ approval: "auto",
118
+ riskLevel: 1
119
+ });
120
+ var listPendingSignaturesTool = defineTool({
121
+ id: "governance.list-pending-signatures",
122
+ description: "List artifacts awaiting signature from a given signer. Reads from the pending-signatures index (regeneratable from artifact frontmatter).",
123
+ version: "0.1.0",
124
+ inputSchema: z.object({
125
+ signer: z.string().describe("AIP-27 Ref compact form, identity collection.")
126
+ }),
127
+ outputSchema: z.object({
128
+ pending: z.array(
129
+ z.object({
130
+ artifactPath: z.string(),
131
+ deadline: z.string().optional(),
132
+ requestedAt: z.string(),
133
+ method: z.string().optional(),
134
+ weight: z.number().optional()
135
+ })
136
+ )
137
+ }),
138
+ contextSchema: governanceToolContextSchema,
139
+ mutates: [],
140
+ approval: "auto",
141
+ riskLevel: 0,
142
+ idempotent: true
143
+ });
144
+
145
+ export { listPendingSignaturesTool, recordAuditEventTool, requestSignaturesTool, signArtifactTool };
146
+ //# sourceMappingURL=chunk-NXMY54BX.mjs.map
147
+ //# sourceMappingURL=chunk-NXMY54BX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/sign-artifact.tool.ts","../src/tools/record-audit-event.tool.ts","../src/tools/request-signatures.tool.ts","../src/tools/list-pending-signatures.tool.ts"],"names":["defineTool","z"],"mappings":"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","file":"chunk-NXMY54BX.mjs","sourcesContent":["import { z } from \"zod\"\nimport { defineTool } from \"@agentproto/tool\"\nimport {\n governanceToolContextSchema,\n type GovernanceToolContext,\n} from \"../workspace-config.js\"\n\n/**\n * AIP-14 contract for signing an artifact.\n *\n * `governanceConfig` is read from the per-call `context` (validated by\n * `contextSchema`) — a single tool instance serves multiple workspaces /\n * tenants without re-instantiation. Hosts that wire the tool through a\n * Mastra adapter use `resolveContext` to project per-request guildId →\n * config.\n *\n * Input fields use AIP-27 `Ref` compact strings — `artifact: \"local:...\"`,\n * `signer: \"operator:...\"`. The body lives on the AIP-30 PROVIDER\n * (`@agentproto/governance-engine/provider`); this file carries only\n * the abstract contract per AIP-14.\n */\nexport const signArtifactTool = defineTool({\n id: \"governance.sign-artifact\",\n description:\n \"Sign an artifact: hash the artifact bytes, write a signature.json next to it, \" +\n \"append a hash-chained `signature.created` event to the audit log. The runtime \" +\n \"always re-hashes from disk; supplying `expectedDocumentHash` asserts the \" +\n \"expected hash and rejects on mismatch (does NOT substitute).\",\n version: \"0.1.0\",\n inputSchema: z.object({\n artifact: z\n .string()\n .describe(\n \"AIP-27 Ref compact form for the artifact, in the `file` collection. \" +\n \"Example: `local:engagements/acme/proposal.md`.\"\n ),\n signer: z\n .string()\n .describe(\n \"AIP-27 Ref compact form for the signer, in the `identity` collection. \" +\n \"Example: `operator:atlas`, `email:counterparty@example.com`.\"\n ),\n signerKind: z.enum([\n \"operator\",\n \"user\",\n \"counterparty\",\n \"agent\",\n \"external\",\n ]),\n signerEmail: z.email().optional(),\n method: z.enum([\n \"typed_name\",\n \"agent_confirm\",\n \"click_through\",\n \"esign_external\",\n ]),\n evidence: z\n .unknown()\n .describe(\n \"Method-specific evidence object whose `kind` MUST equal `method`. \" +\n \"Shape per AIP-7 signature evidence types.\"\n ),\n expectedDocumentHash: z\n .string()\n .regex(/^[a-f0-9]{64}$/)\n .optional()\n .describe(\n \"Optional caller-asserted SHA-256 of the artifact bytes. Rejects on \" +\n \"mismatch with the actual on-disk hash. Never substitutes.\"\n ),\n idempotencyKey: z.string().min(1).optional(),\n }),\n outputSchema: z.object({\n signaturePath: z.string(),\n auditLogPath: z.string(),\n auditLineIndex: z.number().int().nonnegative(),\n documentHash: z.string().regex(/^[a-f0-9]{64}$/),\n }),\n contextSchema:\n governanceToolContextSchema as z.ZodType<GovernanceToolContext>,\n mutates: [\"fs:write\"],\n approval: \"on-mutate\",\n riskLevel: 2,\n})\n","import { z } from \"zod\"\nimport { defineTool } from \"@agentproto/tool\"\nimport {\n governanceToolContextSchema,\n type GovernanceToolContext,\n} from \"../workspace-config.js\"\n\n/**\n * AIP-14 contract for appending an event to the workspace audit log.\n *\n * Reads `governanceConfig` from the per-call context (validated by\n * `contextSchema`). Body lives on the AIP-30 PROVIDER\n * (`@agentproto/governance-engine/provider`).\n */\nexport const recordAuditEventTool = defineTool({\n id: \"governance.record-audit-event\",\n description:\n \"Append a hash-chained event to the workspace (or engagement) audit log. \" +\n \"The runtime serialises concurrent appends per log path and computes the \" +\n \"chain signature against the prior tail. Use for any non-signature event \" +\n \"that needs to be auditable.\",\n version: \"0.1.0\",\n inputSchema: z.object({\n scopeDir: z\n .string()\n .optional()\n .describe(\n \"Workspace-relative folder containing audit-log.jsonl. Default: 'audit'. \" +\n \"Engagement-scoped example: 'engagements/2026-acme/audit'.\"\n ),\n actorKind: z.enum([\"operator\", \"user\", \"agent\", \"system\", \"counterparty\"]),\n actor: z\n .string()\n .nullable()\n .describe(\n \"AIP-27 Ref compact form for the actor (identity collection), or null \" +\n \"for system events. Example: 'operator:atlas'.\"\n ),\n entityType: z.string().min(1),\n entity: z\n .string()\n .describe(\n \"AIP-27 Ref compact form for the entity the event is about. May be a \" +\n \"file ref ('local:...') or an identity ref ('operator:...').\"\n ),\n action: z.string().regex(/^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$/, {\n message:\n \"action must be '<entity>.<verb>' lowercase, e.g., 'policy.evaluated'\",\n }),\n payload: z.record(z.string(), z.unknown()).optional(),\n requestId: z.string().optional(),\n traceId: z.string().optional(),\n idempotencyKey: z.string().min(1).optional(),\n }),\n outputSchema: z.object({\n logPath: z.string(),\n lineIndex: z.number().int().nonnegative(),\n anchored: z.boolean(),\n signature: z.string().regex(/^[a-f0-9]{64}$/),\n }),\n contextSchema:\n governanceToolContextSchema as z.ZodType<GovernanceToolContext>,\n mutates: [\"fs:write\"],\n approval: \"auto\",\n riskLevel: 1,\n})\n","import { z } from \"zod\"\nimport { defineTool } from \"@agentproto/tool\"\nimport {\n governanceToolContextSchema,\n type GovernanceToolContext,\n} from \"../workspace-config.js\"\n\n/**\n * AIP-14 contract for registering pending signatures on an artifact.\n *\n * Reads `governanceConfig` from per-call context. Body lives on the\n * AIP-30 PROVIDER (`@agentproto/governance-engine/provider`).\n */\nexport const requestSignaturesTool = defineTool({\n id: \"governance.request-signatures\",\n description:\n \"Register that an artifact needs signatures from one or more signers. \" +\n \"Each signer becomes queryable via governance.list-pending-signatures. \" +\n \"The pending-signatures index is regeneratable from artifact frontmatter; \" +\n \"this tool is the cache-write side of that contract.\",\n version: \"0.1.0\",\n inputSchema: z.object({\n artifact: z\n .string()\n .describe(\n \"AIP-27 Ref compact form, file collection. Example: 'local:engagements/acme/proposal.md'.\"\n ),\n requiredSignatures: z\n .array(\n z.object({\n signer: z\n .string()\n .describe(\"AIP-27 Ref compact form, identity collection.\"),\n method: z\n .enum([\n \"typed_name\",\n \"agent_confirm\",\n \"click_through\",\n \"esign_external\",\n ])\n .optional(),\n weight: z.number().int().positive().optional(),\n deadline: z.iso.datetime().optional(),\n })\n )\n .min(1),\n }),\n outputSchema: z.object({\n added: z.number().int().nonnegative(),\n }),\n contextSchema:\n governanceToolContextSchema as z.ZodType<GovernanceToolContext>,\n mutates: [\"fs:write\"],\n approval: \"auto\",\n riskLevel: 1,\n})\n","import { z } from \"zod\"\nimport { defineTool } from \"@agentproto/tool\"\nimport {\n governanceToolContextSchema,\n type GovernanceToolContext,\n} from \"../workspace-config.js\"\n\n/**\n * AIP-14 contract for the read-only query of pending signatures.\n *\n * Reads `governanceConfig` from per-call context. Body lives on the\n * AIP-30 PROVIDER (`@agentproto/governance-engine/provider`).\n */\nexport const listPendingSignaturesTool = defineTool({\n id: \"governance.list-pending-signatures\",\n description:\n \"List artifacts awaiting signature from a given signer. Reads from the \" +\n \"pending-signatures index (regeneratable from artifact frontmatter).\",\n version: \"0.1.0\",\n inputSchema: z.object({\n signer: z\n .string()\n .describe(\"AIP-27 Ref compact form, identity collection.\"),\n }),\n outputSchema: z.object({\n pending: z.array(\n z.object({\n artifactPath: z.string(),\n deadline: z.string().optional(),\n requestedAt: z.string(),\n method: z.string().optional(),\n weight: z.number().optional(),\n })\n ),\n }),\n contextSchema:\n governanceToolContextSchema as z.ZodType<GovernanceToolContext>,\n mutates: [],\n approval: \"auto\",\n riskLevel: 0,\n idempotent: true,\n})\n"]}
@@ -0,0 +1,320 @@
1
+ import { DEFAULT_ANCHOR_EVERY_LINES } from './chunk-M7I5HTGN.mjs';
2
+ import { promises } from 'fs';
3
+ import * as path2 from 'path';
4
+ import { createHash } from 'crypto';
5
+ import { signatureSchema, signatureFilename, auditEventSchema } from '@agentproto/governance/doctypes';
6
+ import { chainRow } from '@agentproto/governance/hash-chain';
7
+
8
+ /**
9
+ * @agentproto/governance-engine v0.1.0-alpha
10
+ * Reference engine for agentgovernance/v1 — audit chain + sign artifact +
11
+ * pending-signatures index, all routed through IGovernanceFilesystem so the
12
+ * same code runs against Node fs, Supabase Storage, S3, or in-memory.
13
+ */
14
+ var NodeGovernanceFilesystem = class {
15
+ async ensureDir(absDir) {
16
+ await promises.mkdir(absDir, { recursive: true });
17
+ }
18
+ async readFile(absPath) {
19
+ try {
20
+ return await promises.readFile(absPath, "utf8");
21
+ } catch (e) {
22
+ if (e.code === "ENOENT") return null;
23
+ throw e;
24
+ }
25
+ }
26
+ async writeFileAtomic(absPath, content) {
27
+ await this.ensureDir(path2.dirname(absPath));
28
+ const tmp = `${absPath}.${process.pid}.${Date.now()}.tmp`;
29
+ await promises.writeFile(tmp, content, "utf8");
30
+ await promises.rename(tmp, absPath);
31
+ }
32
+ async appendLine(absPath, line) {
33
+ await this.ensureDir(path2.dirname(absPath));
34
+ await promises.appendFile(absPath, line + "\n", "utf8");
35
+ }
36
+ async listDirectory(absDir) {
37
+ try {
38
+ const entries = await promises.readdir(absDir, { withFileTypes: true });
39
+ return entries.map((e) => ({
40
+ name: e.name,
41
+ isDirectory: e.isDirectory()
42
+ }));
43
+ } catch (e) {
44
+ if (e.code === "ENOENT") return [];
45
+ throw e;
46
+ }
47
+ }
48
+ };
49
+ var sharedNodeFs = new NodeGovernanceFilesystem();
50
+ function defaultGovernanceFilesystem() {
51
+ return sharedNodeFs;
52
+ }
53
+ function getFilesystem(config) {
54
+ return config.filesystem ?? defaultGovernanceFilesystem();
55
+ }
56
+ function sha256Hex(content) {
57
+ const hash = createHash("sha256");
58
+ if (typeof content === "string") hash.update(content, "utf8");
59
+ else hash.update(content);
60
+ return hash.digest("hex");
61
+ }
62
+ function resolveFromRoot(root, relPath) {
63
+ const abs = path2.resolve(root, relPath);
64
+ const rootAbs = path2.resolve(root);
65
+ if (!abs.startsWith(rootAbs + path2.sep) && abs !== rootAbs) {
66
+ throw new Error(
67
+ `Path escape detected: '${relPath}' resolves outside root '${root}'`
68
+ );
69
+ }
70
+ return abs;
71
+ }
72
+ function toRelativePath(root, absPath) {
73
+ const rel = path2.relative(path2.resolve(root), path2.resolve(absPath));
74
+ return rel.split(path2.sep).join("/");
75
+ }
76
+ async function ensureDir(dirPath) {
77
+ return defaultGovernanceFilesystem().ensureDir(dirPath);
78
+ }
79
+ async function readFileIfExists(filePath) {
80
+ return defaultGovernanceFilesystem().readFile(filePath);
81
+ }
82
+ async function atomicWrite(filePath, content) {
83
+ return defaultGovernanceFilesystem().writeFileAtomic(filePath, content);
84
+ }
85
+ async function appendLine(filePath, line) {
86
+ return defaultGovernanceFilesystem().appendLine(filePath, line);
87
+ }
88
+
89
+ // src/path-lock.ts
90
+ var chains = /* @__PURE__ */ new Map();
91
+ async function withPathLock(absPath, fn) {
92
+ const prev = chains.get(absPath) ?? Promise.resolve();
93
+ const next = prev.then(
94
+ () => fn(),
95
+ () => fn()
96
+ );
97
+ chains.set(
98
+ absPath,
99
+ next.catch(() => void 0)
100
+ );
101
+ return next;
102
+ }
103
+
104
+ // src/audit-chain.ts
105
+ var SCOPE_DIR_DEFAULT = "audit";
106
+ async function recordAuditEvent(config, input) {
107
+ getFilesystem(config);
108
+ const scopeDir = input.scopeDir ?? SCOPE_DIR_DEFAULT;
109
+ const logRel = `${scopeDir}/audit-log.jsonl`;
110
+ const logAbs = resolveFromRoot(config.workspaceRoot, logRel);
111
+ return withPathLock(
112
+ logAbs,
113
+ () => recordAuditEventLocked(config, input, logRel, logAbs)
114
+ );
115
+ }
116
+ async function recordAuditEventLocked(config, input, logRel, logAbs) {
117
+ const fs2 = getFilesystem(config);
118
+ const existing = await fs2.readFile(logAbs);
119
+ const lines = existing == null ? [] : existing.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
120
+ let prevSignature = config.genesisSeed;
121
+ if (lines.length > 0) {
122
+ const lastLine = lines[lines.length - 1];
123
+ const last = JSON.parse(lastLine);
124
+ if (typeof last.signature !== "string") {
125
+ throw new Error(
126
+ `recordAuditEvent: tail of ${logRel} is missing a signature field \u2014 chain corrupted`
127
+ );
128
+ }
129
+ prevSignature = last.signature;
130
+ }
131
+ const createdAt = input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
132
+ const metadata = { ...input.metadata ?? {} };
133
+ if (input.idempotencyKey) metadata.idempotencyKey = input.idempotencyKey;
134
+ const rowWithoutChain = {
135
+ schema: "agentgovernance/v1",
136
+ doctype: "audit-event",
137
+ actorKind: input.actorKind,
138
+ actorId: input.actorId,
139
+ entityType: input.entityType,
140
+ entityId: input.entityId,
141
+ action: input.action,
142
+ createdAt
143
+ };
144
+ if (input.payload !== void 0) rowWithoutChain.payload = input.payload;
145
+ if (input.requestId !== void 0) rowWithoutChain.requestId = input.requestId;
146
+ if (input.traceId !== void 0) rowWithoutChain.traceId = input.traceId;
147
+ if (input.ipAddress !== void 0) rowWithoutChain.ipAddress = input.ipAddress;
148
+ if (input.userAgent !== void 0) rowWithoutChain.userAgent = input.userAgent;
149
+ if (Object.keys(metadata).length > 0) rowWithoutChain.metadata = metadata;
150
+ const chained = chainRow(rowWithoutChain, prevSignature, config.hmacSecret);
151
+ const parsed = auditEventSchema.safeParse(chained);
152
+ if (!parsed.success) {
153
+ throw new Error(
154
+ `recordAuditEvent: produced an invalid event \u2014 ${parsed.error.issues.map((i) => i.message).join("; ")}`
155
+ );
156
+ }
157
+ await fs2.ensureDir(path2.dirname(logAbs));
158
+ await fs2.appendLine(logAbs, JSON.stringify(chained));
159
+ const lineIndex = lines.length;
160
+ const anchorEvery = config.anchorEveryLines ?? DEFAULT_ANCHOR_EVERY_LINES;
161
+ const anchored = (lineIndex + 1) % anchorEvery === 0;
162
+ if (anchored && config.anchorSink) {
163
+ const anchor = {
164
+ logPath: logRel,
165
+ lineIndex,
166
+ signature: chained.signature,
167
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
168
+ };
169
+ await config.anchorSink(anchor);
170
+ }
171
+ return {
172
+ event: parsed.data,
173
+ logPath: logRel,
174
+ lineIndex,
175
+ anchored
176
+ };
177
+ }
178
+ async function signArtifact(config, input) {
179
+ if (input.evidence.kind !== input.method) {
180
+ throw new Error(
181
+ `signArtifact: evidence.kind '${input.evidence.kind}' must match method '${input.method}'`
182
+ );
183
+ }
184
+ const fs2 = getFilesystem(config);
185
+ const artifactAbs = resolveFromRoot(config.workspaceRoot, input.artifactPath);
186
+ const artifactContent = await fs2.readFile(artifactAbs);
187
+ if (artifactContent == null) {
188
+ throw new Error(`signArtifact: artifact not found at ${input.artifactPath}`);
189
+ }
190
+ const documentHash = sha256Hex(artifactContent);
191
+ if (input.expectedDocumentHash !== void 0 && input.expectedDocumentHash !== documentHash) {
192
+ throw new Error(
193
+ `signArtifact: expectedDocumentHash mismatch \u2014 caller asserted ${input.expectedDocumentHash} but artifact bytes hash to ${documentHash}. Either the artifact changed since the caller computed the hash, or the caller is signing the wrong content.`
194
+ );
195
+ }
196
+ const signedAt = input.signedAt ?? (/* @__PURE__ */ new Date()).toISOString();
197
+ const sigObj = {
198
+ schema: "agentgovernance/v1",
199
+ doctype: "signature",
200
+ signer: input.signer,
201
+ signerKind: input.signerKind,
202
+ ...input.signerEmail !== void 0 ? { signerEmail: input.signerEmail } : {},
203
+ artifactPath: input.artifactPath,
204
+ documentHash,
205
+ method: input.method,
206
+ evidence: input.evidence,
207
+ signedAt,
208
+ ...input.metadata !== void 0 ? { metadata: input.metadata } : {}
209
+ };
210
+ const parsed = signatureSchema.safeParse(sigObj);
211
+ if (!parsed.success) {
212
+ throw new Error(
213
+ `signArtifact: produced an invalid signature \u2014 ${parsed.error.issues.map((i) => i.message).join("; ")}`
214
+ );
215
+ }
216
+ const artifactDir = path2.dirname(artifactAbs);
217
+ const signaturesDir = path2.join(artifactDir, "signatures");
218
+ const filename = signatureFilename(input.signer, signedAt);
219
+ const signaturePathAbs = path2.join(signaturesDir, filename);
220
+ await fs2.ensureDir(signaturesDir);
221
+ await fs2.writeFileAtomic(
222
+ signaturePathAbs,
223
+ JSON.stringify(parsed.data, null, 2) + "\n"
224
+ );
225
+ const auditScopeDir = pickAuditScope(input);
226
+ const audit = await recordAuditEvent(config, {
227
+ scopeDir: auditScopeDir,
228
+ actorKind: input.signerKind === "agent" ? "agent" : input.signerKind === "counterparty" ? "counterparty" : "operator",
229
+ actorId: extractSlug(input.signer),
230
+ entityType: "signature",
231
+ entityId: toRelativePath(config.workspaceRoot, signaturePathAbs),
232
+ action: "signature.created",
233
+ payload: {
234
+ method: input.method,
235
+ artifactPath: input.artifactPath,
236
+ documentHash
237
+ },
238
+ idempotencyKey: input.idempotencyKey
239
+ });
240
+ return {
241
+ signature: parsed.data,
242
+ signaturePath: toRelativePath(config.workspaceRoot, signaturePathAbs),
243
+ auditLogPath: audit.logPath,
244
+ auditLineIndex: audit.lineIndex
245
+ };
246
+ }
247
+ function extractSlug(signerId) {
248
+ const colon = signerId.indexOf(":");
249
+ return colon === -1 ? signerId : signerId.slice(colon + 1);
250
+ }
251
+ function pickAuditScope(input) {
252
+ const m = input.artifactPath.match(/^(engagements\/[^/]+)\//);
253
+ return m ? `${m[1]}/audit` : "audit";
254
+ }
255
+ var INDEX_PATH = "_index/pending-signatures.json";
256
+ async function loadPendingSignaturesIndex(config) {
257
+ const fs2 = getFilesystem(config);
258
+ const abs = resolveFromRoot(config.workspaceRoot, INDEX_PATH);
259
+ const content = await fs2.readFile(abs);
260
+ if (content == null) {
261
+ return { version: "1", updatedAt: (/* @__PURE__ */ new Date(0)).toISOString(), bySigner: {} };
262
+ }
263
+ try {
264
+ const parsed = JSON.parse(content);
265
+ return parsed;
266
+ } catch {
267
+ return { version: "1", updatedAt: (/* @__PURE__ */ new Date(0)).toISOString(), bySigner: {} };
268
+ }
269
+ }
270
+ async function writeIndex(config, idx) {
271
+ const fs2 = getFilesystem(config);
272
+ const abs = resolveFromRoot(config.workspaceRoot, INDEX_PATH);
273
+ await fs2.ensureDir(path2.dirname(abs));
274
+ await fs2.writeFileAtomic(abs, JSON.stringify(idx, null, 2) + "\n");
275
+ }
276
+ async function addPendingSignatures(config, artifactPath, required, requestedAt = (/* @__PURE__ */ new Date()).toISOString()) {
277
+ const indexAbs = resolveFromRoot(config.workspaceRoot, INDEX_PATH);
278
+ return withPathLock(indexAbs, async () => {
279
+ const idx = await loadPendingSignaturesIndex(config);
280
+ for (const r of required) {
281
+ const list = idx.bySigner[r.signer] ?? [];
282
+ const existing = list.findIndex((e) => e.artifactPath === artifactPath);
283
+ const entry = {
284
+ artifactPath,
285
+ requestedAt,
286
+ ...r.deadline !== void 0 ? { deadline: r.deadline } : {},
287
+ ...r.method !== void 0 ? { method: r.method } : {},
288
+ ...r.weight !== void 0 ? { weight: r.weight } : {}
289
+ };
290
+ if (existing >= 0) list[existing] = entry;
291
+ else list.push(entry);
292
+ idx.bySigner[r.signer] = list;
293
+ }
294
+ idx.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
295
+ await writeIndex(config, idx);
296
+ return idx;
297
+ });
298
+ }
299
+ async function removePendingSignature(config, signer, artifactPath) {
300
+ const indexAbs = resolveFromRoot(config.workspaceRoot, INDEX_PATH);
301
+ return withPathLock(indexAbs, async () => {
302
+ const idx = await loadPendingSignaturesIndex(config);
303
+ const list = idx.bySigner[signer];
304
+ if (list) {
305
+ idx.bySigner[signer] = list.filter((e) => e.artifactPath !== artifactPath);
306
+ if (idx.bySigner[signer].length === 0) delete idx.bySigner[signer];
307
+ }
308
+ idx.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
309
+ await writeIndex(config, idx);
310
+ return idx;
311
+ });
312
+ }
313
+ async function listPendingSignatures(config, signer) {
314
+ const idx = await loadPendingSignaturesIndex(config);
315
+ return idx.bySigner[signer] ?? [];
316
+ }
317
+
318
+ export { NodeGovernanceFilesystem, addPendingSignatures, appendLine, atomicWrite, defaultGovernanceFilesystem, ensureDir, getFilesystem, listPendingSignatures, loadPendingSignaturesIndex, readFileIfExists, recordAuditEvent, removePendingSignature, resolveFromRoot, sha256Hex, signArtifact, toRelativePath };
319
+ //# sourceMappingURL=chunk-P7TDNF2H.mjs.map
320
+ //# sourceMappingURL=chunk-P7TDNF2H.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/filesystem.ts","../src/fs.ts","../src/path-lock.ts","../src/audit-chain.ts","../src/sign-artifact.ts","../src/pending-signatures-index.ts"],"names":["fs","path","path3","path4","path5"],"mappings":"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","file":"chunk-P7TDNF2H.mjs","sourcesContent":["/**\n * IGovernanceFilesystem — vendor-neutral filesystem interface used by the\n * governance runtime. Lets a consumer plug a non-Node backend (Supabase\n * Storage, S3, in-memory) without forking the runtime.\n *\n * All paths are absolute (the runtime resolves root-relative paths via\n * `resolveFromRoot` before calling FS methods).\n *\n * Default implementation: `NodeGovernanceFilesystem`, backed by `node:fs`.\n * Backends MUST guarantee:\n * - `writeFileAtomic` is atomic on same-volume rename (or equivalent).\n * - `appendLine` is durable across process crashes (line either fully\n * present or absent in the resulting file — no torn writes).\n * - `readFile` returns `null` (not throws) when the file does not exist.\n */\n\nimport { promises as fs } from \"node:fs\"\nimport * as path from \"node:path\"\n\n/** A single child entry returned by `listDirectory`. */\nexport interface DirectoryEntry {\n /** File or subdirectory name (relative to the parent). No leading slash. */\n name: string\n /** True for subdirectories, false for files. */\n isDirectory: boolean\n}\n\nexport interface IGovernanceFilesystem {\n /** Recursively create a directory if it does not exist. */\n ensureDir(absDir: string): Promise<void>\n\n /** Read a UTF-8 file. Returns `null` when the file does not exist. */\n readFile(absPath: string): Promise<string | null>\n\n /**\n * Atomically write the full contents of a file. Implementations should\n * write to a temp file in the same dir, then rename — or use whatever\n * atomic-replace primitive the backend provides.\n */\n writeFileAtomic(absPath: string, content: string): Promise<void>\n\n /**\n * Append a single line + newline. Creates the file if absent. Must be\n * crash-safe: a partially-written line MUST NOT be visible to a\n * concurrent reader.\n */\n appendLine(absPath: string, line: string): Promise<void>\n\n /**\n * List the immediate children of a directory. Returns `[]` when the\n * directory does not exist (NOT throw). Order is implementation-defined;\n * callers MUST sort if they need stable order.\n */\n listDirectory(absDir: string): Promise<DirectoryEntry[]>\n}\n\n// ─── Default Node-fs implementation ────────────────────────────────────\n\nexport class NodeGovernanceFilesystem implements IGovernanceFilesystem {\n async ensureDir(absDir: string): Promise<void> {\n await fs.mkdir(absDir, { recursive: true })\n }\n\n async readFile(absPath: string): Promise<string | null> {\n try {\n return await fs.readFile(absPath, \"utf8\")\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ENOENT\") return null\n throw e\n }\n }\n\n async writeFileAtomic(absPath: string, content: string): Promise<void> {\n await this.ensureDir(path.dirname(absPath))\n const tmp = `${absPath}.${process.pid}.${Date.now()}.tmp`\n await fs.writeFile(tmp, content, \"utf8\")\n await fs.rename(tmp, absPath)\n }\n\n async appendLine(absPath: string, line: string): Promise<void> {\n await this.ensureDir(path.dirname(absPath))\n await fs.appendFile(absPath, line + \"\\n\", \"utf8\")\n }\n\n async listDirectory(absDir: string): Promise<DirectoryEntry[]> {\n try {\n const entries = await fs.readdir(absDir, { withFileTypes: true })\n return entries.map(e => ({\n name: e.name,\n isDirectory: e.isDirectory(),\n }))\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ENOENT\") return []\n throw e\n }\n }\n}\n\nconst sharedNodeFs = new NodeGovernanceFilesystem()\n\n/** Default singleton — Node fs. */\nexport function defaultGovernanceFilesystem(): IGovernanceFilesystem {\n return sharedNodeFs\n}\n","import { createHash } from \"node:crypto\"\nimport * as path from \"node:path\"\n\nimport {\n defaultGovernanceFilesystem,\n type IGovernanceFilesystem,\n} from \"./filesystem.js\"\nimport type { GovernanceConfig } from \"./workspace-config.js\"\n\n/**\n * Pure helpers (no FS) + thin adapter shims.\n *\n * The FS-bound legacy exports (`ensureDir`, `readFileIfExists`, `atomicWrite`,\n * `appendLine`) are kept as thin wrappers over the default Node filesystem so\n * external consumers continue to work; new code should prefer\n * `getFilesystem(config)` from `./filesystem.js` and call the adapter directly.\n */\n\n/** Resolve the filesystem adapter from a GovernanceConfig (default = Node fs). */\nexport function getFilesystem(config: GovernanceConfig): IGovernanceFilesystem {\n return config.filesystem ?? defaultGovernanceFilesystem()\n}\n\n/** Compute SHA-256 of UTF-8 string content, hex lowercase. */\nexport function sha256Hex(content: string | Uint8Array): string {\n const hash = createHash(\"sha256\")\n if (typeof content === \"string\") hash.update(content, \"utf8\")\n else hash.update(content)\n return hash.digest(\"hex\")\n}\n\n/**\n * Resolve a path against a root directory, rejecting paths that escape the\n * root. The \"root\" is any base directory string — workspace, engagement\n * subdir, external mount, etc. — not coupled to the workspace entity notion.\n */\nexport function resolveFromRoot(root: string, relPath: string): string {\n const abs = path.resolve(root, relPath)\n const rootAbs = path.resolve(root)\n if (!abs.startsWith(rootAbs + path.sep) && abs !== rootAbs) {\n throw new Error(\n `Path escape detected: '${relPath}' resolves outside root '${root}'`\n )\n }\n return abs\n}\n\n/** Inverse of `resolveFromRoot`: forward-slash relative path, no leading \"./\". */\nexport function toRelativePath(root: string, absPath: string): string {\n const rel = path.relative(path.resolve(root), path.resolve(absPath))\n return rel.split(path.sep).join(\"/\")\n}\n\n// ─── Legacy Node-fs shims (kept for backward compatibility) ────────────\n//\n// These delegate to the default Node filesystem adapter. New runtime code\n// should call `getFilesystem(config).ensureDir(...)` etc. directly so it\n// honors a consumer-supplied SupabaseFilesystem (or other backend).\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n return defaultGovernanceFilesystem().ensureDir(dirPath)\n}\n\nexport async function readFileIfExists(\n filePath: string\n): Promise<string | null> {\n return defaultGovernanceFilesystem().readFile(filePath)\n}\n\nexport async function atomicWrite(\n filePath: string,\n content: string\n): Promise<void> {\n return defaultGovernanceFilesystem().writeFileAtomic(filePath, content)\n}\n\nexport async function appendLine(\n filePath: string,\n line: string\n): Promise<void> {\n return defaultGovernanceFilesystem().appendLine(filePath, line)\n}\n","/**\n * Per-path in-process lock — serializes operations against a given absolute\n * path so concurrent callers cannot interleave a read-modify-write window.\n *\n * Implementation: one Promise chain per path, kept in a module-level Map.\n * Each operation `.then`s onto the chain; `await withPathLock(p, fn)` resolves\n * only after every prior locked operation against `p` has completed.\n *\n * Scope: in-process only. Concurrent governance writers across multiple\n * processes (same machine, multiple containers, multi-replica deploys) need\n * an external lock — `proper-lockfile`, an advisory DB lock, or moving the\n * audit log into a transactional store. Out of scope for v0.1; the README\n * documents this trust boundary.\n *\n * Why we lock at all: the audit-log append is a read-tail → compute-chain\n * → append sequence. Two concurrent appends without a lock both see the\n * same tail, both compute the same prevSignature, and the second append's\n * chain expectation no longer matches reality — the chain is silently\n * corrupted. Same race exists for the pending-signatures index (a\n * read-modify-write of a single JSON file).\n */\n\nconst chains = new Map<string, Promise<unknown>>()\n\nexport async function withPathLock<T>(\n absPath: string,\n fn: () => Promise<T>\n): Promise<T> {\n const prev = chains.get(absPath) ?? Promise.resolve()\n const next = prev.then(\n () => fn(),\n () => fn()\n )\n // Store a swallowed copy so a rejected operation doesn't poison the chain.\n chains.set(\n absPath,\n next.catch(() => undefined)\n )\n return next\n}\n\n/** Test-only — drop all lock state. Never call from production code. */\nexport function _resetPathLocksForTest(): void {\n chains.clear()\n}\n","import * as path from \"node:path\"\n\nimport {\n auditEventSchema,\n type ActorKind,\n type AuditEvent,\n} from \"@agentproto/governance/doctypes\"\nimport { chainRow } from \"@agentproto/governance/hash-chain\"\nimport { getFilesystem, resolveFromRoot } from \"./fs.js\"\nimport { withPathLock } from \"./path-lock.js\"\nimport {\n type GovernanceConfig,\n type AnchorPayload,\n DEFAULT_ANCHOR_EVERY_LINES,\n} from \"./workspace-config.js\"\n\n/**\n * `recordAuditEvent` — append a hash-chained audit event to the workspace's\n * audit log.\n *\n * Scope is workspace-relative; defaults to the workspace-level log\n * (`audit/audit-log.jsonl`). Per-engagement scope is the typical alternative\n * (`engagements/<slug>/audit/audit-log.jsonl`).\n *\n * The function:\n * 1. Reads the current log to find the last signature (or uses genesisSeed for the first line).\n * 2. Composes the row, computes the chain signature.\n * 3. Appends the JSONL line.\n * 4. Optionally invokes the anchor sink if the line index crosses the cadence.\n *\n * Returns the recorded event with `prevSignature` and `signature` populated.\n */\n\nexport interface RecordAuditEventInput {\n /**\n * Where the log lives. Workspace-relative folder; the file is\n * `<scope>/audit-log.jsonl`. Default: `audit` (workspace-level log).\n *\n * Example for engagement-scoped: `engagements/2026-acme/audit`\n */\n scopeDir?: string\n\n actorKind: ActorKind\n actorId: string | null\n\n entityType: string\n entityId: string\n /** \"<entity>.<verb>\" lowercase (e.g., `signature.created`). */\n action: string\n\n payload?: Record<string, unknown>\n requestId?: string\n traceId?: string\n ipAddress?: string\n userAgent?: string\n\n /**\n * Idempotency key. v1 stores it in `metadata.idempotencyKey` for inspection\n * but does not yet dedup automatically (TODO: `_index/dispatched-keys.json`).\n */\n idempotencyKey?: string\n\n /** Override the timestamp; default is `new Date().toISOString()`. */\n createdAt?: string\n\n /** Vendor extensions under metadata.<vendor>.* */\n metadata?: Record<string, unknown>\n}\n\nexport interface RecordAuditEventResult {\n event: AuditEvent\n /** Workspace-relative path to the log file. */\n logPath: string\n /** 0-based line index of the appended event. */\n lineIndex: number\n /** True if an anchor was emitted on this append. */\n anchored: boolean\n}\n\nconst SCOPE_DIR_DEFAULT = \"audit\"\n\nexport async function recordAuditEvent(\n config: GovernanceConfig,\n input: RecordAuditEventInput\n): Promise<RecordAuditEventResult> {\n const fs = getFilesystem(config)\n const scopeDir = input.scopeDir ?? SCOPE_DIR_DEFAULT\n const logRel = `${scopeDir}/audit-log.jsonl`\n const logAbs = resolveFromRoot(config.workspaceRoot, logRel)\n\n // Serialize the read-tail → compute-chain → append sequence per log path.\n // Without this lock, concurrent appends silently corrupt the chain.\n return withPathLock(logAbs, () =>\n recordAuditEventLocked(config, input, logRel, logAbs)\n )\n}\n\nasync function recordAuditEventLocked(\n config: GovernanceConfig,\n input: RecordAuditEventInput,\n logRel: string,\n logAbs: string\n): Promise<RecordAuditEventResult> {\n const fs = getFilesystem(config)\n\n const existing = await fs.readFile(logAbs)\n const lines =\n existing == null\n ? []\n : existing\n .split(\"\\n\")\n .map(l => l.trim())\n .filter(l => l.length > 0)\n\n let prevSignature = config.genesisSeed\n if (lines.length > 0) {\n const lastLine = lines[lines.length - 1]!\n const last = JSON.parse(lastLine) as { signature?: string }\n if (typeof last.signature !== \"string\") {\n throw new Error(\n `recordAuditEvent: tail of ${logRel} is missing a signature field — chain corrupted`\n )\n }\n prevSignature = last.signature\n }\n\n const createdAt = input.createdAt ?? new Date().toISOString()\n const metadata: Record<string, unknown> = { ...(input.metadata ?? {}) }\n if (input.idempotencyKey) metadata.idempotencyKey = input.idempotencyKey\n\n const rowWithoutChain: Record<string, unknown> = {\n schema: \"agentgovernance/v1\",\n doctype: \"audit-event\",\n actorKind: input.actorKind,\n actorId: input.actorId,\n entityType: input.entityType,\n entityId: input.entityId,\n action: input.action,\n createdAt,\n }\n if (input.payload !== undefined) rowWithoutChain.payload = input.payload\n if (input.requestId !== undefined) rowWithoutChain.requestId = input.requestId\n if (input.traceId !== undefined) rowWithoutChain.traceId = input.traceId\n if (input.ipAddress !== undefined) rowWithoutChain.ipAddress = input.ipAddress\n if (input.userAgent !== undefined) rowWithoutChain.userAgent = input.userAgent\n if (Object.keys(metadata).length > 0) rowWithoutChain.metadata = metadata\n\n const chained = chainRow(rowWithoutChain, prevSignature, config.hmacSecret)\n\n // Validate against zod before writing — catch any shape regression early.\n const parsed = auditEventSchema.safeParse(chained)\n if (!parsed.success) {\n throw new Error(\n `recordAuditEvent: produced an invalid event — ${parsed.error.issues.map(i => i.message).join(\"; \")}`\n )\n }\n\n await fs.ensureDir(path.dirname(logAbs))\n await fs.appendLine(logAbs, JSON.stringify(chained))\n\n const lineIndex = lines.length\n const anchorEvery = config.anchorEveryLines ?? DEFAULT_ANCHOR_EVERY_LINES\n const anchored = (lineIndex + 1) % anchorEvery === 0\n if (anchored && config.anchorSink) {\n const anchor: AnchorPayload = {\n logPath: logRel,\n lineIndex,\n signature: chained.signature,\n emittedAt: new Date().toISOString(),\n }\n await config.anchorSink(anchor)\n }\n\n return {\n event: parsed.data,\n logPath: logRel,\n lineIndex,\n anchored,\n }\n}\n","import * as path from \"node:path\"\n\nimport {\n signatureSchema,\n type Signature,\n type SignatureEvidence,\n type SignerKind,\n type SigningMethod,\n signatureFilename,\n} from \"@agentproto/governance/doctypes\"\nimport {\n getFilesystem,\n resolveFromRoot,\n sha256Hex,\n toRelativePath,\n} from \"./fs.js\"\nimport { recordAuditEvent } from \"./audit-chain.js\"\nimport type { GovernanceConfig } from \"./workspace-config.js\"\n\n/**\n * `signArtifact` — write a signature.json file alongside an artifact and\n * append a hash-chained audit-log entry.\n *\n * The signing method's evidence shape MUST match the top-level method.\n * The document hash is ALWAYS computed from the artifact bytes on disk.\n * Callers MAY supply `expectedDocumentHash` to assert what they believe\n * the bytes are; if it doesn't match what we actually read, we reject.\n * This closes the forgery primitive in earlier drafts where a caller\n * could dictate the hash and produce a signature that didn't match the\n * underlying file.\n */\n\nexport interface SignArtifactInput {\n /** Workspace-relative path to the artifact being signed. */\n artifactPath: string\n /** Canonical signer \"<kind>:<slug>\" (e.g., `operator:jeremy`, `counterparty:acme-corp`). */\n signer: string\n signerKind: SignerKind\n signerEmail?: string\n method: SigningMethod\n evidence: SignatureEvidence\n /**\n * Optional caller-asserted document hash. When provided, MUST equal the\n * SHA-256 of the artifact bytes — otherwise `signArtifact` rejects.\n * The recorded `documentHash` is always the value computed from bytes,\n * never the caller-supplied assertion.\n */\n expectedDocumentHash?: string\n /** Override timestamp; default = now. */\n signedAt?: string\n /** Idempotency key (recorded in audit-log entry). */\n idempotencyKey?: string\n /** Vendor extensions under metadata.<vendor>.* */\n metadata?: Record<string, unknown>\n}\n\nexport interface SignArtifactResult {\n signature: Signature\n /** Workspace-relative path to the signature.json file written. */\n signaturePath: string\n /** Audit log entry summary. */\n auditLogPath: string\n auditLineIndex: number\n}\n\nexport async function signArtifact(\n config: GovernanceConfig,\n input: SignArtifactInput\n): Promise<SignArtifactResult> {\n // 1. Validate evidence-method consistency upfront for a clearer error.\n if (input.evidence.kind !== input.method) {\n throw new Error(\n `signArtifact: evidence.kind '${input.evidence.kind}' must match method '${input.method}'`\n )\n }\n\n const fs = getFilesystem(config)\n\n // 2. Resolve artifact + compute documentHash from disk bytes.\n // The hash is ALWAYS what we actually read; an `expectedDocumentHash`\n // input acts only as an assertion, never as a substitute.\n const artifactAbs = resolveFromRoot(config.workspaceRoot, input.artifactPath)\n const artifactContent = await fs.readFile(artifactAbs)\n if (artifactContent == null) {\n throw new Error(`signArtifact: artifact not found at ${input.artifactPath}`)\n }\n const documentHash = sha256Hex(artifactContent)\n if (\n input.expectedDocumentHash !== undefined &&\n input.expectedDocumentHash !== documentHash\n ) {\n throw new Error(\n `signArtifact: expectedDocumentHash mismatch — caller asserted ${input.expectedDocumentHash} ` +\n `but artifact bytes hash to ${documentHash}. ` +\n `Either the artifact changed since the caller computed the hash, ` +\n `or the caller is signing the wrong content.`\n )\n }\n\n // 3. Compose the signature object.\n const signedAt = input.signedAt ?? new Date().toISOString()\n const sigObj: Signature = {\n schema: \"agentgovernance/v1\",\n doctype: \"signature\",\n signer: input.signer,\n signerKind: input.signerKind,\n ...(input.signerEmail !== undefined\n ? { signerEmail: input.signerEmail }\n : {}),\n artifactPath: input.artifactPath,\n documentHash,\n method: input.method,\n evidence: input.evidence,\n signedAt,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n } as Signature\n\n // 4. Validate against the zod schema.\n const parsed = signatureSchema.safeParse(sigObj)\n if (!parsed.success) {\n throw new Error(\n `signArtifact: produced an invalid signature — ${parsed.error.issues.map(i => i.message).join(\"; \")}`\n )\n }\n\n // 5. Write signature.json next to the artifact.\n const artifactDir = path.dirname(artifactAbs)\n const signaturesDir = path.join(artifactDir, \"signatures\")\n const filename = signatureFilename(input.signer, signedAt)\n const signaturePathAbs = path.join(signaturesDir, filename)\n await fs.ensureDir(signaturesDir)\n await fs.writeFileAtomic(\n signaturePathAbs,\n JSON.stringify(parsed.data, null, 2) + \"\\n\"\n )\n\n // 6. Determine audit-log scope: same parent folder as the artifact for\n // engagement-scoped logs; falls back to workspace-level otherwise.\n // Heuristic: if artifact is under <something>/.../<x>/ANY.md, write to\n // <something>/audit/audit-log.jsonl when that folder exists, else to\n // workspace-level audit/audit-log.jsonl.\n // Apps can override by passing `metadata.<vendor>.auditScopeDir`.\n const auditScopeDir = pickAuditScope(input)\n const audit = await recordAuditEvent(config, {\n scopeDir: auditScopeDir,\n actorKind:\n input.signerKind === \"agent\"\n ? \"agent\"\n : input.signerKind === \"counterparty\"\n ? \"counterparty\"\n : \"operator\",\n actorId: extractSlug(input.signer),\n entityType: \"signature\",\n entityId: toRelativePath(config.workspaceRoot, signaturePathAbs),\n action: \"signature.created\",\n payload: {\n method: input.method,\n artifactPath: input.artifactPath,\n documentHash,\n },\n idempotencyKey: input.idempotencyKey,\n })\n\n return {\n signature: parsed.data,\n signaturePath: toRelativePath(config.workspaceRoot, signaturePathAbs),\n auditLogPath: audit.logPath,\n auditLineIndex: audit.lineIndex,\n }\n}\n\nfunction extractSlug(signerId: string): string {\n const colon = signerId.indexOf(\":\")\n return colon === -1 ? signerId : signerId.slice(colon + 1)\n}\n\nfunction pickAuditScope(input: SignArtifactInput): string {\n // Heuristic: if artifactPath has the form \"engagements/<slug>/...\", use\n // \"engagements/<slug>/audit\". Otherwise use workspace-level \"audit\".\n const m = input.artifactPath.match(/^(engagements\\/[^/]+)\\//)\n return m ? `${m[1]}/audit` : \"audit\"\n}\n","import * as path from \"node:path\"\nimport { getFilesystem, resolveFromRoot } from \"./fs.js\"\nimport { withPathLock } from \"./path-lock.js\"\nimport type { GovernanceConfig } from \"./workspace-config.js\"\n\n/**\n * `_index/pending-signatures.json` — regeneratable index keyed by signer.\n *\n * Source of truth: artifact frontmatter / sidecar files declaring\n * `requiredSignatures`. This index is a cache for fast \"what is awaiting\n * my signature\" queries; it can be rebuilt at any time by walking the\n * workspace.\n *\n * Shape:\n * ```json\n * {\n * \"version\": \"1\",\n * \"updatedAt\": \"2026-04-26T15:00:00.000Z\",\n * \"bySigner\": {\n * \"operator:founder\": [\n * {\n * \"artifactPath\": \"engagements/2026-acme/INVOICE.md\",\n * \"deadline\": \"2026-05-15T17:00:00.000Z\",\n * \"requestedAt\": \"2026-04-26T14:00:00.000Z\"\n * }\n * ]\n * }\n * }\n * ```\n */\n\nexport interface PendingSignatureEntry {\n artifactPath: string\n deadline?: string\n requestedAt: string\n /** Method preferred for this signer. */\n method?: string\n /** Optional weight (for weighted_threshold policies). */\n weight?: number\n}\n\nexport interface PendingSignaturesIndex {\n version: \"1\"\n updatedAt: string\n bySigner: Record<string, PendingSignatureEntry[]>\n}\n\nconst INDEX_PATH = \"_index/pending-signatures.json\"\n\nexport async function loadPendingSignaturesIndex(\n config: GovernanceConfig\n): Promise<PendingSignaturesIndex> {\n const fs = getFilesystem(config)\n const abs = resolveFromRoot(config.workspaceRoot, INDEX_PATH)\n const content = await fs.readFile(abs)\n if (content == null) {\n return { version: \"1\", updatedAt: new Date(0).toISOString(), bySigner: {} }\n }\n try {\n const parsed = JSON.parse(content) as PendingSignaturesIndex\n return parsed\n } catch {\n // Corrupted index — return empty; caller can rebuild from filesystem walk.\n return { version: \"1\", updatedAt: new Date(0).toISOString(), bySigner: {} }\n }\n}\n\nasync function writeIndex(\n config: GovernanceConfig,\n idx: PendingSignaturesIndex\n): Promise<void> {\n const fs = getFilesystem(config)\n const abs = resolveFromRoot(config.workspaceRoot, INDEX_PATH)\n await fs.ensureDir(path.dirname(abs))\n await fs.writeFileAtomic(abs, JSON.stringify(idx, null, 2) + \"\\n\")\n}\n\nexport async function addPendingSignatures(\n config: GovernanceConfig,\n artifactPath: string,\n required: {\n signer: string\n method?: string\n weight?: number\n deadline?: string\n }[],\n requestedAt: string = new Date().toISOString()\n): Promise<PendingSignaturesIndex> {\n const indexAbs = resolveFromRoot(config.workspaceRoot, INDEX_PATH)\n // Serialize the read-modify-write of the index file per workspace.\n return withPathLock(indexAbs, async () => {\n const idx = await loadPendingSignaturesIndex(config)\n for (const r of required) {\n const list = idx.bySigner[r.signer] ?? []\n // Avoid duplicate entries for the same artifact.\n const existing = list.findIndex(e => e.artifactPath === artifactPath)\n const entry: PendingSignatureEntry = {\n artifactPath,\n requestedAt,\n ...(r.deadline !== undefined ? { deadline: r.deadline } : {}),\n ...(r.method !== undefined ? { method: r.method } : {}),\n ...(r.weight !== undefined ? { weight: r.weight } : {}),\n }\n if (existing >= 0) list[existing] = entry\n else list.push(entry)\n idx.bySigner[r.signer] = list\n }\n idx.updatedAt = new Date().toISOString()\n await writeIndex(config, idx)\n return idx\n })\n}\n\nexport async function removePendingSignature(\n config: GovernanceConfig,\n signer: string,\n artifactPath: string\n): Promise<PendingSignaturesIndex> {\n const indexAbs = resolveFromRoot(config.workspaceRoot, INDEX_PATH)\n return withPathLock(indexAbs, async () => {\n const idx = await loadPendingSignaturesIndex(config)\n const list = idx.bySigner[signer]\n if (list) {\n idx.bySigner[signer] = list.filter(e => e.artifactPath !== artifactPath)\n if (idx.bySigner[signer]!.length === 0) delete idx.bySigner[signer]\n }\n idx.updatedAt = new Date().toISOString()\n await writeIndex(config, idx)\n return idx\n })\n}\n\nexport async function listPendingSignatures(\n config: GovernanceConfig,\n signer: string\n): Promise<PendingSignatureEntry[]> {\n const idx = await loadPendingSignaturesIndex(config)\n return idx.bySigner[signer] ?? []\n}\n"]}