@asm-agent/governance 0.8.2

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,13 @@
1
+ # `@asm-agent/governance`
2
+
3
+ This package separates intent parsing, provider/model routing, policy decisions,
4
+ approval grants, and execution. Parsing describes an operation; it never
5
+ authorizes it.
6
+
7
+ Ambiguous requests may be routed only through authenticated `openai-codex` or
8
+ `anthropic` models. The router accepts login-backed model availability and has
9
+ no API-key/BYOK configuration.
10
+
11
+ External, destructive, deployment, communication, and financial mutations
12
+ require idempotency. Approval grants are bound to the exact intent operation,
13
+ resources, and approval type by SHA-256.
@@ -0,0 +1,17 @@
1
+ import type { IntentContract } from "@asm-agent/contracts";
2
+ export type IntentOperation = IntentContract["operation"];
3
+ export type ConsequenceClass = "none" | "local_mutation" | "external_mutation" | "irreversible" | "financial";
4
+ export interface IntentSchemaDefinition {
5
+ operation: IntentOperation;
6
+ consequence: ConsequenceClass;
7
+ requiresIdempotency: boolean;
8
+ requiredApprovals: readonly string[];
9
+ resourcePrefixes: readonly string[];
10
+ }
11
+ export declare class IntentSchemaCatalog {
12
+ private readonly definitions;
13
+ get(operation: IntentOperation): IntentSchemaDefinition;
14
+ list(): IntentSchemaDefinition[];
15
+ validate(intent: IntentContract): void;
16
+ }
17
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;AAC1D,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,cAAc,GAAG,WAAW,CAAC;AAE9G,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,eAAe,CAAC;IAC3B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AA6DD,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgF;IAE5G,GAAG,CAAC,SAAS,EAAE,eAAe,GAAG,sBAAsB,CAItD;IAED,IAAI,IAAI,sBAAsB,EAAE,CAE/B;IAED,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAWrC;CACD","sourcesContent":["import type { IntentContract } from \"@asm-agent/contracts\";\n\nexport type IntentOperation = IntentContract[\"operation\"];\nexport type ConsequenceClass = \"none\" | \"local_mutation\" | \"external_mutation\" | \"irreversible\" | \"financial\";\n\nexport interface IntentSchemaDefinition {\n\toperation: IntentOperation;\n\tconsequence: ConsequenceClass;\n\trequiresIdempotency: boolean;\n\trequiredApprovals: readonly string[];\n\tresourcePrefixes: readonly string[];\n}\n\nconst DEFINITIONS: readonly IntentSchemaDefinition[] = [\n\t{\n\t\toperation: \"diagnose\",\n\t\tconsequence: \"none\",\n\t\trequiresIdempotency: false,\n\t\trequiredApprovals: [],\n\t\tresourcePrefixes: [\"system:\", \"log:\"],\n\t},\n\t{\n\t\toperation: \"read\",\n\t\tconsequence: \"none\",\n\t\trequiresIdempotency: false,\n\t\trequiredApprovals: [],\n\t\tresourcePrefixes: [\"file:\", \"db:\", \"url:\"],\n\t},\n\t{\n\t\toperation: \"write\",\n\t\tconsequence: \"local_mutation\",\n\t\trequiresIdempotency: false,\n\t\trequiredApprovals: [\"local_write\"],\n\t\tresourcePrefixes: [\"file:\", \"db:\"],\n\t},\n\t{\n\t\toperation: \"execute\",\n\t\tconsequence: \"external_mutation\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"command_execution\"],\n\t\tresourcePrefixes: [\"command:\", \"tool:\"],\n\t},\n\t{\n\t\toperation: \"communicate\",\n\t\tconsequence: \"external_mutation\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"external_communication\"],\n\t\tresourcePrefixes: [\"recipient:\", \"channel:\"],\n\t},\n\t{\n\t\toperation: \"deploy\",\n\t\tconsequence: \"external_mutation\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"deployment\"],\n\t\tresourcePrefixes: [\"environment:\", \"service:\"],\n\t},\n\t{\n\t\toperation: \"purchase\",\n\t\tconsequence: \"financial\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"purchase\"],\n\t\tresourcePrefixes: [\"merchant:\", \"product:\"],\n\t},\n\t{\n\t\toperation: \"delete\",\n\t\tconsequence: \"irreversible\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"destructive_action\"],\n\t\tresourcePrefixes: [\"file:\", \"db:\", \"remote:\"],\n\t},\n] as const;\n\nexport class IntentSchemaCatalog {\n\tprivate readonly definitions = new Map(DEFINITIONS.map((definition) => [definition.operation, definition]));\n\n\tget(operation: IntentOperation): IntentSchemaDefinition {\n\t\tconst definition = this.definitions.get(operation);\n\t\tif (!definition) throw new Error(`Unknown intent operation: ${operation}`);\n\t\treturn definition;\n\t}\n\n\tlist(): IntentSchemaDefinition[] {\n\t\treturn [...this.definitions.values()];\n\t}\n\n\tvalidate(intent: IntentContract): void {\n\t\tconst definition = this.get(intent.operation);\n\t\tif (definition.requiresIdempotency && !intent.idempotencyKey) {\n\t\t\tthrow new Error(`${intent.operation} intent requires an idempotency key`);\n\t\t}\n\t\tif (\n\t\t\tintent.externalEffect !== (definition.consequence !== \"none\" && definition.consequence !== \"local_mutation\")\n\t\t) {\n\t\t\tthrow new Error(`${intent.operation} intent has inconsistent externalEffect`);\n\t\t}\n\t\tif (intent.resources.length === 0) throw new Error(\"Intent requires at least one resource\");\n\t}\n}\n"]}
@@ -0,0 +1,82 @@
1
+ const DEFINITIONS = [
2
+ {
3
+ operation: "diagnose",
4
+ consequence: "none",
5
+ requiresIdempotency: false,
6
+ requiredApprovals: [],
7
+ resourcePrefixes: ["system:", "log:"],
8
+ },
9
+ {
10
+ operation: "read",
11
+ consequence: "none",
12
+ requiresIdempotency: false,
13
+ requiredApprovals: [],
14
+ resourcePrefixes: ["file:", "db:", "url:"],
15
+ },
16
+ {
17
+ operation: "write",
18
+ consequence: "local_mutation",
19
+ requiresIdempotency: false,
20
+ requiredApprovals: ["local_write"],
21
+ resourcePrefixes: ["file:", "db:"],
22
+ },
23
+ {
24
+ operation: "execute",
25
+ consequence: "external_mutation",
26
+ requiresIdempotency: true,
27
+ requiredApprovals: ["command_execution"],
28
+ resourcePrefixes: ["command:", "tool:"],
29
+ },
30
+ {
31
+ operation: "communicate",
32
+ consequence: "external_mutation",
33
+ requiresIdempotency: true,
34
+ requiredApprovals: ["external_communication"],
35
+ resourcePrefixes: ["recipient:", "channel:"],
36
+ },
37
+ {
38
+ operation: "deploy",
39
+ consequence: "external_mutation",
40
+ requiresIdempotency: true,
41
+ requiredApprovals: ["deployment"],
42
+ resourcePrefixes: ["environment:", "service:"],
43
+ },
44
+ {
45
+ operation: "purchase",
46
+ consequence: "financial",
47
+ requiresIdempotency: true,
48
+ requiredApprovals: ["purchase"],
49
+ resourcePrefixes: ["merchant:", "product:"],
50
+ },
51
+ {
52
+ operation: "delete",
53
+ consequence: "irreversible",
54
+ requiresIdempotency: true,
55
+ requiredApprovals: ["destructive_action"],
56
+ resourcePrefixes: ["file:", "db:", "remote:"],
57
+ },
58
+ ];
59
+ export class IntentSchemaCatalog {
60
+ definitions = new Map(DEFINITIONS.map((definition) => [definition.operation, definition]));
61
+ get(operation) {
62
+ const definition = this.definitions.get(operation);
63
+ if (!definition)
64
+ throw new Error(`Unknown intent operation: ${operation}`);
65
+ return definition;
66
+ }
67
+ list() {
68
+ return [...this.definitions.values()];
69
+ }
70
+ validate(intent) {
71
+ const definition = this.get(intent.operation);
72
+ if (definition.requiresIdempotency && !intent.idempotencyKey) {
73
+ throw new Error(`${intent.operation} intent requires an idempotency key`);
74
+ }
75
+ if (intent.externalEffect !== (definition.consequence !== "none" && definition.consequence !== "local_mutation")) {
76
+ throw new Error(`${intent.operation} intent has inconsistent externalEffect`);
77
+ }
78
+ if (intent.resources.length === 0)
79
+ throw new Error("Intent requires at least one resource");
80
+ }
81
+ }
82
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IntentContract } from \"@asm-agent/contracts\";\n\nexport type IntentOperation = IntentContract[\"operation\"];\nexport type ConsequenceClass = \"none\" | \"local_mutation\" | \"external_mutation\" | \"irreversible\" | \"financial\";\n\nexport interface IntentSchemaDefinition {\n\toperation: IntentOperation;\n\tconsequence: ConsequenceClass;\n\trequiresIdempotency: boolean;\n\trequiredApprovals: readonly string[];\n\tresourcePrefixes: readonly string[];\n}\n\nconst DEFINITIONS: readonly IntentSchemaDefinition[] = [\n\t{\n\t\toperation: \"diagnose\",\n\t\tconsequence: \"none\",\n\t\trequiresIdempotency: false,\n\t\trequiredApprovals: [],\n\t\tresourcePrefixes: [\"system:\", \"log:\"],\n\t},\n\t{\n\t\toperation: \"read\",\n\t\tconsequence: \"none\",\n\t\trequiresIdempotency: false,\n\t\trequiredApprovals: [],\n\t\tresourcePrefixes: [\"file:\", \"db:\", \"url:\"],\n\t},\n\t{\n\t\toperation: \"write\",\n\t\tconsequence: \"local_mutation\",\n\t\trequiresIdempotency: false,\n\t\trequiredApprovals: [\"local_write\"],\n\t\tresourcePrefixes: [\"file:\", \"db:\"],\n\t},\n\t{\n\t\toperation: \"execute\",\n\t\tconsequence: \"external_mutation\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"command_execution\"],\n\t\tresourcePrefixes: [\"command:\", \"tool:\"],\n\t},\n\t{\n\t\toperation: \"communicate\",\n\t\tconsequence: \"external_mutation\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"external_communication\"],\n\t\tresourcePrefixes: [\"recipient:\", \"channel:\"],\n\t},\n\t{\n\t\toperation: \"deploy\",\n\t\tconsequence: \"external_mutation\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"deployment\"],\n\t\tresourcePrefixes: [\"environment:\", \"service:\"],\n\t},\n\t{\n\t\toperation: \"purchase\",\n\t\tconsequence: \"financial\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"purchase\"],\n\t\tresourcePrefixes: [\"merchant:\", \"product:\"],\n\t},\n\t{\n\t\toperation: \"delete\",\n\t\tconsequence: \"irreversible\",\n\t\trequiresIdempotency: true,\n\t\trequiredApprovals: [\"destructive_action\"],\n\t\tresourcePrefixes: [\"file:\", \"db:\", \"remote:\"],\n\t},\n] as const;\n\nexport class IntentSchemaCatalog {\n\tprivate readonly definitions = new Map(DEFINITIONS.map((definition) => [definition.operation, definition]));\n\n\tget(operation: IntentOperation): IntentSchemaDefinition {\n\t\tconst definition = this.definitions.get(operation);\n\t\tif (!definition) throw new Error(`Unknown intent operation: ${operation}`);\n\t\treturn definition;\n\t}\n\n\tlist(): IntentSchemaDefinition[] {\n\t\treturn [...this.definitions.values()];\n\t}\n\n\tvalidate(intent: IntentContract): void {\n\t\tconst definition = this.get(intent.operation);\n\t\tif (definition.requiresIdempotency && !intent.idempotencyKey) {\n\t\t\tthrow new Error(`${intent.operation} intent requires an idempotency key`);\n\t\t}\n\t\tif (\n\t\t\tintent.externalEffect !== (definition.consequence !== \"none\" && definition.consequence !== \"local_mutation\")\n\t\t) {\n\t\t\tthrow new Error(`${intent.operation} intent has inconsistent externalEffect`);\n\t\t}\n\t\tif (intent.resources.length === 0) throw new Error(\"Intent requires at least one resource\");\n\t}\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { IntentContract, PolicyContract } from "@asm-agent/contracts";
2
+ import type { PostgresStore } from "@asm-agent/postgres";
3
+ export interface GovernedExecutionResult<T> {
4
+ status: "executed" | "replayed";
5
+ value: T;
6
+ }
7
+ export declare class GovernedExecutor {
8
+ private readonly database;
9
+ private readonly policy;
10
+ private readonly repository;
11
+ constructor(database: PostgresStore);
12
+ execute<T>(intent: IntentContract, decision: PolicyContract, handler: () => Promise<T>): Promise<GovernedExecutionResult<T>>;
13
+ }
14
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACzC,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;IAChC,KAAK,EAAE,CAAC,CAAC;CACT;AAED,qBAAa,gBAAgB;IAIhB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAHrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAElD,YAA6B,QAAQ,EAAE,aAAa,EAEnD;IAEK,OAAO,CAAC,CAAC,EACd,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAoCrC;CACD","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { IntentContract, PolicyContract } from \"@asm-agent/contracts\";\nimport type { PostgresStore } from \"@asm-agent/postgres\";\nimport { IntentPolicyEngine } from \"./policy.js\";\nimport { GovernanceRepository } from \"./repository.js\";\n\nexport interface GovernedExecutionResult<T> {\n\tstatus: \"executed\" | \"replayed\";\n\tvalue: T;\n}\n\nexport class GovernedExecutor {\n\tprivate readonly policy = new IntentPolicyEngine();\n\tprivate readonly repository: GovernanceRepository;\n\n\tconstructor(private readonly database: PostgresStore) {\n\t\tthis.repository = new GovernanceRepository(database);\n\t}\n\n\tasync execute<T>(\n\t\tintent: IntentContract,\n\t\tdecision: PolicyContract,\n\t\thandler: () => Promise<T>,\n\t): Promise<GovernedExecutionResult<T>> {\n\t\tif (decision.outcome === \"deny\") throw new Error(\"Policy denied governed execution\");\n\t\tif (!(await this.repository.hasApprovals(decision, intent, (type) => this.policy.scopeHash(intent, type)))) {\n\t\t\tthrow new Error(\"Required approval is missing, expired, consumed, or outside scope\");\n\t\t}\n\t\tconst key = intent.idempotencyKey ?? (intent.externalEffect ? undefined : intent.intentId);\n\t\tif (!key) throw new Error(\"Consequential execution requires an idempotency key\");\n\t\tconst scope = `governed:${intent.operation}`;\n\t\tconst requestHash = createHash(\"sha256\").update(stableJson(intent)).digest(\"hex\");\n\t\tconst started = await this.database.startIdempotent({ scope, key, requestHash });\n\t\tif (started.status === \"replayed\") return { status: \"replayed\", value: started.response as T };\n\t\tif (started.status === \"in_progress\") throw new Error(\"Governed execution is already in progress\");\n\t\tconst executionId = randomUUID();\n\t\tawait this.repository.recordExecutionStarted({\n\t\t\tid: executionId,\n\t\t\tintentId: intent.intentId,\n\t\t\tdecisionId: decision.decisionId,\n\t\t\tscope,\n\t\t\tkey,\n\t\t\trequestHash,\n\t\t});\n\t\tawait this.repository.consumeApprovals(decision.decisionId);\n\t\ttry {\n\t\t\tconst value = await handler();\n\t\t\tawait this.database.completeIdempotent(scope, key, value);\n\t\t\tawait this.repository.finishExecution(executionId, \"completed\", value);\n\t\t\treturn { status: \"executed\", value };\n\t\t} catch (error) {\n\t\t\tconst detail = { message: error instanceof Error ? error.message : String(error) };\n\t\t\tawait this.repository.finishExecution(\n\t\t\t\texecutionId,\n\t\t\t\tintent.externalEffect ? \"unknown_external_outcome\" : \"failed\",\n\t\t\t\tdetail,\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nfunction stableJson(value: IntentContract): string {\n\treturn JSON.stringify({\n\t\theader: value.header,\n\t\tintentId: value.intentId,\n\t\toperation: value.operation,\n\t\tresources: [...value.resources].sort(),\n\t\texternalEffect: value.externalEffect,\n\t\tidempotencyKey: value.idempotencyKey,\n\t\trequestedAt: value.requestedAt,\n\t\targuments: value.arguments,\n\t});\n}\n"]}
@@ -0,0 +1,63 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { IntentPolicyEngine } from "./policy.js";
3
+ import { GovernanceRepository } from "./repository.js";
4
+ export class GovernedExecutor {
5
+ database;
6
+ policy = new IntentPolicyEngine();
7
+ repository;
8
+ constructor(database) {
9
+ this.database = database;
10
+ this.repository = new GovernanceRepository(database);
11
+ }
12
+ async execute(intent, decision, handler) {
13
+ if (decision.outcome === "deny")
14
+ throw new Error("Policy denied governed execution");
15
+ if (!(await this.repository.hasApprovals(decision, intent, (type) => this.policy.scopeHash(intent, type)))) {
16
+ throw new Error("Required approval is missing, expired, consumed, or outside scope");
17
+ }
18
+ const key = intent.idempotencyKey ?? (intent.externalEffect ? undefined : intent.intentId);
19
+ if (!key)
20
+ throw new Error("Consequential execution requires an idempotency key");
21
+ const scope = `governed:${intent.operation}`;
22
+ const requestHash = createHash("sha256").update(stableJson(intent)).digest("hex");
23
+ const started = await this.database.startIdempotent({ scope, key, requestHash });
24
+ if (started.status === "replayed")
25
+ return { status: "replayed", value: started.response };
26
+ if (started.status === "in_progress")
27
+ throw new Error("Governed execution is already in progress");
28
+ const executionId = randomUUID();
29
+ await this.repository.recordExecutionStarted({
30
+ id: executionId,
31
+ intentId: intent.intentId,
32
+ decisionId: decision.decisionId,
33
+ scope,
34
+ key,
35
+ requestHash,
36
+ });
37
+ await this.repository.consumeApprovals(decision.decisionId);
38
+ try {
39
+ const value = await handler();
40
+ await this.database.completeIdempotent(scope, key, value);
41
+ await this.repository.finishExecution(executionId, "completed", value);
42
+ return { status: "executed", value };
43
+ }
44
+ catch (error) {
45
+ const detail = { message: error instanceof Error ? error.message : String(error) };
46
+ await this.repository.finishExecution(executionId, intent.externalEffect ? "unknown_external_outcome" : "failed", detail);
47
+ throw error;
48
+ }
49
+ }
50
+ }
51
+ function stableJson(value) {
52
+ return JSON.stringify({
53
+ header: value.header,
54
+ intentId: value.intentId,
55
+ operation: value.operation,
56
+ resources: [...value.resources].sort(),
57
+ externalEffect: value.externalEffect,
58
+ idempotencyKey: value.idempotencyKey,
59
+ requestedAt: value.requestedAt,
60
+ arguments: value.arguments,
61
+ });
62
+ }
63
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { IntentContract, PolicyContract } from \"@asm-agent/contracts\";\nimport type { PostgresStore } from \"@asm-agent/postgres\";\nimport { IntentPolicyEngine } from \"./policy.js\";\nimport { GovernanceRepository } from \"./repository.js\";\n\nexport interface GovernedExecutionResult<T> {\n\tstatus: \"executed\" | \"replayed\";\n\tvalue: T;\n}\n\nexport class GovernedExecutor {\n\tprivate readonly policy = new IntentPolicyEngine();\n\tprivate readonly repository: GovernanceRepository;\n\n\tconstructor(private readonly database: PostgresStore) {\n\t\tthis.repository = new GovernanceRepository(database);\n\t}\n\n\tasync execute<T>(\n\t\tintent: IntentContract,\n\t\tdecision: PolicyContract,\n\t\thandler: () => Promise<T>,\n\t): Promise<GovernedExecutionResult<T>> {\n\t\tif (decision.outcome === \"deny\") throw new Error(\"Policy denied governed execution\");\n\t\tif (!(await this.repository.hasApprovals(decision, intent, (type) => this.policy.scopeHash(intent, type)))) {\n\t\t\tthrow new Error(\"Required approval is missing, expired, consumed, or outside scope\");\n\t\t}\n\t\tconst key = intent.idempotencyKey ?? (intent.externalEffect ? undefined : intent.intentId);\n\t\tif (!key) throw new Error(\"Consequential execution requires an idempotency key\");\n\t\tconst scope = `governed:${intent.operation}`;\n\t\tconst requestHash = createHash(\"sha256\").update(stableJson(intent)).digest(\"hex\");\n\t\tconst started = await this.database.startIdempotent({ scope, key, requestHash });\n\t\tif (started.status === \"replayed\") return { status: \"replayed\", value: started.response as T };\n\t\tif (started.status === \"in_progress\") throw new Error(\"Governed execution is already in progress\");\n\t\tconst executionId = randomUUID();\n\t\tawait this.repository.recordExecutionStarted({\n\t\t\tid: executionId,\n\t\t\tintentId: intent.intentId,\n\t\t\tdecisionId: decision.decisionId,\n\t\t\tscope,\n\t\t\tkey,\n\t\t\trequestHash,\n\t\t});\n\t\tawait this.repository.consumeApprovals(decision.decisionId);\n\t\ttry {\n\t\t\tconst value = await handler();\n\t\t\tawait this.database.completeIdempotent(scope, key, value);\n\t\t\tawait this.repository.finishExecution(executionId, \"completed\", value);\n\t\t\treturn { status: \"executed\", value };\n\t\t} catch (error) {\n\t\t\tconst detail = { message: error instanceof Error ? error.message : String(error) };\n\t\t\tawait this.repository.finishExecution(\n\t\t\t\texecutionId,\n\t\t\t\tintent.externalEffect ? \"unknown_external_outcome\" : \"failed\",\n\t\t\t\tdetail,\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nfunction stableJson(value: IntentContract): string {\n\treturn JSON.stringify({\n\t\theader: value.header,\n\t\tintentId: value.intentId,\n\t\toperation: value.operation,\n\t\tresources: [...value.resources].sort(),\n\t\texternalEffect: value.externalEffect,\n\t\tidempotencyKey: value.idempotencyKey,\n\t\trequestedAt: value.requestedAt,\n\t\targuments: value.arguments,\n\t});\n}\n"]}
@@ -0,0 +1,9 @@
1
+ export { type ConsequenceClass, type IntentOperation, IntentSchemaCatalog, type IntentSchemaDefinition, } from "./catalog.js";
2
+ export { type GovernedExecutionResult, GovernedExecutor } from "./executor.js";
3
+ export { type ParsedIntentResult, type ParseIntentInput, type StructuredIntentModel, VersionedIntentParser, } from "./parser.js";
4
+ export { IntentPolicyEngine, type PolicyContext } from "./policy.js";
5
+ export { GovernanceRepository } from "./repository.js";
6
+ export { type AuthenticatedModel, AuthenticatedModelRouter, type LoginProvider, type ModelRoute, type RoutingPurpose, } from "./router.js";
7
+ export { GovernanceService } from "./service.js";
8
+ export { classifyDefaultTool, DEFAULT_CONSEQUENTIAL_TOOL_NAMES, type DefaultConsequentialToolName, type ToolIntentClassification, } from "./tool-policy.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,mBAAmB,EACnB,KAAK,sBAAsB,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EACN,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,qBAAqB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACN,KAAK,kBAAkB,EACvB,wBAAwB,EACxB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,cAAc,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EACN,mBAAmB,EACnB,gCAAgC,EAChC,KAAK,4BAA4B,EACjC,KAAK,wBAAwB,GAC7B,MAAM,kBAAkB,CAAC","sourcesContent":["export {\n\ttype ConsequenceClass,\n\ttype IntentOperation,\n\tIntentSchemaCatalog,\n\ttype IntentSchemaDefinition,\n} from \"./catalog.js\";\nexport { type GovernedExecutionResult, GovernedExecutor } from \"./executor.js\";\nexport {\n\ttype ParsedIntentResult,\n\ttype ParseIntentInput,\n\ttype StructuredIntentModel,\n\tVersionedIntentParser,\n} from \"./parser.js\";\nexport { IntentPolicyEngine, type PolicyContext } from \"./policy.js\";\nexport { GovernanceRepository } from \"./repository.js\";\nexport {\n\ttype AuthenticatedModel,\n\tAuthenticatedModelRouter,\n\ttype LoginProvider,\n\ttype ModelRoute,\n\ttype RoutingPurpose,\n} from \"./router.js\";\nexport { GovernanceService } from \"./service.js\";\nexport {\n\tclassifyDefaultTool,\n\tDEFAULT_CONSEQUENTIAL_TOOL_NAMES,\n\ttype DefaultConsequentialToolName,\n\ttype ToolIntentClassification,\n} from \"./tool-policy.js\";\n"]}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { IntentSchemaCatalog, } from "./catalog.js";
2
+ export { GovernedExecutor } from "./executor.js";
3
+ export { VersionedIntentParser, } from "./parser.js";
4
+ export { IntentPolicyEngine } from "./policy.js";
5
+ export { GovernanceRepository } from "./repository.js";
6
+ export { AuthenticatedModelRouter, } from "./router.js";
7
+ export { GovernanceService } from "./service.js";
8
+ export { classifyDefaultTool, DEFAULT_CONSEQUENTIAL_TOOL_NAMES, } from "./tool-policy.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,mBAAmB,GAEnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAgC,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAIN,qBAAqB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAsB,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAEN,wBAAwB,GAIxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EACN,mBAAmB,EACnB,gCAAgC,GAGhC,MAAM,kBAAkB,CAAC","sourcesContent":["export {\n\ttype ConsequenceClass,\n\ttype IntentOperation,\n\tIntentSchemaCatalog,\n\ttype IntentSchemaDefinition,\n} from \"./catalog.js\";\nexport { type GovernedExecutionResult, GovernedExecutor } from \"./executor.js\";\nexport {\n\ttype ParsedIntentResult,\n\ttype ParseIntentInput,\n\ttype StructuredIntentModel,\n\tVersionedIntentParser,\n} from \"./parser.js\";\nexport { IntentPolicyEngine, type PolicyContext } from \"./policy.js\";\nexport { GovernanceRepository } from \"./repository.js\";\nexport {\n\ttype AuthenticatedModel,\n\tAuthenticatedModelRouter,\n\ttype LoginProvider,\n\ttype ModelRoute,\n\ttype RoutingPurpose,\n} from \"./router.js\";\nexport { GovernanceService } from \"./service.js\";\nexport {\n\tclassifyDefaultTool,\n\tDEFAULT_CONSEQUENTIAL_TOOL_NAMES,\n\ttype DefaultConsequentialToolName,\n\ttype ToolIntentClassification,\n} from \"./tool-policy.js\";\n"]}
@@ -0,0 +1,30 @@
1
+ import { type IntentContract } from "@asm-agent/contracts";
2
+ import { IntentSchemaCatalog } from "./catalog.js";
3
+ import type { AuthenticatedModelRouter, LoginProvider, ModelRoute } from "./router.js";
4
+ export interface ParseIntentInput {
5
+ text: string;
6
+ resources?: string[];
7
+ arguments?: Record<string, unknown>;
8
+ idempotencyKey?: string;
9
+ now?: Date;
10
+ intentId?: string;
11
+ preferredProvider?: LoginProvider;
12
+ }
13
+ export interface ParsedIntentResult {
14
+ intent: IntentContract;
15
+ parserId: "deterministic-v1" | "authenticated-model-v1";
16
+ parserVersion: "1";
17
+ route?: ModelRoute;
18
+ }
19
+ export interface StructuredIntentModel {
20
+ parse(route: ModelRoute, input: ParseIntentInput): Promise<IntentContract>;
21
+ }
22
+ export declare class VersionedIntentParser {
23
+ private readonly catalog;
24
+ private readonly router?;
25
+ private readonly model?;
26
+ constructor(catalog?: IntentSchemaCatalog, router?: AuthenticatedModelRouter | undefined, model?: StructuredIntentModel | undefined);
27
+ parse(input: ParseIntentInput): Promise<ParsedIntentResult>;
28
+ private buildDeterministic;
29
+ }
30
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAwB,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEvF,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,aAAa,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,kBAAkB,GAAG,wBAAwB,CAAC;IACxD,aAAa,EAAE,GAAG,CAAC;IACnB,KAAK,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACrC,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC3E;AAED,qBAAa,qBAAqB;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAHxB,YACkB,OAAO,sBAA4B,EACnC,MAAM,CAAC,sCAA0B,EACjC,KAAK,CAAC,mCAAuB,EAC3C;IAEE,KAAK,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAUhE;IAED,OAAO,CAAC,kBAAkB;CAgB1B","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { assertContract, type IntentContract } from \"@asm-agent/contracts\";\nimport { type IntentOperation, IntentSchemaCatalog } from \"./catalog.js\";\nimport type { AuthenticatedModelRouter, LoginProvider, ModelRoute } from \"./router.js\";\n\nexport interface ParseIntentInput {\n\ttext: string;\n\tresources?: string[];\n\targuments?: Record<string, unknown>;\n\tidempotencyKey?: string;\n\tnow?: Date;\n\tintentId?: string;\n\tpreferredProvider?: LoginProvider;\n}\n\nexport interface ParsedIntentResult {\n\tintent: IntentContract;\n\tparserId: \"deterministic-v1\" | \"authenticated-model-v1\";\n\tparserVersion: \"1\";\n\troute?: ModelRoute;\n}\n\nexport interface StructuredIntentModel {\n\tparse(route: ModelRoute, input: ParseIntentInput): Promise<IntentContract>;\n}\n\nexport class VersionedIntentParser {\n\tconstructor(\n\t\tprivate readonly catalog = new IntentSchemaCatalog(),\n\t\tprivate readonly router?: AuthenticatedModelRouter,\n\t\tprivate readonly model?: StructuredIntentModel,\n\t) {}\n\n\tasync parse(input: ParseIntentInput): Promise<ParsedIntentResult> {\n\t\tconst deterministic = deterministicOperation(input.text);\n\t\tif (deterministic) return this.buildDeterministic(input, deterministic);\n\t\tif (!this.router || !this.model)\n\t\t\tthrow new Error(\"Intent is ambiguous and no authenticated model parser is configured\");\n\t\tconst route = this.router.route(\"intent_parse\", input.preferredProvider);\n\t\tconst intent = await this.model.parse(route, input);\n\t\tassertContract(\"intent\", intent);\n\t\tthis.catalog.validate(intent);\n\t\treturn { intent, parserId: \"authenticated-model-v1\", parserVersion: \"1\", route };\n\t}\n\n\tprivate buildDeterministic(input: ParseIntentInput, operation: IntentOperation): ParsedIntentResult {\n\t\tconst definition = this.catalog.get(operation);\n\t\tconst intent: IntentContract = {\n\t\t\theader: { namespace: \"ai.aletheion.asm\", major: 1, minor: 1 },\n\t\t\tintentId: input.intentId ?? randomUUID(),\n\t\t\toperation,\n\t\t\tresources: input.resources ?? inferResources(input.text, operation),\n\t\t\texternalEffect: definition.consequence !== \"none\" && definition.consequence !== \"local_mutation\",\n\t\t\tidempotencyKey: input.idempotencyKey,\n\t\t\trequestedAt: (input.now ?? new Date()).toISOString(),\n\t\t\targuments: input.arguments ?? { text: input.text },\n\t\t};\n\t\tassertContract(\"intent\", intent);\n\t\tthis.catalog.validate(intent);\n\t\treturn { intent, parserId: \"deterministic-v1\", parserVersion: \"1\" };\n\t}\n}\n\nfunction deterministicOperation(text: string): IntentOperation | undefined {\n\tconst normalized = text.trim().toLowerCase();\n\tif (/\\b(delete|remove|erase|drop|apagar|deletar|excluir|remover)\\b/.test(normalized)) return \"delete\";\n\tif (/\\b(buy|purchase|checkout|comprar|pagar|adquirir)\\b/.test(normalized)) return \"purchase\";\n\tif (/\\b(deploy|release|publish|implantar|publicar)\\b/.test(normalized)) return \"deploy\";\n\tif (/\\b(send|email|message|notify|enviar|avisar|comunicar)\\b/.test(normalized)) return \"communicate\";\n\tif (/\\b(run|execute|restart|executar|rodar|reiniciar)\\b/.test(normalized)) return \"execute\";\n\tif (/\\b(edit|write|change|create|editar|alterar|criar|escrever)\\b/.test(normalized)) return \"write\";\n\tif (/\\b(read|show|list|inspect|ler|mostrar|listar|ver)\\b/.test(normalized)) return \"read\";\n\tif (/\\b(diagnose|debug|investigate|diagnosticar|depurar|investigar)\\b/.test(normalized)) return \"diagnose\";\n\treturn undefined;\n}\n\nfunction inferResources(text: string, operation: IntentOperation): string[] {\n\treturn [`${operation}:${text.trim().slice(0, 160) || \"unspecified\"}`];\n}\n"]}
package/dist/parser.js ADDED
@@ -0,0 +1,65 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { assertContract } from "@asm-agent/contracts";
3
+ import { IntentSchemaCatalog } from "./catalog.js";
4
+ export class VersionedIntentParser {
5
+ catalog;
6
+ router;
7
+ model;
8
+ constructor(catalog = new IntentSchemaCatalog(), router, model) {
9
+ this.catalog = catalog;
10
+ this.router = router;
11
+ this.model = model;
12
+ }
13
+ async parse(input) {
14
+ const deterministic = deterministicOperation(input.text);
15
+ if (deterministic)
16
+ return this.buildDeterministic(input, deterministic);
17
+ if (!this.router || !this.model)
18
+ throw new Error("Intent is ambiguous and no authenticated model parser is configured");
19
+ const route = this.router.route("intent_parse", input.preferredProvider);
20
+ const intent = await this.model.parse(route, input);
21
+ assertContract("intent", intent);
22
+ this.catalog.validate(intent);
23
+ return { intent, parserId: "authenticated-model-v1", parserVersion: "1", route };
24
+ }
25
+ buildDeterministic(input, operation) {
26
+ const definition = this.catalog.get(operation);
27
+ const intent = {
28
+ header: { namespace: "ai.aletheion.asm", major: 1, minor: 1 },
29
+ intentId: input.intentId ?? randomUUID(),
30
+ operation,
31
+ resources: input.resources ?? inferResources(input.text, operation),
32
+ externalEffect: definition.consequence !== "none" && definition.consequence !== "local_mutation",
33
+ idempotencyKey: input.idempotencyKey,
34
+ requestedAt: (input.now ?? new Date()).toISOString(),
35
+ arguments: input.arguments ?? { text: input.text },
36
+ };
37
+ assertContract("intent", intent);
38
+ this.catalog.validate(intent);
39
+ return { intent, parserId: "deterministic-v1", parserVersion: "1" };
40
+ }
41
+ }
42
+ function deterministicOperation(text) {
43
+ const normalized = text.trim().toLowerCase();
44
+ if (/\b(delete|remove|erase|drop|apagar|deletar|excluir|remover)\b/.test(normalized))
45
+ return "delete";
46
+ if (/\b(buy|purchase|checkout|comprar|pagar|adquirir)\b/.test(normalized))
47
+ return "purchase";
48
+ if (/\b(deploy|release|publish|implantar|publicar)\b/.test(normalized))
49
+ return "deploy";
50
+ if (/\b(send|email|message|notify|enviar|avisar|comunicar)\b/.test(normalized))
51
+ return "communicate";
52
+ if (/\b(run|execute|restart|executar|rodar|reiniciar)\b/.test(normalized))
53
+ return "execute";
54
+ if (/\b(edit|write|change|create|editar|alterar|criar|escrever)\b/.test(normalized))
55
+ return "write";
56
+ if (/\b(read|show|list|inspect|ler|mostrar|listar|ver)\b/.test(normalized))
57
+ return "read";
58
+ if (/\b(diagnose|debug|investigate|diagnosticar|depurar|investigar)\b/.test(normalized))
59
+ return "diagnose";
60
+ return undefined;
61
+ }
62
+ function inferResources(text, operation) {
63
+ return [`${operation}:${text.trim().slice(0, 160) || "unspecified"}`];
64
+ }
65
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { assertContract, type IntentContract } from \"@asm-agent/contracts\";\nimport { type IntentOperation, IntentSchemaCatalog } from \"./catalog.js\";\nimport type { AuthenticatedModelRouter, LoginProvider, ModelRoute } from \"./router.js\";\n\nexport interface ParseIntentInput {\n\ttext: string;\n\tresources?: string[];\n\targuments?: Record<string, unknown>;\n\tidempotencyKey?: string;\n\tnow?: Date;\n\tintentId?: string;\n\tpreferredProvider?: LoginProvider;\n}\n\nexport interface ParsedIntentResult {\n\tintent: IntentContract;\n\tparserId: \"deterministic-v1\" | \"authenticated-model-v1\";\n\tparserVersion: \"1\";\n\troute?: ModelRoute;\n}\n\nexport interface StructuredIntentModel {\n\tparse(route: ModelRoute, input: ParseIntentInput): Promise<IntentContract>;\n}\n\nexport class VersionedIntentParser {\n\tconstructor(\n\t\tprivate readonly catalog = new IntentSchemaCatalog(),\n\t\tprivate readonly router?: AuthenticatedModelRouter,\n\t\tprivate readonly model?: StructuredIntentModel,\n\t) {}\n\n\tasync parse(input: ParseIntentInput): Promise<ParsedIntentResult> {\n\t\tconst deterministic = deterministicOperation(input.text);\n\t\tif (deterministic) return this.buildDeterministic(input, deterministic);\n\t\tif (!this.router || !this.model)\n\t\t\tthrow new Error(\"Intent is ambiguous and no authenticated model parser is configured\");\n\t\tconst route = this.router.route(\"intent_parse\", input.preferredProvider);\n\t\tconst intent = await this.model.parse(route, input);\n\t\tassertContract(\"intent\", intent);\n\t\tthis.catalog.validate(intent);\n\t\treturn { intent, parserId: \"authenticated-model-v1\", parserVersion: \"1\", route };\n\t}\n\n\tprivate buildDeterministic(input: ParseIntentInput, operation: IntentOperation): ParsedIntentResult {\n\t\tconst definition = this.catalog.get(operation);\n\t\tconst intent: IntentContract = {\n\t\t\theader: { namespace: \"ai.aletheion.asm\", major: 1, minor: 1 },\n\t\t\tintentId: input.intentId ?? randomUUID(),\n\t\t\toperation,\n\t\t\tresources: input.resources ?? inferResources(input.text, operation),\n\t\t\texternalEffect: definition.consequence !== \"none\" && definition.consequence !== \"local_mutation\",\n\t\t\tidempotencyKey: input.idempotencyKey,\n\t\t\trequestedAt: (input.now ?? new Date()).toISOString(),\n\t\t\targuments: input.arguments ?? { text: input.text },\n\t\t};\n\t\tassertContract(\"intent\", intent);\n\t\tthis.catalog.validate(intent);\n\t\treturn { intent, parserId: \"deterministic-v1\", parserVersion: \"1\" };\n\t}\n}\n\nfunction deterministicOperation(text: string): IntentOperation | undefined {\n\tconst normalized = text.trim().toLowerCase();\n\tif (/\\b(delete|remove|erase|drop|apagar|deletar|excluir|remover)\\b/.test(normalized)) return \"delete\";\n\tif (/\\b(buy|purchase|checkout|comprar|pagar|adquirir)\\b/.test(normalized)) return \"purchase\";\n\tif (/\\b(deploy|release|publish|implantar|publicar)\\b/.test(normalized)) return \"deploy\";\n\tif (/\\b(send|email|message|notify|enviar|avisar|comunicar)\\b/.test(normalized)) return \"communicate\";\n\tif (/\\b(run|execute|restart|executar|rodar|reiniciar)\\b/.test(normalized)) return \"execute\";\n\tif (/\\b(edit|write|change|create|editar|alterar|criar|escrever)\\b/.test(normalized)) return \"write\";\n\tif (/\\b(read|show|list|inspect|ler|mostrar|listar|ver)\\b/.test(normalized)) return \"read\";\n\tif (/\\b(diagnose|debug|investigate|diagnosticar|depurar|investigar)\\b/.test(normalized)) return \"diagnose\";\n\treturn undefined;\n}\n\nfunction inferResources(text: string, operation: IntentOperation): string[] {\n\treturn [`${operation}:${text.trim().slice(0, 160) || \"unspecified\"}`];\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import type { IntentContract, PolicyContract } from "@asm-agent/contracts";
2
+ import { IntentSchemaCatalog } from "./catalog.js";
3
+ export interface PolicyContext {
4
+ trustedOwnerProject: boolean;
5
+ preauthorizedApprovals?: readonly string[];
6
+ now?: Date;
7
+ }
8
+ export declare class IntentPolicyEngine {
9
+ private readonly catalog;
10
+ readonly version = "asm-personal-policy-v1";
11
+ constructor(catalog?: IntentSchemaCatalog);
12
+ decide(intent: IntentContract, context: PolicyContext): PolicyContract;
13
+ scopeHash(intent: IntentContract, approvalType: string): string;
14
+ private decision;
15
+ }
16
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC7B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,sBAAsB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,GAAG,CAAC,EAAE,IAAI,CAAC;CACX;AAED,qBAAa,kBAAkB;IAGlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,OAAO,4BAA4B;IAE5C,YAA6B,OAAO,sBAA4B,EAAI;IAEpE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,GAAG,cAAc,CAsBrE;IAED,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAW9D;IAED,OAAO,CAAC,QAAQ;CAkBhB","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { IntentContract, PolicyContract } from \"@asm-agent/contracts\";\nimport { IntentSchemaCatalog } from \"./catalog.js\";\n\nexport interface PolicyContext {\n\ttrustedOwnerProject: boolean;\n\tpreauthorizedApprovals?: readonly string[];\n\tnow?: Date;\n}\n\nexport class IntentPolicyEngine {\n\treadonly version = \"asm-personal-policy-v1\";\n\n\tconstructor(private readonly catalog = new IntentSchemaCatalog()) {}\n\n\tdecide(intent: IntentContract, context: PolicyContext): PolicyContract {\n\t\tthis.catalog.validate(intent);\n\t\tif (!context.trustedOwnerProject)\n\t\t\treturn this.decision(intent, \"deny\", [\"untrusted_identity_scope\"], [], context.now);\n\t\tif (intent.operation === \"purchase\") {\n\t\t\tconst amount = intent.arguments.amount;\n\t\t\tif (typeof amount !== \"number\" || !Number.isFinite(amount) || amount <= 0) {\n\t\t\t\treturn this.decision(intent, \"deny\", [\"invalid_purchase_amount\"], [], context.now);\n\t\t\t}\n\t\t}\n\t\tconst required = [...this.catalog.get(intent.operation).requiredApprovals];\n\t\tconst preauthorized = new Set(context.preauthorizedApprovals ?? []);\n\t\tconst missing = required.filter((approval) => !preauthorized.has(approval));\n\t\tif (missing.length > 0)\n\t\t\treturn this.decision(intent, \"require_approval\", [\"approval_required\"], missing, context.now);\n\t\treturn this.decision(\n\t\t\tintent,\n\t\t\t\"allow\",\n\t\t\trequired.length > 0 ? [\"preauthorized_scope\"] : [\"read_only_or_diagnostic\"],\n\t\t\t[],\n\t\t\tcontext.now,\n\t\t);\n\t}\n\n\tscopeHash(intent: IntentContract, approvalType: string): string {\n\t\treturn createHash(\"sha256\")\n\t\t\t.update(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tintentId: intent.intentId,\n\t\t\t\t\toperation: intent.operation,\n\t\t\t\t\tresources: intent.resources,\n\t\t\t\t\tapprovalType,\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.digest(\"hex\");\n\t}\n\n\tprivate decision(\n\t\tintent: IntentContract,\n\t\toutcome: PolicyContract[\"outcome\"],\n\t\treasonCodes: string[],\n\t\trequiredApprovals: string[],\n\t\tnow = new Date(),\n\t): PolicyContract {\n\t\treturn {\n\t\t\theader: { namespace: \"ai.aletheion.asm\", major: 1, minor: 1 },\n\t\t\tdecisionId: randomUUID(),\n\t\t\tintentId: intent.intentId,\n\t\t\toutcome,\n\t\t\treasonCodes,\n\t\t\trequiredApprovals,\n\t\t\tdecidedAt: now.toISOString(),\n\t\t\tpolicyVersion: this.version,\n\t\t};\n\t}\n}\n"]}
package/dist/policy.js ADDED
@@ -0,0 +1,49 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { IntentSchemaCatalog } from "./catalog.js";
3
+ export class IntentPolicyEngine {
4
+ catalog;
5
+ version = "asm-personal-policy-v1";
6
+ constructor(catalog = new IntentSchemaCatalog()) {
7
+ this.catalog = catalog;
8
+ }
9
+ decide(intent, context) {
10
+ this.catalog.validate(intent);
11
+ if (!context.trustedOwnerProject)
12
+ return this.decision(intent, "deny", ["untrusted_identity_scope"], [], context.now);
13
+ if (intent.operation === "purchase") {
14
+ const amount = intent.arguments.amount;
15
+ if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) {
16
+ return this.decision(intent, "deny", ["invalid_purchase_amount"], [], context.now);
17
+ }
18
+ }
19
+ const required = [...this.catalog.get(intent.operation).requiredApprovals];
20
+ const preauthorized = new Set(context.preauthorizedApprovals ?? []);
21
+ const missing = required.filter((approval) => !preauthorized.has(approval));
22
+ if (missing.length > 0)
23
+ return this.decision(intent, "require_approval", ["approval_required"], missing, context.now);
24
+ return this.decision(intent, "allow", required.length > 0 ? ["preauthorized_scope"] : ["read_only_or_diagnostic"], [], context.now);
25
+ }
26
+ scopeHash(intent, approvalType) {
27
+ return createHash("sha256")
28
+ .update(JSON.stringify({
29
+ intentId: intent.intentId,
30
+ operation: intent.operation,
31
+ resources: intent.resources,
32
+ approvalType,
33
+ }))
34
+ .digest("hex");
35
+ }
36
+ decision(intent, outcome, reasonCodes, requiredApprovals, now = new Date()) {
37
+ return {
38
+ header: { namespace: "ai.aletheion.asm", major: 1, minor: 1 },
39
+ decisionId: randomUUID(),
40
+ intentId: intent.intentId,
41
+ outcome,
42
+ reasonCodes,
43
+ requiredApprovals,
44
+ decidedAt: now.toISOString(),
45
+ policyVersion: this.version,
46
+ };
47
+ }
48
+ }
49
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { IntentContract, PolicyContract } from \"@asm-agent/contracts\";\nimport { IntentSchemaCatalog } from \"./catalog.js\";\n\nexport interface PolicyContext {\n\ttrustedOwnerProject: boolean;\n\tpreauthorizedApprovals?: readonly string[];\n\tnow?: Date;\n}\n\nexport class IntentPolicyEngine {\n\treadonly version = \"asm-personal-policy-v1\";\n\n\tconstructor(private readonly catalog = new IntentSchemaCatalog()) {}\n\n\tdecide(intent: IntentContract, context: PolicyContext): PolicyContract {\n\t\tthis.catalog.validate(intent);\n\t\tif (!context.trustedOwnerProject)\n\t\t\treturn this.decision(intent, \"deny\", [\"untrusted_identity_scope\"], [], context.now);\n\t\tif (intent.operation === \"purchase\") {\n\t\t\tconst amount = intent.arguments.amount;\n\t\t\tif (typeof amount !== \"number\" || !Number.isFinite(amount) || amount <= 0) {\n\t\t\t\treturn this.decision(intent, \"deny\", [\"invalid_purchase_amount\"], [], context.now);\n\t\t\t}\n\t\t}\n\t\tconst required = [...this.catalog.get(intent.operation).requiredApprovals];\n\t\tconst preauthorized = new Set(context.preauthorizedApprovals ?? []);\n\t\tconst missing = required.filter((approval) => !preauthorized.has(approval));\n\t\tif (missing.length > 0)\n\t\t\treturn this.decision(intent, \"require_approval\", [\"approval_required\"], missing, context.now);\n\t\treturn this.decision(\n\t\t\tintent,\n\t\t\t\"allow\",\n\t\t\trequired.length > 0 ? [\"preauthorized_scope\"] : [\"read_only_or_diagnostic\"],\n\t\t\t[],\n\t\t\tcontext.now,\n\t\t);\n\t}\n\n\tscopeHash(intent: IntentContract, approvalType: string): string {\n\t\treturn createHash(\"sha256\")\n\t\t\t.update(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tintentId: intent.intentId,\n\t\t\t\t\toperation: intent.operation,\n\t\t\t\t\tresources: intent.resources,\n\t\t\t\t\tapprovalType,\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.digest(\"hex\");\n\t}\n\n\tprivate decision(\n\t\tintent: IntentContract,\n\t\toutcome: PolicyContract[\"outcome\"],\n\t\treasonCodes: string[],\n\t\trequiredApprovals: string[],\n\t\tnow = new Date(),\n\t): PolicyContract {\n\t\treturn {\n\t\t\theader: { namespace: \"ai.aletheion.asm\", major: 1, minor: 1 },\n\t\t\tdecisionId: randomUUID(),\n\t\t\tintentId: intent.intentId,\n\t\t\toutcome,\n\t\t\treasonCodes,\n\t\t\trequiredApprovals,\n\t\t\tdecidedAt: now.toISOString(),\n\t\t\tpolicyVersion: this.version,\n\t\t};\n\t}\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import type { IntentContract, PolicyContract } from "@asm-agent/contracts";
2
+ import type { PostgresStore } from "@asm-agent/postgres";
3
+ import type { ParsedIntentResult } from "./parser.js";
4
+ export declare class GovernanceRepository {
5
+ private readonly database;
6
+ constructor(database: PostgresStore);
7
+ saveIntent(ownerId: string, projectId: string, sessionId: string | undefined, parsed: ParsedIntentResult): Promise<void>;
8
+ saveDecision(decision: PolicyContract): Promise<void>;
9
+ grantApproval(input: {
10
+ id: string;
11
+ decisionId: string;
12
+ approvalType: string;
13
+ grantedBy: string;
14
+ scopeHash: string;
15
+ expiresAt?: Date;
16
+ }): Promise<void>;
17
+ hasApprovals(decision: PolicyContract, intent: IntentContract, expectedScopeHash: (type: string) => string): Promise<boolean>;
18
+ consumeApprovals(decisionId: string): Promise<void>;
19
+ recordExecutionStarted(input: {
20
+ id: string;
21
+ intentId: string;
22
+ decisionId: string;
23
+ scope: string;
24
+ key: string;
25
+ requestHash: string;
26
+ }): Promise<void>;
27
+ finishExecution(id: string, status: "completed" | "failed" | "unknown_external_outcome", value: unknown): Promise<void>;
28
+ }
29
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,qBAAa,oBAAoB;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAArC,YAA6B,QAAQ,EAAE,aAAa,EAAI;IAElD,UAAU,CACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC,CA2Bf;IAEK,YAAY,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAe1D;IAEK,aAAa,CAAC,KAAK,EAAE;QAC1B,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,IAAI,CAAC;KACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhB;IAEK,YAAY,CACjB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,cAAc,EACtB,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GACzC,OAAO,CAAC,OAAO,CAAC,CAalB;IAEK,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxD;IAEK,sBAAsB,CAAC,KAAK,EAAE;QACnC,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;IAEK,eAAe,CACpB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,0BAA0B,EAC3D,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC,CAQf;CACD","sourcesContent":["import type { IntentContract, PolicyContract } from \"@asm-agent/contracts\";\nimport type { PostgresStore } from \"@asm-agent/postgres\";\nimport type { ParsedIntentResult } from \"./parser.js\";\n\nexport class GovernanceRepository {\n\tconstructor(private readonly database: PostgresStore) {}\n\n\tasync saveIntent(\n\t\townerId: string,\n\t\tprojectId: string,\n\t\tsessionId: string | undefined,\n\t\tparsed: ParsedIntentResult,\n\t): Promise<void> {\n\t\tconst { intent } = parsed;\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.parsed_intents\n\t\t\t (id, owner_id, project_id, session_id, contract_major, contract_minor, operation,\n\t\t\t resources, external_effect, idempotency_key, arguments, parser_id, parser_version,\n\t\t\t model_provider, model_id, requested_at)\n\t\t\t VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14,$15,$16)`,\n\t\t\t[\n\t\t\t\tintent.intentId,\n\t\t\t\townerId,\n\t\t\t\tprojectId,\n\t\t\t\tsessionId ?? null,\n\t\t\t\tintent.header.major,\n\t\t\t\tintent.header.minor,\n\t\t\t\tintent.operation,\n\t\t\t\tintent.resources,\n\t\t\t\tintent.externalEffect,\n\t\t\t\tintent.idempotencyKey ?? null,\n\t\t\t\tJSON.stringify(intent.arguments),\n\t\t\t\tparsed.parserId,\n\t\t\t\tparsed.parserVersion,\n\t\t\t\tparsed.route?.provider ?? null,\n\t\t\t\tparsed.route?.modelId ?? null,\n\t\t\t\tintent.requestedAt,\n\t\t\t],\n\t\t);\n\t}\n\n\tasync saveDecision(decision: PolicyContract): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.policy_decisions\n\t\t\t (id, intent_id, outcome, reason_codes, required_approvals, policy_version, decided_at)\n\t\t\t VALUES ($1,$2,$3,$4,$5,$6,$7)`,\n\t\t\t[\n\t\t\t\tdecision.decisionId,\n\t\t\t\tdecision.intentId,\n\t\t\t\tdecision.outcome,\n\t\t\t\tdecision.reasonCodes,\n\t\t\t\tdecision.requiredApprovals,\n\t\t\t\tdecision.policyVersion,\n\t\t\t\tdecision.decidedAt,\n\t\t\t],\n\t\t);\n\t}\n\n\tasync grantApproval(input: {\n\t\tid: string;\n\t\tdecisionId: string;\n\t\tapprovalType: string;\n\t\tgrantedBy: string;\n\t\tscopeHash: string;\n\t\texpiresAt?: Date;\n\t}): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.approval_grants\n\t\t\t (id, decision_id, approval_type, granted_by, scope_hash, expires_at) VALUES ($1,$2,$3,$4,$5,$6)`,\n\t\t\t[input.id, input.decisionId, input.approvalType, input.grantedBy, input.scopeHash, input.expiresAt ?? null],\n\t\t);\n\t}\n\n\tasync hasApprovals(\n\t\tdecision: PolicyContract,\n\t\tintent: IntentContract,\n\t\texpectedScopeHash: (type: string) => string,\n\t): Promise<boolean> {\n\t\tif (decision.outcome === \"allow\") return true;\n\t\tif (decision.outcome === \"deny\") return false;\n\t\tconst result = await this.database.pool.query<{ approval_type: string; scope_hash: string }>(\n\t\t\t`SELECT approval_type, scope_hash FROM asm_agent.approval_grants\n\t\t\t WHERE decision_id = $1 AND (expires_at IS NULL OR expires_at > clock_timestamp())`,\n\t\t\t[decision.decisionId],\n\t\t);\n\t\tconst grants = new Map(result.rows.map((row) => [row.approval_type, row.scope_hash]));\n\t\treturn (\n\t\t\tdecision.requiredApprovals.every((type) => grants.get(type) === expectedScopeHash(type)) &&\n\t\t\tintent.intentId === decision.intentId\n\t\t);\n\t}\n\n\tasync consumeApprovals(decisionId: string): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`UPDATE asm_agent.approval_grants SET consumed_at = clock_timestamp()\n\t\t\t WHERE decision_id = $1 AND consumed_at IS NULL`,\n\t\t\t[decisionId],\n\t\t);\n\t}\n\n\tasync recordExecutionStarted(input: {\n\t\tid: string;\n\t\tintentId: string;\n\t\tdecisionId: string;\n\t\tscope: string;\n\t\tkey: string;\n\t\trequestHash: string;\n\t}): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.governed_executions\n\t\t\t (id, intent_id, decision_id, idempotency_scope, idempotency_key, request_hash, status)\n\t\t\t VALUES ($1,$2,$3,$4,$5,$6,'started') ON CONFLICT (idempotency_scope, idempotency_key) DO NOTHING`,\n\t\t\t[input.id, input.intentId, input.decisionId, input.scope, input.key, input.requestHash],\n\t\t);\n\t}\n\n\tasync finishExecution(\n\t\tid: string,\n\t\tstatus: \"completed\" | \"failed\" | \"unknown_external_outcome\",\n\t\tvalue: unknown,\n\t): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`UPDATE asm_agent.governed_executions SET status = $2,\n\t\t\t result = CASE WHEN $2 = 'completed' THEN $3::jsonb ELSE NULL END,\n\t\t\t error = CASE WHEN $2 <> 'completed' THEN $3::jsonb ELSE NULL END,\n\t\t\t completed_at = clock_timestamp() WHERE id = $1`,\n\t\t\t[id, status, JSON.stringify(value)],\n\t\t);\n\t}\n}\n"]}
@@ -0,0 +1,75 @@
1
+ export class GovernanceRepository {
2
+ database;
3
+ constructor(database) {
4
+ this.database = database;
5
+ }
6
+ async saveIntent(ownerId, projectId, sessionId, parsed) {
7
+ const { intent } = parsed;
8
+ await this.database.pool.query(`INSERT INTO asm_agent.parsed_intents
9
+ (id, owner_id, project_id, session_id, contract_major, contract_minor, operation,
10
+ resources, external_effect, idempotency_key, arguments, parser_id, parser_version,
11
+ model_provider, model_id, requested_at)
12
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14,$15,$16)`, [
13
+ intent.intentId,
14
+ ownerId,
15
+ projectId,
16
+ sessionId ?? null,
17
+ intent.header.major,
18
+ intent.header.minor,
19
+ intent.operation,
20
+ intent.resources,
21
+ intent.externalEffect,
22
+ intent.idempotencyKey ?? null,
23
+ JSON.stringify(intent.arguments),
24
+ parsed.parserId,
25
+ parsed.parserVersion,
26
+ parsed.route?.provider ?? null,
27
+ parsed.route?.modelId ?? null,
28
+ intent.requestedAt,
29
+ ]);
30
+ }
31
+ async saveDecision(decision) {
32
+ await this.database.pool.query(`INSERT INTO asm_agent.policy_decisions
33
+ (id, intent_id, outcome, reason_codes, required_approvals, policy_version, decided_at)
34
+ VALUES ($1,$2,$3,$4,$5,$6,$7)`, [
35
+ decision.decisionId,
36
+ decision.intentId,
37
+ decision.outcome,
38
+ decision.reasonCodes,
39
+ decision.requiredApprovals,
40
+ decision.policyVersion,
41
+ decision.decidedAt,
42
+ ]);
43
+ }
44
+ async grantApproval(input) {
45
+ await this.database.pool.query(`INSERT INTO asm_agent.approval_grants
46
+ (id, decision_id, approval_type, granted_by, scope_hash, expires_at) VALUES ($1,$2,$3,$4,$5,$6)`, [input.id, input.decisionId, input.approvalType, input.grantedBy, input.scopeHash, input.expiresAt ?? null]);
47
+ }
48
+ async hasApprovals(decision, intent, expectedScopeHash) {
49
+ if (decision.outcome === "allow")
50
+ return true;
51
+ if (decision.outcome === "deny")
52
+ return false;
53
+ const result = await this.database.pool.query(`SELECT approval_type, scope_hash FROM asm_agent.approval_grants
54
+ WHERE decision_id = $1 AND (expires_at IS NULL OR expires_at > clock_timestamp())`, [decision.decisionId]);
55
+ const grants = new Map(result.rows.map((row) => [row.approval_type, row.scope_hash]));
56
+ return (decision.requiredApprovals.every((type) => grants.get(type) === expectedScopeHash(type)) &&
57
+ intent.intentId === decision.intentId);
58
+ }
59
+ async consumeApprovals(decisionId) {
60
+ await this.database.pool.query(`UPDATE asm_agent.approval_grants SET consumed_at = clock_timestamp()
61
+ WHERE decision_id = $1 AND consumed_at IS NULL`, [decisionId]);
62
+ }
63
+ async recordExecutionStarted(input) {
64
+ await this.database.pool.query(`INSERT INTO asm_agent.governed_executions
65
+ (id, intent_id, decision_id, idempotency_scope, idempotency_key, request_hash, status)
66
+ VALUES ($1,$2,$3,$4,$5,$6,'started') ON CONFLICT (idempotency_scope, idempotency_key) DO NOTHING`, [input.id, input.intentId, input.decisionId, input.scope, input.key, input.requestHash]);
67
+ }
68
+ async finishExecution(id, status, value) {
69
+ await this.database.pool.query(`UPDATE asm_agent.governed_executions SET status = $2,
70
+ result = CASE WHEN $2 = 'completed' THEN $3::jsonb ELSE NULL END,
71
+ error = CASE WHEN $2 <> 'completed' THEN $3::jsonb ELSE NULL END,
72
+ completed_at = clock_timestamp() WHERE id = $1`, [id, status, JSON.stringify(value)]);
73
+ }
74
+ }
75
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IntentContract, PolicyContract } from \"@asm-agent/contracts\";\nimport type { PostgresStore } from \"@asm-agent/postgres\";\nimport type { ParsedIntentResult } from \"./parser.js\";\n\nexport class GovernanceRepository {\n\tconstructor(private readonly database: PostgresStore) {}\n\n\tasync saveIntent(\n\t\townerId: string,\n\t\tprojectId: string,\n\t\tsessionId: string | undefined,\n\t\tparsed: ParsedIntentResult,\n\t): Promise<void> {\n\t\tconst { intent } = parsed;\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.parsed_intents\n\t\t\t (id, owner_id, project_id, session_id, contract_major, contract_minor, operation,\n\t\t\t resources, external_effect, idempotency_key, arguments, parser_id, parser_version,\n\t\t\t model_provider, model_id, requested_at)\n\t\t\t VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14,$15,$16)`,\n\t\t\t[\n\t\t\t\tintent.intentId,\n\t\t\t\townerId,\n\t\t\t\tprojectId,\n\t\t\t\tsessionId ?? null,\n\t\t\t\tintent.header.major,\n\t\t\t\tintent.header.minor,\n\t\t\t\tintent.operation,\n\t\t\t\tintent.resources,\n\t\t\t\tintent.externalEffect,\n\t\t\t\tintent.idempotencyKey ?? null,\n\t\t\t\tJSON.stringify(intent.arguments),\n\t\t\t\tparsed.parserId,\n\t\t\t\tparsed.parserVersion,\n\t\t\t\tparsed.route?.provider ?? null,\n\t\t\t\tparsed.route?.modelId ?? null,\n\t\t\t\tintent.requestedAt,\n\t\t\t],\n\t\t);\n\t}\n\n\tasync saveDecision(decision: PolicyContract): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.policy_decisions\n\t\t\t (id, intent_id, outcome, reason_codes, required_approvals, policy_version, decided_at)\n\t\t\t VALUES ($1,$2,$3,$4,$5,$6,$7)`,\n\t\t\t[\n\t\t\t\tdecision.decisionId,\n\t\t\t\tdecision.intentId,\n\t\t\t\tdecision.outcome,\n\t\t\t\tdecision.reasonCodes,\n\t\t\t\tdecision.requiredApprovals,\n\t\t\t\tdecision.policyVersion,\n\t\t\t\tdecision.decidedAt,\n\t\t\t],\n\t\t);\n\t}\n\n\tasync grantApproval(input: {\n\t\tid: string;\n\t\tdecisionId: string;\n\t\tapprovalType: string;\n\t\tgrantedBy: string;\n\t\tscopeHash: string;\n\t\texpiresAt?: Date;\n\t}): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.approval_grants\n\t\t\t (id, decision_id, approval_type, granted_by, scope_hash, expires_at) VALUES ($1,$2,$3,$4,$5,$6)`,\n\t\t\t[input.id, input.decisionId, input.approvalType, input.grantedBy, input.scopeHash, input.expiresAt ?? null],\n\t\t);\n\t}\n\n\tasync hasApprovals(\n\t\tdecision: PolicyContract,\n\t\tintent: IntentContract,\n\t\texpectedScopeHash: (type: string) => string,\n\t): Promise<boolean> {\n\t\tif (decision.outcome === \"allow\") return true;\n\t\tif (decision.outcome === \"deny\") return false;\n\t\tconst result = await this.database.pool.query<{ approval_type: string; scope_hash: string }>(\n\t\t\t`SELECT approval_type, scope_hash FROM asm_agent.approval_grants\n\t\t\t WHERE decision_id = $1 AND (expires_at IS NULL OR expires_at > clock_timestamp())`,\n\t\t\t[decision.decisionId],\n\t\t);\n\t\tconst grants = new Map(result.rows.map((row) => [row.approval_type, row.scope_hash]));\n\t\treturn (\n\t\t\tdecision.requiredApprovals.every((type) => grants.get(type) === expectedScopeHash(type)) &&\n\t\t\tintent.intentId === decision.intentId\n\t\t);\n\t}\n\n\tasync consumeApprovals(decisionId: string): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`UPDATE asm_agent.approval_grants SET consumed_at = clock_timestamp()\n\t\t\t WHERE decision_id = $1 AND consumed_at IS NULL`,\n\t\t\t[decisionId],\n\t\t);\n\t}\n\n\tasync recordExecutionStarted(input: {\n\t\tid: string;\n\t\tintentId: string;\n\t\tdecisionId: string;\n\t\tscope: string;\n\t\tkey: string;\n\t\trequestHash: string;\n\t}): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.governed_executions\n\t\t\t (id, intent_id, decision_id, idempotency_scope, idempotency_key, request_hash, status)\n\t\t\t VALUES ($1,$2,$3,$4,$5,$6,'started') ON CONFLICT (idempotency_scope, idempotency_key) DO NOTHING`,\n\t\t\t[input.id, input.intentId, input.decisionId, input.scope, input.key, input.requestHash],\n\t\t);\n\t}\n\n\tasync finishExecution(\n\t\tid: string,\n\t\tstatus: \"completed\" | \"failed\" | \"unknown_external_outcome\",\n\t\tvalue: unknown,\n\t): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`UPDATE asm_agent.governed_executions SET status = $2,\n\t\t\t result = CASE WHEN $2 = 'completed' THEN $3::jsonb ELSE NULL END,\n\t\t\t error = CASE WHEN $2 <> 'completed' THEN $3::jsonb ELSE NULL END,\n\t\t\t completed_at = clock_timestamp() WHERE id = $1`,\n\t\t\t[id, status, JSON.stringify(value)],\n\t\t);\n\t}\n}\n"]}
@@ -0,0 +1,18 @@
1
+ export type LoginProvider = "openai-codex" | "anthropic";
2
+ export type RoutingPurpose = "intent_parse" | "intent_repair";
3
+ export interface AuthenticatedModel {
4
+ provider: LoginProvider;
5
+ modelId: string;
6
+ purposes: readonly RoutingPurpose[];
7
+ }
8
+ export interface ModelRoute {
9
+ provider: LoginProvider;
10
+ modelId: string;
11
+ purpose: RoutingPurpose;
12
+ }
13
+ export declare class AuthenticatedModelRouter {
14
+ private readonly models;
15
+ constructor(models: readonly AuthenticatedModel[]);
16
+ route(purpose: RoutingPurpose, preferredProvider?: LoginProvider): ModelRoute;
17
+ }
18
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,WAAW,CAAC;AACzD,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,eAAe,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,cAAc,CAAC;CACxB;AAED,qBAAa,wBAAwB;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAnC,YAA6B,MAAM,EAAE,SAAS,kBAAkB,EAAE,EAAI;IAEtE,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,iBAAiB,CAAC,EAAE,aAAa,GAAG,UAAU,CAK5E;CACD","sourcesContent":["export type LoginProvider = \"openai-codex\" | \"anthropic\";\nexport type RoutingPurpose = \"intent_parse\" | \"intent_repair\";\n\nexport interface AuthenticatedModel {\n\tprovider: LoginProvider;\n\tmodelId: string;\n\tpurposes: readonly RoutingPurpose[];\n}\n\nexport interface ModelRoute {\n\tprovider: LoginProvider;\n\tmodelId: string;\n\tpurpose: RoutingPurpose;\n}\n\nexport class AuthenticatedModelRouter {\n\tconstructor(private readonly models: readonly AuthenticatedModel[]) {}\n\n\troute(purpose: RoutingPurpose, preferredProvider?: LoginProvider): ModelRoute {\n\t\tconst candidates = this.models.filter((model) => model.purposes.includes(purpose));\n\t\tconst selected = candidates.find((model) => model.provider === preferredProvider) ?? candidates[0];\n\t\tif (!selected) throw new Error(`No authenticated Codex or Claude model supports ${purpose}`);\n\t\treturn { provider: selected.provider, modelId: selected.modelId, purpose };\n\t}\n}\n"]}
package/dist/router.js ADDED
@@ -0,0 +1,14 @@
1
+ export class AuthenticatedModelRouter {
2
+ models;
3
+ constructor(models) {
4
+ this.models = models;
5
+ }
6
+ route(purpose, preferredProvider) {
7
+ const candidates = this.models.filter((model) => model.purposes.includes(purpose));
8
+ const selected = candidates.find((model) => model.provider === preferredProvider) ?? candidates[0];
9
+ if (!selected)
10
+ throw new Error(`No authenticated Codex or Claude model supports ${purpose}`);
11
+ return { provider: selected.provider, modelId: selected.modelId, purpose };
12
+ }
13
+ }
14
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAeA,MAAM,OAAO,wBAAwB;IACP,MAAM;IAAnC,YAA6B,MAAqC,EAAE;sBAAvC,MAAM;IAAkC,CAAC;IAEtE,KAAK,CAAC,OAAuB,EAAE,iBAAiC,EAAc;QAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACnF,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,iBAAiB,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QACnG,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,OAAO,EAAE,CAAC,CAAC;QAC7F,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;IAAA,CAC3E;CACD","sourcesContent":["export type LoginProvider = \"openai-codex\" | \"anthropic\";\nexport type RoutingPurpose = \"intent_parse\" | \"intent_repair\";\n\nexport interface AuthenticatedModel {\n\tprovider: LoginProvider;\n\tmodelId: string;\n\tpurposes: readonly RoutingPurpose[];\n}\n\nexport interface ModelRoute {\n\tprovider: LoginProvider;\n\tmodelId: string;\n\tpurpose: RoutingPurpose;\n}\n\nexport class AuthenticatedModelRouter {\n\tconstructor(private readonly models: readonly AuthenticatedModel[]) {}\n\n\troute(purpose: RoutingPurpose, preferredProvider?: LoginProvider): ModelRoute {\n\t\tconst candidates = this.models.filter((model) => model.purposes.includes(purpose));\n\t\tconst selected = candidates.find((model) => model.provider === preferredProvider) ?? candidates[0];\n\t\tif (!selected) throw new Error(`No authenticated Codex or Claude model supports ${purpose}`);\n\t\treturn { provider: selected.provider, modelId: selected.modelId, purpose };\n\t}\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { PolicyContract } from "@asm-agent/contracts";
2
+ import type { ParsedIntentResult, ParseIntentInput, VersionedIntentParser } from "./parser.js";
3
+ import type { IntentPolicyEngine, PolicyContext } from "./policy.js";
4
+ import type { GovernanceRepository } from "./repository.js";
5
+ export declare class GovernanceService {
6
+ private readonly parser;
7
+ private readonly policy;
8
+ private readonly repository;
9
+ constructor(parser: VersionedIntentParser, policy: IntentPolicyEngine, repository: GovernanceRepository);
10
+ assess(input: {
11
+ ownerId: string;
12
+ projectId: string;
13
+ sessionId?: string;
14
+ request: ParseIntentInput;
15
+ policyContext: PolicyContext;
16
+ }): Promise<{
17
+ parsed: ParsedIntentResult;
18
+ decision: PolicyContract;
19
+ }>;
20
+ }
21
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,qBAAa,iBAAiB;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAH5B,YACkB,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,kBAAkB,EAC1B,UAAU,EAAE,oBAAoB,EAC9C;IAEE,MAAM,CAAC,KAAK,EAAE;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,gBAAgB,CAAC;QAC1B,aAAa,EAAE,aAAa,CAAC;KAC7B,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,kBAAkB,CAAC;QAAC,QAAQ,EAAE,cAAc,CAAA;KAAE,CAAC,CAMpE;CACD","sourcesContent":["import type { PolicyContract } from \"@asm-agent/contracts\";\nimport type { ParsedIntentResult, ParseIntentInput, VersionedIntentParser } from \"./parser.js\";\nimport type { IntentPolicyEngine, PolicyContext } from \"./policy.js\";\nimport type { GovernanceRepository } from \"./repository.js\";\n\nexport class GovernanceService {\n\tconstructor(\n\t\tprivate readonly parser: VersionedIntentParser,\n\t\tprivate readonly policy: IntentPolicyEngine,\n\t\tprivate readonly repository: GovernanceRepository,\n\t) {}\n\n\tasync assess(input: {\n\t\townerId: string;\n\t\tprojectId: string;\n\t\tsessionId?: string;\n\t\trequest: ParseIntentInput;\n\t\tpolicyContext: PolicyContext;\n\t}): Promise<{ parsed: ParsedIntentResult; decision: PolicyContract }> {\n\t\tconst parsed = await this.parser.parse(input.request);\n\t\tawait this.repository.saveIntent(input.ownerId, input.projectId, input.sessionId, parsed);\n\t\tconst decision = this.policy.decide(parsed.intent, input.policyContext);\n\t\tawait this.repository.saveDecision(decision);\n\t\treturn { parsed, decision };\n\t}\n}\n"]}
@@ -0,0 +1,18 @@
1
+ export class GovernanceService {
2
+ parser;
3
+ policy;
4
+ repository;
5
+ constructor(parser, policy, repository) {
6
+ this.parser = parser;
7
+ this.policy = policy;
8
+ this.repository = repository;
9
+ }
10
+ async assess(input) {
11
+ const parsed = await this.parser.parse(input.request);
12
+ await this.repository.saveIntent(input.ownerId, input.projectId, input.sessionId, parsed);
13
+ const decision = this.policy.decide(parsed.intent, input.policyContext);
14
+ await this.repository.saveDecision(decision);
15
+ return { parsed, decision };
16
+ }
17
+ }
18
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,iBAAiB;IAEX,MAAM;IACN,MAAM;IACN,UAAU;IAH5B,YACkB,MAA6B,EAC7B,MAA0B,EAC1B,UAAgC,EAChD;sBAHgB,MAAM;sBACN,MAAM;0BACN,UAAU;IACzB,CAAC;IAEJ,KAAK,CAAC,MAAM,CAAC,KAMZ,EAAqE;QACrE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC1F,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;QACxE,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC7C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAAA,CAC5B;CACD","sourcesContent":["import type { PolicyContract } from \"@asm-agent/contracts\";\nimport type { ParsedIntentResult, ParseIntentInput, VersionedIntentParser } from \"./parser.js\";\nimport type { IntentPolicyEngine, PolicyContext } from \"./policy.js\";\nimport type { GovernanceRepository } from \"./repository.js\";\n\nexport class GovernanceService {\n\tconstructor(\n\t\tprivate readonly parser: VersionedIntentParser,\n\t\tprivate readonly policy: IntentPolicyEngine,\n\t\tprivate readonly repository: GovernanceRepository,\n\t) {}\n\n\tasync assess(input: {\n\t\townerId: string;\n\t\tprojectId: string;\n\t\tsessionId?: string;\n\t\trequest: ParseIntentInput;\n\t\tpolicyContext: PolicyContext;\n\t}): Promise<{ parsed: ParsedIntentResult; decision: PolicyContract }> {\n\t\tconst parsed = await this.parser.parse(input.request);\n\t\tawait this.repository.saveIntent(input.ownerId, input.projectId, input.sessionId, parsed);\n\t\tconst decision = this.policy.decide(parsed.intent, input.policyContext);\n\t\tawait this.repository.saveDecision(decision);\n\t\treturn { parsed, decision };\n\t}\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { IntentOperation } from "./catalog.js";
2
+ export declare const DEFAULT_CONSEQUENTIAL_TOOL_NAMES: readonly ["edit", "bash", "ipython"];
3
+ export type DefaultConsequentialToolName = (typeof DEFAULT_CONSEQUENTIAL_TOOL_NAMES)[number];
4
+ export interface ToolIntentClassification {
5
+ operation: IntentOperation;
6
+ externalEffect: boolean;
7
+ requiresIdempotency: boolean;
8
+ requiredApproval: string;
9
+ }
10
+ export declare function classifyDefaultTool(toolName: string): ToolIntentClassification;
11
+ //# sourceMappingURL=tool-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-policy.d.ts","sourceRoot":"","sources":["../src/tool-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,eAAO,MAAM,gCAAgC,sCAAuC,CAAC;AACrF,MAAM,MAAM,4BAA4B,GAAG,CAAC,OAAO,gCAAgC,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7F,MAAM,WAAW,wBAAwB;IACxC,SAAS,EAAE,eAAe,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,wBAAwB,CAoB9E","sourcesContent":["import type { IntentOperation } from \"./catalog.js\";\n\nexport const DEFAULT_CONSEQUENTIAL_TOOL_NAMES = [\"edit\", \"bash\", \"ipython\"] as const;\nexport type DefaultConsequentialToolName = (typeof DEFAULT_CONSEQUENTIAL_TOOL_NAMES)[number];\n\nexport interface ToolIntentClassification {\n\toperation: IntentOperation;\n\texternalEffect: boolean;\n\trequiresIdempotency: boolean;\n\trequiredApproval: string;\n}\n\nexport function classifyDefaultTool(toolName: string): ToolIntentClassification {\n\tswitch (toolName) {\n\t\tcase \"edit\":\n\t\t\treturn {\n\t\t\t\toperation: \"write\",\n\t\t\t\texternalEffect: false,\n\t\t\t\trequiresIdempotency: false,\n\t\t\t\trequiredApproval: \"local_write\",\n\t\t\t};\n\t\tcase \"bash\":\n\t\tcase \"ipython\":\n\t\t\treturn {\n\t\t\t\toperation: \"execute\",\n\t\t\t\texternalEffect: true,\n\t\t\t\trequiresIdempotency: true,\n\t\t\t\trequiredApproval: \"command_execution\",\n\t\t\t};\n\t\tdefault:\n\t\t\tthrow new Error(`Default tool is not classified for consequential execution: ${toolName}`);\n\t}\n}\n"]}
@@ -0,0 +1,23 @@
1
+ export const DEFAULT_CONSEQUENTIAL_TOOL_NAMES = ["edit", "bash", "ipython"];
2
+ export function classifyDefaultTool(toolName) {
3
+ switch (toolName) {
4
+ case "edit":
5
+ return {
6
+ operation: "write",
7
+ externalEffect: false,
8
+ requiresIdempotency: false,
9
+ requiredApproval: "local_write",
10
+ };
11
+ case "bash":
12
+ case "ipython":
13
+ return {
14
+ operation: "execute",
15
+ externalEffect: true,
16
+ requiresIdempotency: true,
17
+ requiredApproval: "command_execution",
18
+ };
19
+ default:
20
+ throw new Error(`Default tool is not classified for consequential execution: ${toolName}`);
21
+ }
22
+ }
23
+ //# sourceMappingURL=tool-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-policy.js","sourceRoot":"","sources":["../src/tool-policy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC;AAUrF,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAA4B;IAC/E,QAAQ,QAAQ,EAAE,CAAC;QAClB,KAAK,MAAM;YACV,OAAO;gBACN,SAAS,EAAE,OAAO;gBAClB,cAAc,EAAE,KAAK;gBACrB,mBAAmB,EAAE,KAAK;gBAC1B,gBAAgB,EAAE,aAAa;aAC/B,CAAC;QACH,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS;YACb,OAAO;gBACN,SAAS,EAAE,SAAS;gBACpB,cAAc,EAAE,IAAI;gBACpB,mBAAmB,EAAE,IAAI;gBACzB,gBAAgB,EAAE,mBAAmB;aACrC,CAAC;QACH;YACC,MAAM,IAAI,KAAK,CAAC,+DAA+D,QAAQ,EAAE,CAAC,CAAC;IAC7F,CAAC;AAAA,CACD","sourcesContent":["import type { IntentOperation } from \"./catalog.js\";\n\nexport const DEFAULT_CONSEQUENTIAL_TOOL_NAMES = [\"edit\", \"bash\", \"ipython\"] as const;\nexport type DefaultConsequentialToolName = (typeof DEFAULT_CONSEQUENTIAL_TOOL_NAMES)[number];\n\nexport interface ToolIntentClassification {\n\toperation: IntentOperation;\n\texternalEffect: boolean;\n\trequiresIdempotency: boolean;\n\trequiredApproval: string;\n}\n\nexport function classifyDefaultTool(toolName: string): ToolIntentClassification {\n\tswitch (toolName) {\n\t\tcase \"edit\":\n\t\t\treturn {\n\t\t\t\toperation: \"write\",\n\t\t\t\texternalEffect: false,\n\t\t\t\trequiresIdempotency: false,\n\t\t\t\trequiredApproval: \"local_write\",\n\t\t\t};\n\t\tcase \"bash\":\n\t\tcase \"ipython\":\n\t\t\treturn {\n\t\t\t\toperation: \"execute\",\n\t\t\t\texternalEffect: true,\n\t\t\t\trequiresIdempotency: true,\n\t\t\t\trequiredApproval: \"command_execution\",\n\t\t\t};\n\t\tdefault:\n\t\t\tthrow new Error(`Default tool is not classified for consequential execution: ${toolName}`);\n\t}\n}\n"]}
@@ -0,0 +1,8 @@
1
+ [
2
+ { "name": "diagnosis", "text": "diagnose the failing worker", "operation": "diagnose", "externalEffect": false },
3
+ { "name": "editing", "text": "edit file src/app.ts", "operation": "write", "externalEffect": false },
4
+ { "name": "deployment", "text": "deploy service api to production", "operation": "deploy", "externalEffect": true },
5
+ { "name": "communication", "text": "send message to the maintainer", "operation": "communicate", "externalEffect": true },
6
+ { "name": "purchase", "text": "purchase product database-plan", "operation": "purchase", "externalEffect": true },
7
+ { "name": "deletion", "text": "delete remote backup", "operation": "delete", "externalEffect": true }
8
+ ]
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@asm-agent/governance",
3
+ "version": "0.8.2",
4
+ "description": "Versioned intent parsing, policy, approval, and governed execution",
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
+ "fixtures",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "clean": "shx rm -rf dist",
21
+ "build": "tsgo -p tsconfig.build.json",
22
+ "test": "vitest --run",
23
+ "prepublishOnly": "npm run clean && npm run build"
24
+ },
25
+ "dependencies": {
26
+ "@asm-agent/contracts": "^0.8.2",
27
+ "@asm-agent/postgres": "^0.8.2"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^24.3.0",
31
+ "typescript": "^7.0.2",
32
+ "vitest": "^4.1.10"
33
+ },
34
+ "engines": {
35
+ "node": ">=22.8.0"
36
+ },
37
+ "license": "(AGPL-3.0-only OR LicenseRef-ASM-Commercial)",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/AletheionAGI/asm-agent.git",
41
+ "directory": "packages/governance"
42
+ }
43
+ }