@agent-deck/backend 1.10.1 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/execution-authority/bearer.d.ts +15 -0
  3. package/dist/execution-authority/bearer.d.ts.map +1 -0
  4. package/dist/execution-authority/bearer.js +35 -0
  5. package/dist/execution-authority/bearer.js.map +1 -0
  6. package/dist/execution-authority/index.d.ts +6 -0
  7. package/dist/execution-authority/index.d.ts.map +1 -0
  8. package/dist/execution-authority/index.js +8 -0
  9. package/dist/execution-authority/index.js.map +1 -0
  10. package/dist/execution-authority/ledger.d.ts +85 -0
  11. package/dist/execution-authority/ledger.d.ts.map +1 -0
  12. package/dist/execution-authority/ledger.js +507 -0
  13. package/dist/execution-authority/ledger.js.map +1 -0
  14. package/dist/execution-authority/store.d.ts +55 -0
  15. package/dist/execution-authority/store.d.ts.map +1 -0
  16. package/dist/execution-authority/store.js +256 -0
  17. package/dist/execution-authority/store.js.map +1 -0
  18. package/dist/execution-authority/types.d.ts +119 -0
  19. package/dist/execution-authority/types.d.ts.map +1 -0
  20. package/dist/execution-authority/types.js +9 -0
  21. package/dist/execution-authority/types.js.map +1 -0
  22. package/dist/lib/execution-authority-http.d.ts +23 -0
  23. package/dist/lib/execution-authority-http.d.ts.map +1 -0
  24. package/dist/lib/execution-authority-http.js +101 -0
  25. package/dist/lib/execution-authority-http.js.map +1 -0
  26. package/dist/lib/fatal.d.ts.map +1 -1
  27. package/dist/lib/fatal.js +12 -2
  28. package/dist/lib/fatal.js.map +1 -1
  29. package/dist/mcp-server.d.ts +6 -5
  30. package/dist/mcp-server.d.ts.map +1 -1
  31. package/dist/mcp-server.js +37 -20
  32. package/dist/mcp-server.js.map +1 -1
  33. package/dist/mcp-session-binding.d.ts +5 -1
  34. package/dist/mcp-session-binding.d.ts.map +1 -1
  35. package/dist/mcp-session-binding.js +22 -12
  36. package/dist/mcp-session-binding.js.map +1 -1
  37. package/dist/mcp-stdio.d.ts +2 -0
  38. package/dist/mcp-stdio.d.ts.map +1 -0
  39. package/dist/mcp-stdio.js.map +1 -0
  40. package/dist/mcp-tools/register.d.ts +3 -0
  41. package/dist/mcp-tools/register.d.ts.map +1 -1
  42. package/dist/mcp-tools/register.js +22 -2
  43. package/dist/mcp-tools/register.js.map +1 -1
  44. package/dist/mcp-unassigned.d.ts +14 -0
  45. package/dist/mcp-unassigned.d.ts.map +1 -0
  46. package/dist/mcp-unassigned.js +22 -0
  47. package/dist/mcp-unassigned.js.map +1 -0
  48. package/dist/playbooks/playbook-parser.d.ts +8 -0
  49. package/dist/playbooks/playbook-parser.d.ts.map +1 -0
  50. package/dist/playbooks/playbook-parser.js +76 -0
  51. package/dist/playbooks/playbook-parser.js.map +1 -0
  52. package/dist/playbooks/playbook-service.d.ts +9 -0
  53. package/dist/playbooks/playbook-service.d.ts.map +1 -0
  54. package/dist/playbooks/playbook-service.js +107 -0
  55. package/dist/playbooks/playbook-service.js.map +1 -0
  56. package/dist/routes/decks.d.ts.map +1 -1
  57. package/dist/routes/decks.js +28 -2
  58. package/dist/routes/decks.js.map +1 -1
  59. package/dist/routes/execution-authority.d.ts +8 -0
  60. package/dist/routes/execution-authority.d.ts.map +1 -0
  61. package/dist/routes/execution-authority.js +498 -0
  62. package/dist/routes/execution-authority.js.map +1 -0
  63. package/dist/scope/bindings-sidecar.d.ts +9 -0
  64. package/dist/scope/bindings-sidecar.d.ts.map +1 -0
  65. package/dist/scope/bindings-sidecar.js +57 -0
  66. package/dist/scope/bindings-sidecar.js.map +1 -0
  67. package/dist/scope/repo-deck.d.ts +10 -0
  68. package/dist/scope/repo-deck.d.ts.map +1 -0
  69. package/dist/scope/repo-deck.js +63 -0
  70. package/dist/scope/repo-deck.js.map +1 -0
  71. package/dist/test-local-mcp-e2e.d.ts +3 -0
  72. package/dist/test-local-mcp-e2e.d.ts.map +1 -0
  73. package/dist/test-local-mcp-e2e.js +104 -0
  74. package/dist/test-local-mcp-e2e.js.map +1 -0
  75. package/dist/trusted-session/auth.d.ts.map +1 -1
  76. package/dist/trusted-session/auth.js +13 -3
  77. package/dist/trusted-session/auth.js.map +1 -1
  78. package/package.json +2 -2
  79. package/static-ui/assets/index-CbccleVj.css +1 -0
  80. package/static-ui/assets/{index-Bb9ckehB.js → index-DKn5XqhM.js} +64 -64
  81. package/static-ui/favicon.png +0 -0
  82. package/static-ui/index.html +2 -2
  83. package/dist/config/mcp-endpoint.d.ts +0 -6
  84. package/dist/config/mcp-endpoint.d.ts.map +0 -1
  85. package/dist/config/mcp-endpoint.js +0 -19
  86. package/dist/config/mcp-endpoint.js.map +0 -1
  87. package/static-ui/assets/index-DRG5q9lJ.css +0 -1
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Parse coordinator enrollment / execution-authority bearer tokens.
3
+ * Forms: `enr_…:secret`, `authz_…:secret` (preferred), or raw secret with id header.
4
+ */
5
+ export type EnrollmentBearer = {
6
+ enrollmentId: string;
7
+ secret: string;
8
+ };
9
+ export type AuthorityBearer = {
10
+ authorityId: string;
11
+ secret: string;
12
+ };
13
+ export declare function parseEnrollmentBearer(token: string): EnrollmentBearer | null;
14
+ export declare function parseAuthorityBearer(token: string): AuthorityBearer | null;
15
+ //# sourceMappingURL=bearer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearer.d.ts","sourceRoot":"","sources":["../../src/execution-authority/bearer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAS5E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAS1E"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * Parse coordinator enrollment / execution-authority bearer tokens.
4
+ * Forms: `enr_…:secret`, `authz_…:secret` (preferred), or raw secret with id header.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.parseEnrollmentBearer = parseEnrollmentBearer;
8
+ exports.parseAuthorityBearer = parseAuthorityBearer;
9
+ function parseEnrollmentBearer(token) {
10
+ const trimmed = token.trim();
11
+ const colon = trimmed.indexOf(':');
12
+ if (colon <= 0)
13
+ return null;
14
+ const enrollmentId = trimmed.slice(0, colon);
15
+ if (!/^enr_[A-Za-z0-9]+$/.test(enrollmentId))
16
+ return null;
17
+ const secret = trimmed.slice(colon + 1);
18
+ if (!secret)
19
+ return null;
20
+ return { enrollmentId, secret };
21
+ }
22
+ function parseAuthorityBearer(token) {
23
+ const trimmed = token.trim();
24
+ const colon = trimmed.indexOf(':');
25
+ if (colon <= 0)
26
+ return null;
27
+ const authorityId = trimmed.slice(0, colon);
28
+ if (!/^authz_[A-Za-z0-9]+$/.test(authorityId))
29
+ return null;
30
+ const secret = trimmed.slice(colon + 1);
31
+ if (!secret)
32
+ return null;
33
+ return { authorityId, secret };
34
+ }
35
+ //# sourceMappingURL=bearer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearer.js","sourceRoot":"","sources":["../../src/execution-authority/bearer.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAYH,sDASC;AAED,oDASC;AApBD,SAAgB,qBAAqB,CAAC,KAAa;IACjD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7C,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;AAClC,CAAC;AAED,SAAgB,oBAAoB,CAAC,KAAa;IAChD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { ExecutionAuthorityLedger } from './ledger';
2
+ export type { LedgerState } from './ledger';
3
+ export { ExecutionAuthorityStore } from './store';
4
+ export type { ExecutionAuthorityStoreOptions } from './store';
5
+ export type * from './types';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/execution-authority/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAClD,YAAY,EAAE,8BAA8B,EAAE,MAAM,SAAS,CAAC;AAC9D,mBAAmB,SAAS,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExecutionAuthorityStore = exports.ExecutionAuthorityLedger = void 0;
4
+ var ledger_1 = require("./ledger");
5
+ Object.defineProperty(exports, "ExecutionAuthorityLedger", { enumerable: true, get: function () { return ledger_1.ExecutionAuthorityLedger; } });
6
+ var store_1 = require("./store");
7
+ Object.defineProperty(exports, "ExecutionAuthorityStore", { enumerable: true, get: function () { return store_1.ExecutionAuthorityStore; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/execution-authority/index.ts"],"names":[],"mappings":";;;AAAA,mCAAoD;AAA3C,kHAAA,wBAAwB,OAAA;AAEjC,iCAAkD;AAAzC,gHAAA,uBAAuB,OAAA"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * In-memory execution-authority ledger (NOT-85 logic engine).
3
+ * Durable SQLite wrapper: `ExecutionAuthorityStore` (NOT-86).
4
+ */
5
+ import type { AuditEvent, AuthorizedCallInput, ContractResult, CoordinatorEnrollment, EnrollCoordinatorResult, ExecutionAuthority, MintAuthorityInput, MintAuthorityResult } from './types';
6
+ /** Serializable ledger state for durable hydrate/persist. */
7
+ export type LedgerState = {
8
+ enrollments: CoordinatorEnrollment[];
9
+ enrollmentSecretHashes: Array<{
10
+ enrollmentId: string;
11
+ secretHash: string;
12
+ }>;
13
+ authorities: ExecutionAuthority[];
14
+ secretHashes: Array<{
15
+ authorityId: string;
16
+ secretHash: string;
17
+ }>;
18
+ mintIndex: Array<{
19
+ enrollmentId: string;
20
+ idempotencyKey: string;
21
+ authorityId: string;
22
+ }>;
23
+ audit: AuditEvent[];
24
+ };
25
+ export interface ExecutionAuthorityLedgerOptions {
26
+ /** Injectable clock for expiry tests. */
27
+ now?: () => Date;
28
+ }
29
+ /**
30
+ * Deck-side authorization ledger (skeleton). Authoritative for enrollment and
31
+ * execution authority; does not model Dealer run lifecycle.
32
+ */
33
+ export declare class ExecutionAuthorityLedger {
34
+ private readonly enrollments;
35
+ private readonly enrollmentSecretHashes;
36
+ private readonly authorities;
37
+ private readonly secretHashes;
38
+ private readonly mintIndex;
39
+ private readonly audit;
40
+ private readonly now;
41
+ constructor(options?: ExecutionAuthorityLedgerOptions);
42
+ exportState(): LedgerState;
43
+ replaceState(state: LedgerState): void;
44
+ getEnrollment(enrollmentId: string): CoordinatorEnrollment | undefined;
45
+ listEnrollments(): CoordinatorEnrollment[];
46
+ verifyEnrollmentSecret(enrollmentId: string, secret: string): boolean;
47
+ verifyAuthoritySecret(authorityId: string, secret: string): boolean;
48
+ enrollCoordinator(input: {
49
+ coordinatorId: string;
50
+ allowedDeckIds: string[];
51
+ }): ContractResult<EnrollCoordinatorResult>;
52
+ revokeEnrollment(enrollmentId: string): ContractResult<{
53
+ enrollmentId: string;
54
+ }>;
55
+ mintAuthority(input: MintAuthorityInput): ContractResult<MintAuthorityResult>;
56
+ /**
57
+ * Non-auditing lookup for ownership / existence checks.
58
+ * Does not refresh expiry (avoids authority_expired side effects during probes).
59
+ */
60
+ peekAuthority(authorityId: string): ExecutionAuthority | undefined;
61
+ inspectAuthority(authorityId: string): ContractResult<ExecutionAuthority>;
62
+ revokeAuthority(authorityId: string): ContractResult<{
63
+ authorityId: string;
64
+ }>;
65
+ /**
66
+ * Simulate one authorized Deck tool call under execution authority.
67
+ * Never waits for human approval — returns INTERACTION_REQUIRED immediately.
68
+ */
69
+ invokeAuthorizedCall(input: AuthorizedCallInput): ContractResult<{
70
+ serviceId: string;
71
+ toolName: string;
72
+ result: string;
73
+ }>;
74
+ listAuditEvents(filter?: {
75
+ runId?: string;
76
+ attemptId?: string;
77
+ authorityId?: string;
78
+ enrollmentId?: string;
79
+ requestId?: string;
80
+ }): AuditEvent[];
81
+ private toolAllowed;
82
+ private refreshAuthorityStatus;
83
+ private pushAudit;
84
+ }
85
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/execution-authority/ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,507 @@
1
+ "use strict";
2
+ /**
3
+ * In-memory execution-authority ledger (NOT-85 logic engine).
4
+ * Durable SQLite wrapper: `ExecutionAuthorityStore` (NOT-86).
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ExecutionAuthorityLedger = void 0;
8
+ const node_crypto_1 = require("node:crypto");
9
+ const shared_1 = require("@agent-deck/shared");
10
+ const store_1 = require("../trusted-session/store");
11
+ function newId(kind) {
12
+ return (0, shared_1.prefixTrustedId)(kind, (0, node_crypto_1.randomUUID)());
13
+ }
14
+ function cloneAuthority(authority) {
15
+ return {
16
+ ...authority,
17
+ allowedServices: [...authority.allowedServices],
18
+ allowedTools: authority.allowedTools.map((t) => ({ ...t })),
19
+ };
20
+ }
21
+ function toolsEqual(a, b) {
22
+ if (a.length !== b.length)
23
+ return false;
24
+ const key = (t) => `${t.serviceId}\0${t.toolName}`;
25
+ const sortedA = [...a].map(key).sort();
26
+ const sortedB = [...b].map(key).sort();
27
+ return sortedA.every((v, i) => v === sortedB[i]);
28
+ }
29
+ function mintParamsMatch(existing, input) {
30
+ const storedTtlMs = Date.parse(existing.expiresAt) - Date.parse(existing.issuedAt);
31
+ return (existing.runId === input.runId &&
32
+ existing.attemptId === input.attemptId &&
33
+ existing.deckId === input.deckId &&
34
+ existing.audience === input.audience &&
35
+ storedTtlMs === input.ttlMs &&
36
+ existing.allowedServices.length === input.allowedServices.length &&
37
+ existing.allowedServices.every((s) => input.allowedServices.includes(s)) &&
38
+ toolsEqual(existing.allowedTools, input.allowedTools));
39
+ }
40
+ /**
41
+ * Deck-side authorization ledger (skeleton). Authoritative for enrollment and
42
+ * execution authority; does not model Dealer run lifecycle.
43
+ */
44
+ class ExecutionAuthorityLedger {
45
+ enrollments = new Map();
46
+ enrollmentSecretHashes = new Map();
47
+ authorities = new Map();
48
+ secretHashes = new Map();
49
+ mintIndex = new Map(); // enrollmentId\0idempotencyKey -> authorityId
50
+ audit = [];
51
+ now;
52
+ constructor(options = {}) {
53
+ this.now = options.now ?? (() => new Date());
54
+ }
55
+ exportState() {
56
+ return {
57
+ enrollments: [...this.enrollments.values()].map((e) => ({
58
+ ...e,
59
+ allowedDeckIds: [...e.allowedDeckIds],
60
+ })),
61
+ enrollmentSecretHashes: [...this.enrollmentSecretHashes.entries()].map(([enrollmentId, secretHash]) => ({ enrollmentId, secretHash })),
62
+ authorities: [...this.authorities.values()].map(cloneAuthority),
63
+ secretHashes: [...this.secretHashes.entries()].map(([authorityId, secretHash]) => ({
64
+ authorityId,
65
+ secretHash,
66
+ })),
67
+ mintIndex: [...this.mintIndex.entries()].map(([key, authorityId]) => {
68
+ const [enrollmentId, idempotencyKey] = key.split('\0');
69
+ return { enrollmentId, idempotencyKey, authorityId };
70
+ }),
71
+ audit: this.audit.map((event) => ({
72
+ ...event,
73
+ correlation: { ...event.correlation },
74
+ detail: event.detail ? { ...event.detail } : undefined,
75
+ })),
76
+ };
77
+ }
78
+ replaceState(state) {
79
+ this.enrollments.clear();
80
+ this.enrollmentSecretHashes.clear();
81
+ this.authorities.clear();
82
+ this.secretHashes.clear();
83
+ this.mintIndex.clear();
84
+ this.audit.length = 0;
85
+ for (const enrollment of state.enrollments) {
86
+ this.enrollments.set(enrollment.enrollmentId, {
87
+ ...enrollment,
88
+ allowedDeckIds: [...enrollment.allowedDeckIds],
89
+ });
90
+ }
91
+ for (const row of state.enrollmentSecretHashes) {
92
+ this.enrollmentSecretHashes.set(row.enrollmentId, row.secretHash);
93
+ }
94
+ for (const authority of state.authorities) {
95
+ this.authorities.set(authority.authorityId, cloneAuthority(authority));
96
+ }
97
+ for (const row of state.secretHashes) {
98
+ this.secretHashes.set(row.authorityId, row.secretHash);
99
+ }
100
+ for (const row of state.mintIndex) {
101
+ this.mintIndex.set(`${row.enrollmentId}\0${row.idempotencyKey}`, row.authorityId);
102
+ }
103
+ for (const event of state.audit) {
104
+ this.audit.push({
105
+ ...event,
106
+ correlation: { ...event.correlation },
107
+ detail: event.detail ? { ...event.detail } : undefined,
108
+ });
109
+ }
110
+ }
111
+ getEnrollment(enrollmentId) {
112
+ const enrollment = this.enrollments.get(enrollmentId);
113
+ if (!enrollment)
114
+ return undefined;
115
+ return { ...enrollment, allowedDeckIds: [...enrollment.allowedDeckIds] };
116
+ }
117
+ listEnrollments() {
118
+ return [...this.enrollments.values()].map((e) => ({
119
+ ...e,
120
+ allowedDeckIds: [...e.allowedDeckIds],
121
+ }));
122
+ }
123
+ verifyEnrollmentSecret(enrollmentId, secret) {
124
+ const hash = this.enrollmentSecretHashes.get(enrollmentId);
125
+ if (!hash)
126
+ return false;
127
+ return (0, store_1.verifyGrantSecret)(secret, hash);
128
+ }
129
+ verifyAuthoritySecret(authorityId, secret) {
130
+ const hash = this.secretHashes.get(authorityId);
131
+ if (!hash)
132
+ return false;
133
+ return (0, store_1.verifyGrantSecret)(secret, hash);
134
+ }
135
+ enrollCoordinator(input) {
136
+ const enrollment = {
137
+ enrollmentId: newId('enr'),
138
+ coordinatorId: input.coordinatorId,
139
+ status: 'active',
140
+ allowedDeckIds: [...input.allowedDeckIds],
141
+ createdAt: this.now().toISOString(),
142
+ revokedAt: null,
143
+ };
144
+ const enrollmentSecret = `enrs_${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
145
+ this.enrollments.set(enrollment.enrollmentId, enrollment);
146
+ this.enrollmentSecretHashes.set(enrollment.enrollmentId, (0, store_1.hashGrantSecret)(enrollmentSecret));
147
+ this.pushAudit('enrollment_created', {
148
+ enrollmentId: enrollment.enrollmentId,
149
+ }, { coordinatorId: input.coordinatorId });
150
+ return {
151
+ ok: true,
152
+ data: {
153
+ enrollment: {
154
+ ...enrollment,
155
+ allowedDeckIds: [...enrollment.allowedDeckIds],
156
+ },
157
+ enrollmentSecret,
158
+ },
159
+ };
160
+ }
161
+ revokeEnrollment(enrollmentId) {
162
+ const enrollment = this.enrollments.get(enrollmentId);
163
+ if (!enrollment) {
164
+ return {
165
+ ok: false,
166
+ error_code: 'COORDINATOR_NOT_ENROLLED',
167
+ message: 'Unknown enrollment',
168
+ correlation: { enrollmentId },
169
+ };
170
+ }
171
+ if (enrollment.status === 'revoked') {
172
+ return { ok: true, data: { enrollmentId } };
173
+ }
174
+ enrollment.status = 'revoked';
175
+ enrollment.revokedAt = this.now().toISOString();
176
+ this.pushAudit('enrollment_revoked', { enrollmentId });
177
+ for (const stored of this.authorities.values()) {
178
+ if (stored.enrollmentId !== enrollmentId)
179
+ continue;
180
+ const authority = this.refreshAuthorityStatus(stored.authorityId);
181
+ if (!authority || authority.status !== 'live')
182
+ continue;
183
+ authority.status = 'revoked';
184
+ this.pushAudit('authority_revoked', {
185
+ enrollmentId,
186
+ authorityId: authority.authorityId,
187
+ runId: authority.runId,
188
+ attemptId: authority.attemptId,
189
+ deckId: authority.deckId,
190
+ }, { reason: 'enrollment_revoked' });
191
+ }
192
+ return { ok: true, data: { enrollmentId } };
193
+ }
194
+ mintAuthority(input) {
195
+ if (!(input.ttlMs > 0)) {
196
+ return {
197
+ ok: false,
198
+ error_code: 'INVALID_MINT_REQUEST',
199
+ message: 'ttlMs must be positive',
200
+ reason: 'ttl_non_positive',
201
+ correlation: { enrollmentId: input.enrollmentId },
202
+ };
203
+ }
204
+ const enrollment = this.enrollments.get(input.enrollmentId);
205
+ if (!enrollment) {
206
+ return {
207
+ ok: false,
208
+ error_code: 'COORDINATOR_NOT_ENROLLED',
209
+ message: 'Unknown enrollment',
210
+ correlation: { enrollmentId: input.enrollmentId },
211
+ };
212
+ }
213
+ if (enrollment.status !== 'active') {
214
+ return {
215
+ ok: false,
216
+ error_code: 'ENROLLMENT_REVOKED',
217
+ message: 'Enrollment is revoked',
218
+ correlation: { enrollmentId: input.enrollmentId },
219
+ };
220
+ }
221
+ if (!enrollment.allowedDeckIds.includes(input.deckId)) {
222
+ return {
223
+ ok: false,
224
+ error_code: 'RESOURCE_OUT_OF_SCOPE',
225
+ message: 'Deck not permitted for this enrollment',
226
+ reason: 'deck_not_permitted',
227
+ correlation: { enrollmentId: input.enrollmentId, deckId: input.deckId },
228
+ };
229
+ }
230
+ const mintKey = `${input.enrollmentId}\0${input.idempotencyKey}`;
231
+ const existingId = this.mintIndex.get(mintKey);
232
+ if (existingId) {
233
+ const existing = this.refreshAuthorityStatus(existingId);
234
+ if (!mintParamsMatch(existing, input)) {
235
+ return {
236
+ ok: false,
237
+ error_code: 'IDEMPOTENCY_KEY_CONFLICT',
238
+ message: 'Idempotency key reused with different mint parameters',
239
+ reason: 'params_mismatch',
240
+ correlation: {
241
+ enrollmentId: existing.enrollmentId,
242
+ authorityId: existing.authorityId,
243
+ runId: existing.runId,
244
+ attemptId: existing.attemptId,
245
+ deckId: existing.deckId,
246
+ },
247
+ };
248
+ }
249
+ if (existing.status === 'live') {
250
+ // Idempotent remint does not re-issue the secret (Dealer must retain launcher handle).
251
+ return {
252
+ ok: true,
253
+ data: {
254
+ authority: cloneAuthority(existing),
255
+ authoritySecret: null,
256
+ secretIssued: false,
257
+ },
258
+ };
259
+ }
260
+ return {
261
+ ok: false,
262
+ error_code: existing.status === 'expired' ? 'AUTHORITY_EXPIRED' : 'AUTHORITY_REVOKED',
263
+ message: `Authority already ${existing.status}`,
264
+ reason: existing.status === 'revoked' ? 'explicit_revoke' : undefined,
265
+ correlation: {
266
+ enrollmentId: existing.enrollmentId,
267
+ authorityId: existing.authorityId,
268
+ runId: existing.runId,
269
+ attemptId: existing.attemptId,
270
+ deckId: existing.deckId,
271
+ },
272
+ };
273
+ }
274
+ const issuedAt = this.now();
275
+ const authority = {
276
+ authorityId: newId('authz'),
277
+ enrollmentId: input.enrollmentId,
278
+ runId: input.runId,
279
+ attemptId: input.attemptId,
280
+ deckId: input.deckId,
281
+ audience: input.audience,
282
+ allowedServices: [...input.allowedServices],
283
+ allowedTools: input.allowedTools.map((t) => ({ ...t })),
284
+ issuedAt: issuedAt.toISOString(),
285
+ expiresAt: new Date(issuedAt.getTime() + input.ttlMs).toISOString(),
286
+ status: 'live',
287
+ idempotencyKey: input.idempotencyKey,
288
+ };
289
+ const authoritySecret = `seas_${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
290
+ this.authorities.set(authority.authorityId, authority);
291
+ this.secretHashes.set(authority.authorityId, (0, store_1.hashGrantSecret)(authoritySecret));
292
+ this.mintIndex.set(mintKey, authority.authorityId);
293
+ this.pushAudit('authority_minted', {
294
+ enrollmentId: authority.enrollmentId,
295
+ authorityId: authority.authorityId,
296
+ runId: authority.runId,
297
+ attemptId: authority.attemptId,
298
+ deckId: authority.deckId,
299
+ });
300
+ return {
301
+ ok: true,
302
+ data: {
303
+ authority: cloneAuthority(authority),
304
+ authoritySecret,
305
+ secretIssued: true,
306
+ },
307
+ };
308
+ }
309
+ /**
310
+ * Non-auditing lookup for ownership / existence checks.
311
+ * Does not refresh expiry (avoids authority_expired side effects during probes).
312
+ */
313
+ peekAuthority(authorityId) {
314
+ const authority = this.authorities.get(authorityId);
315
+ return authority ? cloneAuthority(authority) : undefined;
316
+ }
317
+ inspectAuthority(authorityId) {
318
+ const authority = this.refreshAuthorityStatus(authorityId);
319
+ if (!authority) {
320
+ return {
321
+ ok: false,
322
+ error_code: 'AUTHORITY_UNKNOWN',
323
+ message: 'Unknown authority',
324
+ correlation: { authorityId },
325
+ };
326
+ }
327
+ this.pushAudit('authority_inspected', {
328
+ enrollmentId: authority.enrollmentId,
329
+ authorityId: authority.authorityId,
330
+ runId: authority.runId,
331
+ attemptId: authority.attemptId,
332
+ deckId: authority.deckId,
333
+ });
334
+ return { ok: true, data: cloneAuthority(authority) };
335
+ }
336
+ revokeAuthority(authorityId) {
337
+ const authority = this.authorities.get(authorityId);
338
+ if (!authority) {
339
+ return {
340
+ ok: false,
341
+ error_code: 'AUTHORITY_UNKNOWN',
342
+ message: 'Unknown authority',
343
+ correlation: { authorityId },
344
+ };
345
+ }
346
+ if (authority.status !== 'revoked') {
347
+ authority.status = 'revoked';
348
+ this.pushAudit('authority_revoked', {
349
+ enrollmentId: authority.enrollmentId,
350
+ authorityId: authority.authorityId,
351
+ runId: authority.runId,
352
+ attemptId: authority.attemptId,
353
+ deckId: authority.deckId,
354
+ }, { reason: 'explicit_revoke' });
355
+ }
356
+ return { ok: true, data: { authorityId } };
357
+ }
358
+ /**
359
+ * Simulate one authorized Deck tool call under execution authority.
360
+ * Never waits for human approval — returns INTERACTION_REQUIRED immediately.
361
+ */
362
+ invokeAuthorizedCall(input) {
363
+ const authority = this.refreshAuthorityStatus(input.authorityId);
364
+ const correlation = {
365
+ authorityId: input.authorityId,
366
+ };
367
+ if (!authority) {
368
+ return {
369
+ ok: false,
370
+ error_code: 'AUTHORITY_UNKNOWN',
371
+ message: 'Unknown authority',
372
+ correlation,
373
+ };
374
+ }
375
+ correlation.enrollmentId = authority.enrollmentId;
376
+ correlation.runId = authority.runId;
377
+ correlation.attemptId = authority.attemptId;
378
+ correlation.deckId = authority.deckId;
379
+ const expectedHash = this.secretHashes.get(authority.authorityId);
380
+ if (!expectedHash || !(0, store_1.verifyGrantSecret)(input.authoritySecret, expectedHash)) {
381
+ this.pushAudit('call_denied', correlation, { reason: 'bad_secret' });
382
+ return {
383
+ ok: false,
384
+ error_code: 'AUTHORITY_SECRET_INVALID',
385
+ message: 'Invalid authority secret',
386
+ correlation,
387
+ };
388
+ }
389
+ if (input.audience !== authority.audience) {
390
+ this.pushAudit('call_denied', correlation, {
391
+ reason: 'audience_mismatch',
392
+ expected: authority.audience,
393
+ actual: input.audience,
394
+ });
395
+ return {
396
+ ok: false,
397
+ error_code: 'AUDIENCE_MISMATCH',
398
+ message: 'Caller audience does not match authority audience',
399
+ correlation,
400
+ };
401
+ }
402
+ if (authority.status === 'expired') {
403
+ this.pushAudit('call_denied', correlation, { reason: 'expired' });
404
+ return {
405
+ ok: false,
406
+ error_code: 'AUTHORITY_EXPIRED',
407
+ message: 'Authority expired',
408
+ correlation,
409
+ };
410
+ }
411
+ if (authority.status === 'revoked') {
412
+ this.pushAudit('call_denied', correlation, { reason: 'revoked' });
413
+ return {
414
+ ok: false,
415
+ error_code: 'AUTHORITY_REVOKED',
416
+ message: 'Authority revoked',
417
+ reason: 'explicit_revoke',
418
+ correlation,
419
+ };
420
+ }
421
+ if (input.requiresInteraction) {
422
+ const requestId = newId('req');
423
+ correlation.requestId = requestId;
424
+ this.pushAudit('call_denied', correlation, { reason: 'interaction_required' });
425
+ return {
426
+ ok: false,
427
+ error_code: 'INTERACTION_REQUIRED',
428
+ message: 'Control-plane decision required; do not hold the worker',
429
+ correlation,
430
+ };
431
+ }
432
+ if (!this.toolAllowed(authority, input.serviceId, input.toolName)) {
433
+ this.pushAudit('call_denied', correlation, {
434
+ reason: 'tool_not_in_snapshot',
435
+ serviceId: input.serviceId,
436
+ toolName: input.toolName,
437
+ });
438
+ return {
439
+ ok: false,
440
+ error_code: 'RESOURCE_OUT_OF_SCOPE',
441
+ message: 'Tool not in authority snapshot',
442
+ reason: 'tool_not_in_snapshot',
443
+ correlation,
444
+ };
445
+ }
446
+ this.pushAudit('call_allowed', correlation, {
447
+ serviceId: input.serviceId,
448
+ toolName: input.toolName,
449
+ });
450
+ return {
451
+ ok: true,
452
+ data: {
453
+ serviceId: input.serviceId,
454
+ toolName: input.toolName,
455
+ result: 'ok',
456
+ },
457
+ };
458
+ }
459
+ listAuditEvents(filter = {}) {
460
+ return this.audit.filter((event) => {
461
+ const c = event.correlation;
462
+ if (filter.runId && c.runId !== filter.runId)
463
+ return false;
464
+ if (filter.attemptId && c.attemptId !== filter.attemptId)
465
+ return false;
466
+ if (filter.authorityId && c.authorityId !== filter.authorityId)
467
+ return false;
468
+ if (filter.enrollmentId && c.enrollmentId !== filter.enrollmentId)
469
+ return false;
470
+ if (filter.requestId && c.requestId !== filter.requestId)
471
+ return false;
472
+ return true;
473
+ });
474
+ }
475
+ toolAllowed(authority, serviceId, toolName) {
476
+ if (!authority.allowedServices.includes(serviceId))
477
+ return false;
478
+ return authority.allowedTools.some((t) => t.serviceId === serviceId && t.toolName === toolName);
479
+ }
480
+ refreshAuthorityStatus(authorityId) {
481
+ const authority = this.authorities.get(authorityId);
482
+ if (!authority)
483
+ return undefined;
484
+ if (authority.status === 'live' && Date.parse(authority.expiresAt) <= this.now().getTime()) {
485
+ authority.status = 'expired';
486
+ this.pushAudit('authority_expired', {
487
+ enrollmentId: authority.enrollmentId,
488
+ authorityId: authority.authorityId,
489
+ runId: authority.runId,
490
+ attemptId: authority.attemptId,
491
+ deckId: authority.deckId,
492
+ });
493
+ }
494
+ return authority;
495
+ }
496
+ pushAudit(kind, correlation, detail) {
497
+ this.audit.push({
498
+ eventId: newId('evt'),
499
+ at: this.now().toISOString(),
500
+ kind,
501
+ correlation: { ...correlation },
502
+ detail,
503
+ });
504
+ }
505
+ }
506
+ exports.ExecutionAuthorityLedger = ExecutionAuthorityLedger;
507
+ //# sourceMappingURL=ledger.js.map