@victframework/runtime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/agent-governance.d.ts +285 -0
  2. package/dist/agent-governance.js +438 -0
  3. package/dist/agent-governance.js.map +1 -0
  4. package/dist/agent-registry.d.ts +103 -0
  5. package/dist/agent-registry.js +758 -0
  6. package/dist/agent-registry.js.map +1 -0
  7. package/dist/agent-types.d.ts +433 -0
  8. package/dist/agent-types.js +480 -0
  9. package/dist/agent-types.js.map +1 -0
  10. package/dist/authority.d.ts +83 -0
  11. package/dist/authority.js +170 -0
  12. package/dist/authority.js.map +1 -0
  13. package/dist/boundary-conformance.d.ts +51 -0
  14. package/dist/boundary-conformance.js +364 -0
  15. package/dist/boundary-conformance.js.map +1 -0
  16. package/dist/control-conformance.d.ts +68 -0
  17. package/dist/control-conformance.js +1131 -0
  18. package/dist/control-conformance.js.map +1 -0
  19. package/dist/control-in-memory.d.ts +257 -0
  20. package/dist/control-in-memory.js +886 -0
  21. package/dist/control-in-memory.js.map +1 -0
  22. package/dist/control-types.d.ts +1017 -0
  23. package/dist/control-types.js +779 -0
  24. package/dist/control-types.js.map +1 -0
  25. package/dist/durable-run.d.ts +73 -0
  26. package/dist/durable-run.js +262 -0
  27. package/dist/durable-run.js.map +1 -0
  28. package/dist/effect-policy.d.ts +22 -0
  29. package/dist/effect-policy.js +45 -0
  30. package/dist/effect-policy.js.map +1 -0
  31. package/dist/errors.d.ts +36 -0
  32. package/dist/errors.js +71 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/in-memory-stores.d.ts +16 -0
  35. package/dist/in-memory-stores.js +522 -0
  36. package/dist/in-memory-stores.js.map +1 -0
  37. package/dist/index.d.ts +41 -0
  38. package/dist/index.js +26 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/operator-config.d.ts +141 -0
  41. package/dist/operator-config.js +289 -0
  42. package/dist/operator-config.js.map +1 -0
  43. package/dist/orchestration-activation.d.ts +116 -0
  44. package/dist/orchestration-activation.js +223 -0
  45. package/dist/orchestration-activation.js.map +1 -0
  46. package/dist/orchestration-canary-conformance.d.ts +10 -0
  47. package/dist/orchestration-canary-conformance.js +726 -0
  48. package/dist/orchestration-canary-conformance.js.map +1 -0
  49. package/dist/orchestration-commands.d.ts +47 -0
  50. package/dist/orchestration-commands.js +538 -0
  51. package/dist/orchestration-commands.js.map +1 -0
  52. package/dist/orchestration-conformance.d.ts +59 -0
  53. package/dist/orchestration-conformance.js +594 -0
  54. package/dist/orchestration-conformance.js.map +1 -0
  55. package/dist/orchestration-driver-types.d.ts +163 -0
  56. package/dist/orchestration-driver-types.js +21 -0
  57. package/dist/orchestration-driver-types.js.map +1 -0
  58. package/dist/orchestration-driver.d.ts +48 -0
  59. package/dist/orchestration-driver.js +1030 -0
  60. package/dist/orchestration-driver.js.map +1 -0
  61. package/dist/orchestration-in-memory.d.ts +18 -0
  62. package/dist/orchestration-in-memory.js +1313 -0
  63. package/dist/orchestration-in-memory.js.map +1 -0
  64. package/dist/orchestration-join-conformance.d.ts +20 -0
  65. package/dist/orchestration-join-conformance.js +542 -0
  66. package/dist/orchestration-join-conformance.js.map +1 -0
  67. package/dist/orchestration-plan.d.ts +52 -0
  68. package/dist/orchestration-plan.js +265 -0
  69. package/dist/orchestration-plan.js.map +1 -0
  70. package/dist/orchestration-race-conformance.d.ts +35 -0
  71. package/dist/orchestration-race-conformance.js +1265 -0
  72. package/dist/orchestration-race-conformance.js.map +1 -0
  73. package/dist/orchestration-remediation-conformance.d.ts +54 -0
  74. package/dist/orchestration-remediation-conformance.js +632 -0
  75. package/dist/orchestration-remediation-conformance.js.map +1 -0
  76. package/dist/orchestration-store-types.d.ts +568 -0
  77. package/dist/orchestration-store-types.js +2 -0
  78. package/dist/orchestration-store-types.js.map +1 -0
  79. package/dist/pack-conformance.d.ts +59 -0
  80. package/dist/pack-conformance.js +222 -0
  81. package/dist/pack-conformance.js.map +1 -0
  82. package/dist/pack-install.d.ts +44 -0
  83. package/dist/pack-install.js +126 -0
  84. package/dist/pack-install.js.map +1 -0
  85. package/dist/registry.d.ts +107 -0
  86. package/dist/registry.js +481 -0
  87. package/dist/registry.js.map +1 -0
  88. package/dist/runtime.d.ts +158 -0
  89. package/dist/runtime.js +1341 -0
  90. package/dist/runtime.js.map +1 -0
  91. package/dist/serialization.d.ts +26 -0
  92. package/dist/serialization.js +162 -0
  93. package/dist/serialization.js.map +1 -0
  94. package/dist/store-conformance.d.ts +45 -0
  95. package/dist/store-conformance.js +876 -0
  96. package/dist/store-conformance.js.map +1 -0
  97. package/dist/store-errors.d.ts +49 -0
  98. package/dist/store-errors.js +48 -0
  99. package/dist/store-errors.js.map +1 -0
  100. package/dist/store-types.d.ts +331 -0
  101. package/dist/store-types.js +14 -0
  102. package/dist/store-types.js.map +1 -0
  103. package/dist/store-validation.d.ts +63 -0
  104. package/dist/store-validation.js +164 -0
  105. package/dist/store-validation.js.map +1 -0
  106. package/dist/stream-hub.d.ts +151 -0
  107. package/dist/stream-hub.js +402 -0
  108. package/dist/stream-hub.js.map +1 -0
  109. package/dist/testing.d.ts +25 -0
  110. package/dist/testing.js +19 -0
  111. package/dist/testing.js.map +1 -0
  112. package/dist/types.d.ts +221 -0
  113. package/dist/types.js +2 -0
  114. package/dist/types.js.map +1 -0
  115. package/package.json +45 -0
@@ -0,0 +1,1017 @@
1
+ import { type AgentStreamEvent, type AgentStreamEventKind } from '@victframework/contracts';
2
+ import type { EffectClass } from '@victframework/kernel';
3
+ /**
4
+ * Stage 06B — neutral control-plane and governed-remote-execution ports.
5
+ *
6
+ * This module defines the durable records, closed state machines, and store
7
+ * ports for:
8
+ *
9
+ * - authenticated actors, roles, and scopes (SEC-001/SEC-002; default deny);
10
+ * - ChangeSets with immutable content identity, evidence, approvals,
11
+ * expiry, and forward-only status (CTRL-001..CTRL-007);
12
+ * - Application Release publish/select/rollback over immutable versions;
13
+ * - audit events for every attributable transition;
14
+ * - durable agent turns, tool invocations (durable-before-invocation), and
15
+ * VICT-authoritative approval records (AI-006/AI-007/MSTR-005);
16
+ * - the durable agent-stream ledger (per-stream monotonic sequences,
17
+ * durable milestones, transient deltas — AI-009, `vict.agent-stream@1`).
18
+ *
19
+ * In-memory reference implementations live in `control-in-memory.ts`; the
20
+ * SQLite adapter implements the same ports in `@victframework/store-sqlite` and both
21
+ * pass the shared conformance suite (`control-conformance.ts`).
22
+ *
23
+ * These are NEUTRAL ports: no Mastra, HTTP, transport, or provider type
24
+ * appears here.
25
+ */
26
+ /** Bounded namespace identifier for control-plane and agent records. */
27
+ export declare const CONTROL_ID_PATTERN: RegExp;
28
+ /** Validate one bounded namespace identifier (throws a stable error). */
29
+ export declare function assertControlId(value: unknown, field: string): asserts value is string;
30
+ /** Stable bounded safe-integer validation. */
31
+ export declare function assertControlTimestamp(value: unknown, field: string): asserts value is number;
32
+ /**
33
+ * Stable content identity over strict canonical data. The canonicalizer is
34
+ * the Stage 01 verified canonical JSON; identity MUST NOT be derived from
35
+ * function text, timestamps of capture, or mutable objects.
36
+ */
37
+ export declare function controlContentHash(payload: unknown): string;
38
+ /** Bounded safe string field (rationales, summaries, reason codes). */
39
+ export declare function assertBoundedString(value: unknown, field: string, max: number, allowEmpty?: boolean): asserts value is string;
40
+ /** The closed reference role vocabulary (system reference §14.2). */
41
+ export declare const ACTOR_ROLES: readonly ["viewer", "developer", "operator", "approver", "administrator"];
42
+ export type ActorRole = (typeof ACTOR_ROLES)[number];
43
+ /** The closed scope vocabulary enforced below the HTTP/CLI/UI layers. */
44
+ export declare const ACTOR_SCOPES: readonly ["changeset.propose", "changeset.revise", "changeset.approve", "changeset.commit", "changeset.read", "release.publish", "release.select", "release.read", "activation.select", "activation.read", "run.read", "run.cancel", "operator.resolve", "agent.turn.start", "agent.turn.cancel", "agent.tool.approve", "agent.tool.decline", "agent.stream.read", "app.data.read", "app.data.write", "conversation.read", "conversation.delete", "audit.read"];
45
+ export type ActorScope = (typeof ACTOR_SCOPES)[number];
46
+ /** Deterministic role → scope policy (default policy is denial). */
47
+ export declare const ROLE_SCOPES: Readonly<Record<ActorRole, readonly ActorScope[]>>;
48
+ export type ActorStatus = 'active' | 'disabled';
49
+ /** One authoritative actor record (server-side truth, never client-supplied). */
50
+ export interface ActorRecord {
51
+ readonly actorId: string;
52
+ readonly status: ActorStatus;
53
+ readonly roles: readonly ActorRole[];
54
+ /** Epoch-ms creation time from the injected clock. */
55
+ readonly createdAt: number;
56
+ }
57
+ /** Port resolving actor records from the authoritative directory. */
58
+ export interface ActorDirectory {
59
+ get(actorId: string): Promise<ActorRecord | undefined>;
60
+ list(): Promise<readonly ActorRecord[]>;
61
+ upsert(record: ActorRecord): Promise<void>;
62
+ }
63
+ /**
64
+ * Derive the permitted scopes of one actor record. Unknown, disabled, and
65
+ * malformed actors fail closed: no scopes are derived. Roles outside the
66
+ * closed vocabulary are ignored (fail closed, never widened).
67
+ */
68
+ export declare function authoritativeScopes(actor: ActorRecord | undefined): readonly ActorScope[];
69
+ /** Authoritative server-side actor context (the ONLY identity authority). */
70
+ export interface AuthenticatedActorContext {
71
+ readonly actorId: string;
72
+ readonly roles: readonly ActorRole[];
73
+ readonly scopes: readonly ActorScope[];
74
+ /**
75
+ * The permitted Mastra memory identity, derived ONLY from the
76
+ * authenticated VICT actor (MSTR-007): `vict-actor-<actorId>`.
77
+ */
78
+ readonly mastraResourceId: string;
79
+ }
80
+ /**
81
+ * Derive the authoritative server context from a resolved actor record.
82
+ * Unknown, disabled, malformed, or mismatched actors fail closed.
83
+ */
84
+ export declare function authenticatedActorContext(actor: ActorRecord | undefined, requestedActorId: string): AuthenticatedActorContext;
85
+ /** Stable authorization denial (never echoes the attempted operation's payload). */
86
+ export declare class ActorScopeDeniedError extends Error {
87
+ readonly code = "VICT_ACTOR_SCOPE_DENIED";
88
+ readonly scope: string;
89
+ constructor(scope: string);
90
+ }
91
+ /** Enforce one scope (fail closed; stable non-echoing denial). */
92
+ export declare function assertActorScope(context: AuthenticatedActorContext, scope: ActorScope): void;
93
+ /** In-memory ActorDirectory (tests and local compositions). */
94
+ export declare class InMemoryActorDirectory implements ActorDirectory {
95
+ #private;
96
+ get(actorId: string): Promise<ActorRecord | undefined>;
97
+ list(): Promise<readonly ActorRecord[]>;
98
+ upsert(record: ActorRecord): Promise<void>;
99
+ }
100
+ /** The versioned ChangeSet schema marker. */
101
+ export declare const CHANGESET_SCHEMA = "vict.changeset@1";
102
+ /** Risk/effect classification of a proposed change. */
103
+ export type ChangeSetRiskClass = 'low' | 'medium' | 'high';
104
+ /** Closed typed operation: a reference to immutable published identity. */
105
+ export type ChangeSetOperation = {
106
+ readonly kind: 'select-activation';
107
+ readonly graphId: string;
108
+ readonly activationVersion: string;
109
+ } | {
110
+ kind: 'rollback-activation';
111
+ graphId: string;
112
+ targetActivationVersion: string;
113
+ } | {
114
+ kind: 'publish-and-select-release';
115
+ release: ApplicationReleaseContent;
116
+ } | {
117
+ kind: 'select-release';
118
+ applicationId: string;
119
+ releaseVersion: string;
120
+ } | {
121
+ kind: 'rollback-release';
122
+ applicationId: string;
123
+ targetReleaseVersion: string;
124
+ };
125
+ /** The closed immutable release content a ChangeSet may publish. */
126
+ export type ApplicationReleaseContent = Pick<ApplicationReleaseRecord, 'releaseVersion' | 'applicationId' | 'applicationVersion' | 'rendererIdentity' | 'componentRegistryIdentity' | 'dataAdapterIdentity' | 'activationBinding'>;
127
+ /** Validation evidence attached to a ChangeSet (bound to an EXECUTED run). */
128
+ export interface ChangeSetValidationEvidence {
129
+ /** Identity of the authoritative VICT-executed validation run. */
130
+ readonly runId: string;
131
+ readonly outcome: 'passed' | 'failed';
132
+ readonly recordedAt: number;
133
+ /** The EXACT ChangeSet content identity the run was executed against. */
134
+ readonly contentHash: string;
135
+ /** The EXACT expected base identity the run was executed against. */
136
+ readonly base: ChangeSetBase;
137
+ /** The runner/profile version identity that executed the run. */
138
+ readonly runnerProfile: string;
139
+ /** The authenticated actor for whom the run was executed. */
140
+ readonly actorId: string;
141
+ }
142
+ /** Simulation evidence attached to a ChangeSet (bound to an EXECUTED run). */
143
+ export interface ChangeSetSimulationEvidence {
144
+ /** Identity of the authoritative VICT-executed simulation run. */
145
+ readonly runId: string;
146
+ readonly outcome: 'passed' | 'failed' | 'blocked';
147
+ readonly recordedAt: number;
148
+ /** The EXACT ChangeSet content identity the run was executed against. */
149
+ readonly contentHash: string;
150
+ /** The EXACT expected base identity the run was executed against. */
151
+ readonly base: ChangeSetBase;
152
+ /** The runner/profile version identity that executed the run. */
153
+ readonly runnerProfile: string;
154
+ /** The authenticated actor for whom the run was executed. */
155
+ readonly actorId: string;
156
+ }
157
+ /** Forward-only ChangeSet status. `applying` is a DURABLE, non-final
158
+ * state: a commit whose operations are being applied (saga). A failure
159
+ * inside `applying` leaves the record truthfully `applying` — never a
160
+ * falsely final state — and deterministic recovery completes it. */
161
+ export type ChangeSetStatus = 'draft' | 'approved' | 'applying' | 'committed' | 'declined' | 'expired';
162
+ /** One proposed, version-guarded control-plane change. */
163
+ export interface ChangeSetRecord {
164
+ readonly changesetId: string;
165
+ readonly schema: typeof CHANGESET_SCHEMA;
166
+ readonly authorActorId: string;
167
+ readonly createdAt: number;
168
+ /** The exact expected base identity/version (concurrency guard). */
169
+ readonly base: ChangeSetBase;
170
+ /** Closed, typed operations (never executable functions or JSON patches). */
171
+ readonly operations: readonly ChangeSetOperation[];
172
+ /** Rationale or safe reference (bounded, no payloads). */
173
+ readonly rationale: string;
174
+ readonly riskClass: ChangeSetRiskClass;
175
+ /** Required approval policy (count of distinct approvers). */
176
+ readonly requiredApproverCount: number;
177
+ /** Epoch-ms expiry of the proposal. */
178
+ readonly expiresAt: number;
179
+ readonly validation: ChangeSetValidationEvidence | undefined;
180
+ readonly simulation: ChangeSetSimulationEvidence | undefined;
181
+ /** Immutable content identity — approvals bind to EXACTLY this hash. */
182
+ readonly contentHash: string;
183
+ readonly status: ChangeSetStatus;
184
+ }
185
+ /** The sentinel base version meaning 'no version is currently selected'. */
186
+ export declare const CHANGESET_BASE_NONE = "none";
187
+ /** The expected base identity/version of a ChangeSet. */
188
+ export interface ChangeSetBase {
189
+ readonly kind: 'activation' | 'release';
190
+ /** The graphId (for activations) or applicationId (for releases). */
191
+ readonly subjectId: string;
192
+ /** The expected currently-selected version at proposal and commit time. */
193
+ readonly expectedVersion: string;
194
+ }
195
+ /** One control-plane approval decision (bound to the exact content hash). */
196
+ export interface ChangeSetApprovalDecision {
197
+ readonly approvalId: string;
198
+ readonly changesetId: string;
199
+ /** The exact content hash the decision binds to. */
200
+ readonly contentHash: string;
201
+ readonly approverActorId: string;
202
+ readonly decision: 'approved' | 'declined';
203
+ readonly decidedAt: number;
204
+ }
205
+ /** The closed governance-run vocabulary. */
206
+ export type ControlRunKind = 'validation' | 'simulation';
207
+ /**
208
+ * One authoritative validation/simulation run EXECUTED by the trusted VICT
209
+ * boundary. Run records are the ONLY source from which ChangeSet evidence
210
+ * may be derived; caller-claimed evidence is verified against these
211
+ * durable records before it can authorize a commit.
212
+ */
213
+ export interface ControlRunRecord {
214
+ readonly runId: string;
215
+ readonly kind: ControlRunKind;
216
+ /** The ChangeSet subject the run was executed against. */
217
+ readonly changesetId: string;
218
+ /** The EXACT content identity the run was executed against. */
219
+ readonly contentHash: string;
220
+ /** The EXACT expected base identity at run time. */
221
+ readonly base: ChangeSetBase;
222
+ /** The EXACT operation set the run covered (canonical content). */
223
+ readonly operations: readonly ChangeSetOperation[];
224
+ /** The runner/profile version identity that executed the run. */
225
+ readonly runnerProfile: string;
226
+ /** The authenticated actor the run was executed for. */
227
+ readonly actorId: string;
228
+ readonly outcome: 'passed' | 'failed' | 'blocked';
229
+ /** Epoch-ms execution time from the injected clock. */
230
+ readonly createdAt: number;
231
+ /** The EXACT observed subject base at run time (what the run verified):
232
+ * the currently selected version for the subject, with the
233
+ * `CHANGESET_BASE_NONE` sentinel for explicit absence. This proves what
234
+ * base the run actually checked (never a caller claim). */
235
+ readonly observedBase?: ChangeSetBase;
236
+ /** Simulation-run detail: the exact simulation inputs, per-operation
237
+ * identities and outcomes of the sandboxed execution (IDs and outcomes
238
+ * only — never payloads). Validation runs carry `undefined`. */
239
+ readonly detail?: ControlRunDetail;
240
+ }
241
+ /** Per-operation simulation outcome (operation identity + outcome only). */
242
+ export interface ControlRunOperationOutcome {
243
+ readonly operationIndex: number;
244
+ readonly operationDigest: string;
245
+ readonly outcome: 'applied' | 'failed' | 'blocked';
246
+ /** Stable sanitized code when not applied. */
247
+ readonly code?: string;
248
+ }
249
+ /** The safe detail record of an executed simulation run. */
250
+ export interface ControlRunDetail {
251
+ readonly simulator: string;
252
+ /** The exact sandbox inputs: the subject base and activation/release
253
+ * inputs derived from durable state at simulation time (IDs only). */
254
+ readonly inputs: {
255
+ readonly base: ChangeSetBase;
256
+ readonly operationIdentities: readonly string[];
257
+ };
258
+ readonly operations: readonly ControlRunOperationOutcome[];
259
+ }
260
+ /** One durable receipt of a single applied ChangeSet operation (saga).
261
+ *
262
+ * The receipt is written in TWO states that make the external effect
263
+ * exactly-once even across a crash between effect and receipt:
264
+ *
265
+ * - `prepared`: the durable OPERATION INTENT, recorded BEFORE the effect
266
+ * runs. It carries the stable operation identity and the subject guard
267
+ * (fencing data) captured at prevalidation.
268
+ * - `applied`: recorded after the effect (atomically with it where the
269
+ * effect shares the store's transaction boundary).
270
+ *
271
+ * Recovery verifies the TARGET state for prepared intents (a selection
272
+ * with the same operation identity already applied) and never repeats the
273
+ * effect: it either confirms the target state and marks the receipt
274
+ * applied, or re-applies through the same idempotent/fenced effect path.
275
+ */
276
+ export interface ChangeSetOperationReceipt {
277
+ readonly changesetId: string;
278
+ /** Zero-based index of the operation inside the ChangeSet. */
279
+ readonly operationIndex: number;
280
+ readonly operationKind: ChangeSetOperation['kind'];
281
+ /** Stable operation identity: derived from ChangeSet ID, content hash,
282
+ * operation index, and the exact operation content. */
283
+ readonly operationDigest: string;
284
+ /** Stable identity of the applied effect (idempotency anchor). */
285
+ readonly effectRef: string;
286
+ readonly actorId: string;
287
+ readonly appliedAt: number;
288
+ /** `prepared` (intent, pre-effect) or `applied` (post-effect). */
289
+ readonly state: 'prepared' | 'applied';
290
+ /** Serialized subject guard (expected base/selection revision) captured
291
+ * at intent time; re-used verbatim by idempotent re-application. */
292
+ readonly guardJson: string | undefined;
293
+ }
294
+ /** Derive the stable operation identity for one ChangeSet operation. */
295
+ export declare function changeSetOperationIdentity(input: {
296
+ changesetId: string;
297
+ contentHash: string;
298
+ operationIndex: number;
299
+ operation: ChangeSetOperation;
300
+ }): string;
301
+ /**
302
+ * Capture a CLOSED plain-data record without ever invoking caller code or
303
+ * echoing captured values:
304
+ *
305
+ * - the value must be a non-null, non-array object whose prototype is
306
+ * exactly `Object.prototype` (or null) — class instances, exotic
307
+ * prototypes, and structs smuggled through exotic prototypes fail;
308
+ * - property enumeration, `Reflect.ownKeys`, and descriptor reads are
309
+ * guarded — a hostile proxy that throws is rejected without a raw
310
+ * exception crossing the boundary;
311
+ * - OWN string-keyed ENUMERABLE data properties only: accessors
312
+ * (getters/setters), inherited members, non-enumerable fields, and
313
+ * symbol keys are rejected, and getters are NEVER invoked (values are
314
+ * read exclusively through the captured property descriptor).
315
+ * - the field set must be EXACTLY the declared closed set.
316
+ *
317
+ * The returned capture is a freshly allocated plain object owned by VICT
318
+ * (the caller's object is never retained, frozen, or mutated).
319
+ */
320
+ export declare function captureClosedControlRecord(raw: unknown, field: string, allowed: readonly string[] | undefined): Record<string, unknown>;
321
+ /**
322
+ * Capture a CLOSED dense array without ever invoking caller code:
323
+ *
324
+ * - the value must be a REAL array (`Array.isArray` under a guard — a
325
+ * revoked Proxy throws there — whose prototype is exactly
326
+ * `Array.prototype`);
327
+ * - the `length` is read through its OWN DESCRIPTOR under a guard (never a
328
+ * `get`, which would be a caller-controlled trap): it must be an own
329
+ * DATA property carrying a safe integer within [1, maxLength];
330
+ * - `Reflect.ownKeys` (guarded) must declare EXACTLY the length and the
331
+ * dense index keys: extra string or symbol properties, sparse holes,
332
+ * non-enumerable indices, accessors, and hostile enumeration/descriptor
333
+ * traps are ALL rejected;
334
+ * - elements are captured exclusively through their guarded own property
335
+ * DESCRIPTORS (enumerable data properties only) — caller `.map()`,
336
+ * iterators, getters, and index `get` traps are never consulted.
337
+ *
338
+ * The returned array is freshly allocated and owned by VICT (the caller's
339
+ * array is never retained, frozen, or aliased).
340
+ */
341
+ export declare function captureClosedControlArray(raw: unknown, field: string, maxLength: number): readonly unknown[];
342
+ /**
343
+ * Validate the structural shape of one ChangeSet operation (fail closed).
344
+ *
345
+ * EVERY required member of the operation kind is enforced with its exact
346
+ * runtime type, non-empty bounded identifier form, and the closed own-field
347
+ * set. Accessors, inherited/non-enumerable members, symbols, exotic
348
+ * prototypes, and hostile proxies are rejected WITHOUT invoking getters or
349
+ * echoing captured values. Only the validated VICT-owned canonical capture
350
+ * is returned/hashed — never the original caller object.
351
+ */
352
+ export declare function validateChangeSetOperation(operation: unknown): ChangeSetOperation;
353
+ /**
354
+ * Validate one immutable Application Release content record (fail closed).
355
+ * The content is captured as a CLOSED plain-data structure (exact member
356
+ * set, exact string types, no accessors/symbols/hostile containers) and
357
+ * the returned record is a fresh VICT-owned capture.
358
+ */
359
+ export declare function validateApplicationReleaseContent(content: unknown): ApplicationReleaseContent;
360
+ /** Validate a full ChangeSet authoring input and derive its content hash.
361
+ *
362
+ * The COMPLETE untrusted runtime input is CAPTURED at this boundary before
363
+ * any member is read: the outer envelope must be a plain object with the
364
+ * EXACT closed field set, and `base`, the operation list, and every
365
+ * operation are captured through guarded descriptors (no caller getter,
366
+ * iterator, `.map()`, or other caller-controlled behavior is ever
367
+ * consulted; hostile/revoked proxies, sparse arrays, accessors, symbols,
368
+ * non-enumerable members, and exotic prototypes are all rejected with ONE
369
+ * stable, non-echoing error). Only VICT-owned validated captures are
370
+ * hashed and returned; caller objects are never retained, frozen, or aliased.
371
+ */
372
+ export declare function validateChangeSetContent(untrustedInput: unknown): {
373
+ changesetId: string;
374
+ base: ChangeSetBase;
375
+ operations: readonly ChangeSetOperation[];
376
+ rationale: string;
377
+ riskClass: ChangeSetRiskClass;
378
+ requiredApproverCount: number;
379
+ expiresAt: number;
380
+ contentHash: string;
381
+ };
382
+ /** The durable ChangeSet store port. */
383
+ export interface ControlPlaneStore {
384
+ saveChangeSet(record: ChangeSetRecord): Promise<void>;
385
+ getChangeSet(changesetId: string): Promise<ChangeSetRecord | undefined>;
386
+ listChangeSets(): Promise<readonly ChangeSetRecord[]>;
387
+ /** Update one ChangeSet with optimistic compare-and-set on its status. */
388
+ updateChangeSet(changesetId: string, update: (record: ChangeSetRecord) => ChangeSetRecord): Promise<ChangeSetRecord>;
389
+ /**
390
+ * Revise a ChangeSet's CONTENT (draft/approved only): the update may
391
+ * derive a NEW immutable content identity; the implementation must reset
392
+ * validation/simulation evidence atomically with the new content.
393
+ */
394
+ reviseChangeSetContent(changesetId: string, revise: (record: ChangeSetRecord) => ChangeSetRecord): Promise<ChangeSetRecord>;
395
+ recordChangeSetApproval(decision: ChangeSetApprovalDecision): Promise<void>;
396
+ listChangeSetApprovals(changesetId: string): Promise<readonly ChangeSetApprovalDecision[]>;
397
+ /** Durable record of one authoritative governance run (idempotent by runId). */
398
+ recordControlRun(record: ControlRunRecord): Promise<void>;
399
+ getControlRun(runId: string): Promise<ControlRunRecord | undefined>;
400
+ /**
401
+ * Durable saga receipts for applied ChangeSet operations. The protocol
402
+ * is: record the PREPARED intent (idempotent by (changesetId, index);
403
+ * conflicting content under the same identity fails closed) BEFORE the
404
+ * effect, then record the APPLIED state after the effect (atomically
405
+ * with it where effect and receipt share a transaction). Recovery skips
406
+ * operations whose receipt is `applied` and VERIFIES target state for
407
+ * `prepared` intents — it never manufactures a receipt merely because
408
+ * the operation was supposed to run.
409
+ */
410
+ recordOperationIntent(receipt: ChangeSetOperationReceipt): Promise<'recorded' | 'exists'>;
411
+ markOperationApplied(input: {
412
+ changesetId: string;
413
+ operationIndex: number;
414
+ at: number;
415
+ }): Promise<ChangeSetOperationReceipt>;
416
+ recordOperationReceipt(receipt: ChangeSetOperationReceipt): Promise<void>;
417
+ listOperationReceipts(changesetId: string): Promise<readonly ChangeSetOperationReceipt[]>;
418
+ /**
419
+ * Compare-and-set the ChangeSet status (one winner). The update is
420
+ * applied only when the CURRENT durable status equals `expectedStatus`;
421
+ * a mismatch throws `VICT_CONTROL_CHANGESET_STATUS_CONFLICT`.
422
+ */
423
+ compareAndSetChangeSetStatus(input: {
424
+ changesetId: string;
425
+ expectedStatus: ChangeSetStatus;
426
+ nextStatus: ChangeSetStatus;
427
+ }): Promise<ChangeSetRecord>;
428
+ publishRelease(record: ApplicationReleaseRecord): Promise<void>;
429
+ getRelease(releaseVersion: string): Promise<ApplicationReleaseRecord | undefined>;
430
+ listReleases(applicationId: string): Promise<readonly ApplicationReleaseRecord[]>;
431
+ /**
432
+ * Select one release with the operation protocol: `operationId` is the
433
+ * idempotency/fencing key (re-application returns the ORIGINAL selection
434
+ * revision without adding another one; conflicting content under the
435
+ * same identity fails closed) and `expectedBaseVersion` is the
436
+ * SUBJECT-LEVEL base guard (the currently selected release version for
437
+ * the application; `'none'`-equivalent when no version is selected).
438
+ * The guard is evaluated ATOMICALLY inside the selection mutation — two
439
+ * ChangeSets racing on one base produce exactly one winner, and the
440
+ * loser receives `VICT_CONTROL_BASE_STALE` with NO effects.
441
+ */
442
+ selectRelease(command: {
443
+ applicationId: string;
444
+ releaseVersion: string;
445
+ actorId: string;
446
+ at: number;
447
+ reason: 'select' | 'rollback';
448
+ readonly operationId?: string;
449
+ readonly expectedBaseVersion?: string;
450
+ }): Promise<{
451
+ selectionRevision: number;
452
+ }>;
453
+ getSelectedRelease(applicationId: string): Promise<ApplicationReleaseRecord | undefined>;
454
+ listReleaseSelections(applicationId: string): Promise<readonly ReleaseSelectionRecord[]>;
455
+ /**
456
+ * OPTIONAL atomic composition: publish the release, apply the guarded
457
+ * selection, and record the APPLIED operation receipt in ONE durable
458
+ * transaction (closes the effect/receipt dual-write gap where the
459
+ * effect shares the store). Implementations without a shared
460
+ * transaction boundary may omit it; the service then uses the
461
+ * intent → effect → applied protocol.
462
+ */
463
+ applyReleaseOperation?(input: {
464
+ release: ApplicationReleaseRecord;
465
+ selection: {
466
+ applicationId: string;
467
+ releaseVersion: string;
468
+ actorId: string;
469
+ at: number;
470
+ reason: 'select' | 'rollback';
471
+ operationId: string;
472
+ expectedBaseVersion?: string;
473
+ };
474
+ receipt: ChangeSetOperationReceipt;
475
+ }): Promise<{
476
+ selectionRevision: number;
477
+ }>;
478
+ appendAuditEvent(event: ControlAuditEvent): Promise<void>;
479
+ listAuditEvents(subject: {
480
+ subjectType?: string;
481
+ subjectId?: string;
482
+ }): Promise<readonly ControlAuditEvent[]>;
483
+ }
484
+ /** One published immutable Application Release. */
485
+ export interface ApplicationReleaseRecord {
486
+ readonly releaseVersion: string;
487
+ readonly applicationId: string;
488
+ readonly applicationVersion: string;
489
+ readonly rendererIdentity: string;
490
+ readonly componentRegistryIdentity: string;
491
+ readonly dataAdapterIdentity: string;
492
+ readonly activationBinding: string;
493
+ readonly publishedByActorId: string;
494
+ readonly publishedAt: number;
495
+ /** Immutable content identity of the release record. */
496
+ readonly contentHash: string;
497
+ }
498
+ /** One immutable release selection (selection is monotonic; rollback = select). */
499
+ export interface ReleaseSelectionRecord {
500
+ readonly applicationId: string;
501
+ readonly releaseVersion: string;
502
+ readonly selectionRevision: number;
503
+ readonly actorId: string;
504
+ readonly at: number;
505
+ /** `select` or `rollback` — distinct, attributable operations. */
506
+ readonly reason: 'select' | 'rollback';
507
+ /** The ChangeSet operation identity that produced this selection, when
508
+ * selection ran under the operation protocol (idempotency anchor). */
509
+ readonly operationId?: string;
510
+ }
511
+ /** One attributable audit event (safe summaries only). */
512
+ export interface ControlAuditEvent {
513
+ readonly auditId: string;
514
+ readonly at: number;
515
+ readonly actorId: string;
516
+ readonly action: ControlAuditAction;
517
+ readonly subjectType: string;
518
+ readonly subjectId: string;
519
+ /** Safe bounded summary — never payloads, never credentials. */
520
+ readonly summary: string;
521
+ }
522
+ /** The closed audit action vocabulary. */
523
+ export declare const CONTROL_AUDIT_ACTIONS: readonly ["changeset.proposed", "changeset.revised", "changeset.evidence-attached", "changeset.approved", "changeset.declined", "changeset.committed", "changeset.expired", "release.published", "release.selected", "release.rolled-back", "activation.selected", "activation.rolled-back", "actor.recorded", "run.cancelled", "turn.cancelled", "approval.decided", "approval.expired", "operator.intervened"];
524
+ export type ControlAuditAction = (typeof CONTROL_AUDIT_ACTIONS)[number];
525
+ export declare const AGENT_TURN_SCHEMA = "vict.agent-turn@1";
526
+ export type AgentTurnStatus = 'intent' | 'running' | 'awaiting-approval' | 'completed' | 'failed' | 'cancelled' | 'blocked';
527
+ /** One durable agent-turn record (VICT-authoritative). */
528
+ export interface AgentTurnRecord {
529
+ readonly turnId: string;
530
+ readonly streamId: string;
531
+ readonly threadId: string;
532
+ readonly actorId: string;
533
+ readonly agentProfileVersion: string;
534
+ readonly activationVersion: string | undefined;
535
+ /** Application Release identity pinned for the turn, when applicable. */
536
+ readonly applicationReleaseVersion: string | undefined;
537
+ /** Safe bounded input summary — NEVER the full prompt. */
538
+ readonly inputSummary: string;
539
+ readonly status: AgentTurnStatus;
540
+ readonly createdAt: number;
541
+ readonly updatedAt: number;
542
+ readonly terminalAt: number | undefined;
543
+ readonly errorCode: string | undefined;
544
+ /** Correlation identities (IDs, never payloads). */
545
+ readonly traceId: string | undefined;
546
+ readonly victRunId: string | undefined;
547
+ readonly mastraRunId: string | undefined;
548
+ }
549
+ /** Correlation update for a turn (IDs only). */
550
+ export interface AgentTurnCorrelation {
551
+ readonly traceId?: string;
552
+ readonly victRunId?: string;
553
+ readonly mastraRunId?: string;
554
+ }
555
+ /** The durable agent-turn store port. */
556
+ export interface AgentTurnStore {
557
+ createTurnIntent(record: AgentTurnRecord): Promise<void>;
558
+ getTurn(turnId: string): Promise<AgentTurnRecord | undefined>;
559
+ listTurns(): Promise<readonly AgentTurnRecord[]>;
560
+ listOpenTurns(): Promise<readonly AgentTurnRecord[]>;
561
+ startTurn(turnId: string, at: number): Promise<AgentTurnRecord>;
562
+ awaitApproval(turnId: string, at: number, approvalId: string): Promise<AgentTurnRecord>;
563
+ resumeTurn(turnId: string, at: number): Promise<AgentTurnRecord>;
564
+ recordTurnCorrelation(turnId: string, correlation: AgentTurnCorrelation): Promise<void>;
565
+ completeTurn(command: {
566
+ turnId: string;
567
+ status: 'completed' | 'failed' | 'cancelled' | 'blocked';
568
+ at: number;
569
+ errorCode?: string;
570
+ }): Promise<AgentTurnRecord>;
571
+ recordCancelIntent(command: {
572
+ turnId: string;
573
+ cancelId: string;
574
+ actorId: string;
575
+ reasonCode: string;
576
+ at: number;
577
+ }): Promise<{
578
+ accepted: boolean;
579
+ duplicate: boolean;
580
+ }>;
581
+ hasCancelIntent(turnId: string): Promise<boolean>;
582
+ /** Restart reconciliation: force one honest terminal state for an open turn. */
583
+ reconcileTurn(command: {
584
+ turnId: string;
585
+ status: 'failed' | 'cancelled' | 'blocked';
586
+ reasonCode: string;
587
+ at: number;
588
+ }): Promise<AgentTurnRecord | undefined>;
589
+ listCancelIntents(turnId: string): Promise<readonly {
590
+ cancelId: string;
591
+ actorId: string;
592
+ reasonCode: string;
593
+ at: number;
594
+ }[]>;
595
+ }
596
+ export type AgentToolInvocationStatus = 'intent' | 'approved' | 'running' | 'completed' | 'failed' | 'declined' | 'cancelled' | 'outcome_unknown';
597
+ /** One durable turn-execution tool slot (stable tool-call identity). */
598
+ export interface TurnToolSlotAllocation {
599
+ /** The 1-based monotonic slot index within the turn. */
600
+ readonly slot: number;
601
+ /** The stable tool-call identity derived from the persisted slot. */
602
+ readonly toolCallId: string;
603
+ readonly turnId: string;
604
+ readonly toolName: string;
605
+ readonly argDigest: string;
606
+ }
607
+ /** One durable protected tool-invocation record.
608
+ *
609
+ * LIVE-OWNER FENCING (Stage 06B final boundary correction): the optional
610
+ * `runFence*` members are stamped by `claimInvocationRun` when ONE owner
611
+ * claims the invocation's execution attempt, carried through the durable
612
+ * `running` state, and required as EXACT BINDING on every fenced terminal
613
+ * settlement. `runGeneration` increments on every claim and on every
614
+ * reconciliation, so a stale owner can never settle a later generation.
615
+ */
616
+ export interface AgentToolInvocationRecord {
617
+ readonly invocationId: string;
618
+ readonly turnId: string;
619
+ readonly toolCallId: string;
620
+ readonly toolName: string;
621
+ readonly capabilityId: string;
622
+ readonly capabilityRevision: string;
623
+ readonly effect: EffectClass;
624
+ /** Deterministic idempotency key for the logical invocation. */
625
+ readonly idempotencyKey: string;
626
+ readonly actorId: string;
627
+ /** Canonical argument digest — never the arguments themselves. */
628
+ readonly argDigest: string;
629
+ /** Safe bounded argument summary (never full payloads). */
630
+ readonly argumentSummary: string;
631
+ readonly status: AgentToolInvocationStatus;
632
+ readonly createdAt: number;
633
+ readonly updatedAt: number;
634
+ readonly completedAt: number | undefined;
635
+ /** Safe bounded result summary — never full payloads. */
636
+ readonly resultSummary: string | undefined;
637
+ readonly errorCode: string | undefined;
638
+ /** The fence token of the CURRENT execution attempt (claim-bound). */
639
+ readonly runFenceToken?: string | undefined;
640
+ /** When the current attempt was claimed (epoch ms). */
641
+ readonly runFenceAt?: number | undefined;
642
+ /** The process identity that claimed the current attempt. */
643
+ readonly runOwnerIdentity?: string | undefined;
644
+ /** Monotonic attempt generation (incremented per claim/reconciliation). */
645
+ readonly runGeneration?: number;
646
+ }
647
+ /** The durable tool-invocation store port. */
648
+ export interface AgentToolInvocationStore {
649
+ recordInvocationIntent(record: AgentToolInvocationRecord): Promise<AgentToolInvocationRecord>;
650
+ /**
651
+ * Allocate (or re-read) the DURABLE turn-execution tool slot for one
652
+ * logical tool request. The slot is a separately persisted allocation
653
+ * keyed by (turnId, toolName, argDigest): the FIRST allocation assigns
654
+ * the next monotonic slot for the turn and persists it BEFORE any
655
+ * invocation; every later call with the same key — including after a
656
+ * restart — returns the SAME slot and the SAME stable toolCallId.
657
+ * Logical tool-call identity therefore never derives from time, process
658
+ * counters, or the number of rows currently present.
659
+ */
660
+ allocateTurnToolSlot(input: {
661
+ turnId: string;
662
+ toolName: string;
663
+ argDigest: string;
664
+ }): Promise<TurnToolSlotAllocation>;
665
+ getInvocation(invocationId: string): Promise<AgentToolInvocationRecord | undefined>;
666
+ getInvocationByIdempotencyKey(idempotencyKey: string): Promise<AgentToolInvocationRecord | undefined>;
667
+ updateInvocationStatus(command: {
668
+ invocationId: string;
669
+ status: 'approved' | 'running' | 'completed' | 'failed' | 'declined' | 'cancelled' | 'outcome_unknown';
670
+ at: number;
671
+ resultSummary?: string;
672
+ errorCode?: string;
673
+ }): Promise<AgentToolInvocationRecord>;
674
+ listInvocationsForTurn(turnId: string): Promise<readonly AgentToolInvocationRecord[]>;
675
+ /**
676
+ * CLAIM the invocation's execution attempt for ONE live owner: the durable
677
+ * status moves `intent`|`approved` → `running` and the record is stamped
678
+ * with the attempt fence (token, owner identity, claim time) and the next
679
+ * attempt generation. Exactly one claim per generation wins: a claim on an
680
+ * already-running record throws `VICT_CONTROL_INVOCATION_OWNER_ACTIVE`
681
+ * (the caller is the duplicate of a live owner) and a claim on a terminal
682
+ * record throws the stable terminal error. Duplicates NEVER mutate a
683
+ * live-owned record through any other command.
684
+ */
685
+ claimInvocationRun(command: {
686
+ invocationId: string;
687
+ fenceToken: string;
688
+ ownerIdentity: string;
689
+ at: number;
690
+ }): Promise<AgentToolInvocationRecord>;
691
+ /**
692
+ * FENCED terminal settlement (`completed` | `failed` | `outcome_unknown`):
693
+ * accepted ONLY from the durable `running` state while the observed fence
694
+ * token equals the command's fence token — the caller must be the CURRENT
695
+ * owner of the attempt. An exact rematch of the requested terminal state
696
+ * AND its binding (status + errorCode + resultSummary) under the SAME
697
+ * fence is idempotent; every other conflict fails with a structured,
698
+ * non-echoing error (`VICT_CONTROL_INVOCATION_FENCE_MISMATCH` for a stale
699
+ * owner, `VICT_CONTROL_INVOCATION_TERMINAL`/`REGRESSION` otherwise). A
700
+ * stale owner can never settle a later claim generation.
701
+ */
702
+ settleInvocationRun(command: {
703
+ invocationId: string;
704
+ fenceToken: string;
705
+ status: 'completed' | 'failed' | 'outcome_unknown';
706
+ at: number;
707
+ resultSummary?: string;
708
+ errorCode?: string;
709
+ }): Promise<AgentToolInvocationRecord>;
710
+ /**
711
+ * PRE-RUNNING terminal settlement (`failed` | `declined` | `cancelled`):
712
+ * applies only while the durable state is pre-running (`intent` or
713
+ * `approved`). An exact rematch (same status AND same errorCode) is
714
+ * idempotent; a conflict with a different terminal state or binding fails;
715
+ * a record already claimed (`running`) is never touched
716
+ * (`VICT_CONTROL_INVOCATION_OWNER_ACTIVE`).
717
+ */
718
+ settleInvocationPending(command: {
719
+ invocationId: string;
720
+ status: 'failed' | 'declined' | 'cancelled';
721
+ at: number;
722
+ errorCode?: string;
723
+ }): Promise<AgentToolInvocationRecord>;
724
+ /**
725
+ * Conservative reconciliation of an ABANDONED `running` attempt (its
726
+ * owner is provably lost — no live owner exists for the recorded claim):
727
+ * the record is fenced to the terminal, NON-REPLAYABLE `outcome_unknown`
728
+ * without executing anything. Accepted ONLY when the observed durable
729
+ * state is exactly `running` AND the observed fence token equals
730
+ * `observedFenceToken` (idempotent only on that exact binding); the fence
731
+ * advances to `reconciledFenceToken` and the generation increments, so a
732
+ * stale owner's later settlement fails. Every other observed state fails
733
+ * with a structured, non-echoing error.
734
+ */
735
+ reconcileAbandonedRun(command: {
736
+ invocationId: string;
737
+ observedFenceToken: string;
738
+ reconciledFenceToken: string;
739
+ at: number;
740
+ }): Promise<AgentToolInvocationRecord>;
741
+ }
742
+ export type AgentApprovalStatus = 'pending' | 'approved' | 'declined' | 'expired';
743
+ /**
744
+ * One VICT-authoritative approval record (MSTR-005). The record binds the
745
+ * decision to the EXACT requesting actor, activation/agent profile,
746
+ * capability revision, turn/tool-call/invocation identity, canonical
747
+ * argument digest, effect class, and environment — nothing else can
748
+ * consume it.
749
+ */
750
+ export interface AgentApprovalRecord {
751
+ readonly approvalId: string;
752
+ readonly kind: 'tool-invocation';
753
+ readonly turnId: string;
754
+ readonly invocationId: string;
755
+ readonly toolCallId: string;
756
+ readonly toolName: string;
757
+ readonly capabilityId: string;
758
+ readonly capabilityRevision: string;
759
+ readonly effect: EffectClass;
760
+ readonly actorId: string;
761
+ readonly agentProfileVersion: string;
762
+ readonly argDigest: string;
763
+ readonly environment: string;
764
+ readonly requiredApproverRole: ActorRole;
765
+ readonly status: AgentApprovalStatus;
766
+ readonly createdAt: number;
767
+ readonly expiresAt: number;
768
+ readonly decidedAt: number | undefined;
769
+ readonly approverActorId: string | undefined;
770
+ /** Safe bounded reason/metadata — never payloads. */
771
+ readonly decisionReason: string | undefined;
772
+ }
773
+ /** The durable approval store port (VICT-authoritative decisions). */
774
+ export interface AgentApprovalStore {
775
+ createPendingApproval(record: AgentApprovalRecord): Promise<AgentApprovalRecord>;
776
+ getApproval(approvalId: string): Promise<AgentApprovalRecord | undefined>;
777
+ listOpenApprovals(): Promise<readonly AgentApprovalRecord[]>;
778
+ listApprovalsForInvocation(invocationId: string): Promise<readonly AgentApprovalRecord[]>;
779
+ decideApproval(command: {
780
+ approvalId: string;
781
+ approverActorId: string;
782
+ decision: 'approved' | 'declined';
783
+ decidedAt: number;
784
+ decisionReason?: string;
785
+ }): Promise<AgentApprovalRecord>;
786
+ expireApproval(command: {
787
+ approvalId: string;
788
+ at: number;
789
+ }): Promise<AgentApprovalRecord>;
790
+ }
791
+ /**
792
+ * One durably-ordered agent-stream event row. Only DURABLE kinds are
793
+ * persisted rows; `text.delta` consumes a sequence number but is retained
794
+ * only in the bounded transient buffer (never in operational history by
795
+ * default).
796
+ */
797
+ export interface AgentStreamLedgerEvent {
798
+ readonly streamId: string;
799
+ readonly seq: number;
800
+ readonly kind: AgentStreamEventKind;
801
+ /** Canonical JSON of the kind-specific safe payload fields. */
802
+ readonly payload: string;
803
+ readonly createdAt: number;
804
+ }
805
+ /** The durable stream-ledger store port. */
806
+ export interface AgentStreamLedgerStore {
807
+ /**
808
+ * Append one event to a stream. Assigns the next strictly monotonic
809
+ * sequence; persists a row only for durable kinds (transient kinds
810
+ * advance the sequence without a row). Returns the assigned sequence.
811
+ */
812
+ appendEvent(command: {
813
+ streamId: string;
814
+ kind: AgentStreamEventKind;
815
+ payload: string;
816
+ at: number;
817
+ }): Promise<{
818
+ seq: number;
819
+ persisted: boolean;
820
+ }>;
821
+ latestSeq(streamId: string): Promise<number>;
822
+ /**
823
+ * Durable events after the given sequence, in sequence order. The
824
+ * optional `limit` bounds one read (paged replay); implementations MUST
825
+ * accept calls without it.
826
+ */
827
+ listEventsFrom(streamId: string, afterSeq: number, limit?: number): Promise<readonly AgentStreamLedgerEvent[]>;
828
+ listStreamIds(): Promise<readonly string[]>;
829
+ }
830
+ /** Durable kinds that a ledger row persists (transient kinds never persist). */
831
+ export declare function isDurableStreamKind(kind: AgentStreamEventKind): boolean;
832
+ /** The closed, bounded idempotency-key format (HTTP + CLI surface). */
833
+ export declare const COMMAND_IDEMPOTENCY_KEY_PATTERN: RegExp;
834
+ /**
835
+ * One durable command-idempotency receipt. Receipts are NAMESPACED by
836
+ * (authenticated actor, command, idempotency key): two actors using the
837
+ * same client-generated key never interfere, and the canonical request
838
+ * digest is bound INSIDE that namespace.
839
+ *
840
+ * A `pending` receipt carries a durable LEASE (owner + expiry): the
841
+ * claiming process renews/holds the lease while executing. A crash leaves
842
+ * the lease to expire, after which a retrying caller may take the claim
843
+ * over (attempt counter incremented) instead of the key being stuck as
844
+ * in-progress forever. A `failed` receipt is a DETERMINISTIC command
845
+ * failure (stable code, replayed); retryable infrastructure failures are
846
+ * RELEASED instead so a retry re-executes truthfully.
847
+ */
848
+ export interface CommandIdempotencyReceipt {
849
+ readonly idempotencyKey: string;
850
+ readonly actorId: string;
851
+ readonly command: string;
852
+ /** Canonical digest over the exact request payload. */
853
+ readonly requestDigest: string;
854
+ readonly status: 'pending' | 'completed' | 'failed';
855
+ /** Stable failure code when status is `failed`. */
856
+ readonly responseCode: string | undefined;
857
+ /** SAFE per-command replay projection (identifiers/codes only — never
858
+ * full command responses). */
859
+ readonly resultJson: string | undefined;
860
+ readonly createdAt: number;
861
+ readonly settledAt: number | undefined;
862
+ /** The lease owner (process/instance token) while `pending`. */
863
+ readonly owner: string | undefined;
864
+ /** Epoch-ms lease expiry while `pending` (crash-recovery bound). */
865
+ readonly leaseUntil: number | undefined;
866
+ /** How many times the claim has been (re-)taken (lease takeovers). */
867
+ readonly attempts: number;
868
+ /**
869
+ * The immutable settlement FENCE token for the current claim generation.
870
+ * Completion, deterministic failure, and release must present exactly
871
+ * this token; a stale owner presenting a different token receives a
872
+ * stable non-echoing conflict and the receipt is left byte-identical.
873
+ * The token changes on every lease takeover (it is derived from the
874
+ * namespace, owner, and attempt generation).
875
+ */
876
+ readonly fenceToken: string | undefined;
877
+ }
878
+ /** Stable structured conflict code thrown when a settlement fence mismatches. */
879
+ export declare const VICT_IDEMPOTENCY_FENCE_CONFLICT = "VICT_IDEMPOTENCY_FENCE_CONFLICT";
880
+ /**
881
+ * Derive the deterministic settlement fence token for one claim
882
+ * generation. The token binds the namespace, the lease owner, and the
883
+ * attempt generation: a lease takeover always produces a NEW token, so a
884
+ * stale owner can never settle a claim it no longer owns.
885
+ */
886
+ export declare function commandIdempotencyFenceToken(input: {
887
+ actorId: string;
888
+ command: string;
889
+ idempotencyKey: string;
890
+ owner: string;
891
+ attempts: number;
892
+ }): string;
893
+ /** The result of a lease takeover attempt (the NEW fence token on success). */
894
+ export type CommandIdempotencyLeaseTakeover = {
895
+ outcome: 'taken';
896
+ fenceToken: string;
897
+ } | {
898
+ outcome: 'not-expired' | 'missing';
899
+ };
900
+ /** The namespaced lookup key of one receipt. */
901
+ export interface CommandIdempotencyName {
902
+ readonly actorId: string;
903
+ readonly command: string;
904
+ readonly idempotencyKey: string;
905
+ }
906
+ /** The durable command-idempotency store port (one winner per namespace). */
907
+ export interface CommandIdempotencyStore {
908
+ /**
909
+ * Insert-if-absent within the (actor, command, key) namespace. Returns
910
+ * `claimed` for exactly one concurrent caller (the winner); every other
911
+ * caller receives `exists` with the durable receipt.
912
+ */
913
+ claimReceipt(record: CommandIdempotencyReceipt): Promise<'claimed' | 'exists'>;
914
+ getReceipt(name: CommandIdempotencyName): Promise<CommandIdempotencyReceipt | undefined>;
915
+ /**
916
+ * Cross-command key reuse check: the receipt bound to (actor, key) under
917
+ * ANY command kind, if one exists. The command-service policy layer uses
918
+ * this to turn a client reusing one Idempotency-Key across DIFFERENT
919
+ * commands into a stable conflict without relying on per-command
920
+ * namespaces alone.
921
+ */
922
+ findReceiptByActorKey(input: {
923
+ actorId: string;
924
+ idempotencyKey: string;
925
+ }): Promise<CommandIdempotencyReceipt | undefined>;
926
+ completeReceipt(input: {
927
+ actorId: string;
928
+ command: string;
929
+ idempotencyKey: string;
930
+ resultJson: string;
931
+ at: number;
932
+ /** The claim's settlement fence token (exact generation match). */
933
+ fenceToken: string;
934
+ }): Promise<void>;
935
+ failReceipt(input: {
936
+ actorId: string;
937
+ command: string;
938
+ idempotencyKey: string;
939
+ responseCode: string;
940
+ at: number;
941
+ /** The claim's settlement fence token (exact generation match). */
942
+ fenceToken: string;
943
+ }): Promise<void>;
944
+ /**
945
+ * Release a `pending` claim WITHOUT a terminal disposition — used for
946
+ * RETRYABLE infrastructure failures so they are never permanently
947
+ * confused with deterministic command failures. The key becomes
948
+ * claimable again. The release is FENCED: only the current claim owner
949
+ * generation may release; a stale owner receives a stable conflict and
950
+ * the live claim is left byte-identical.
951
+ */
952
+ releaseReceipt(input: {
953
+ actorId: string;
954
+ command: string;
955
+ idempotencyKey: string;
956
+ at: number;
957
+ /** The claim's settlement fence token (exact generation match). */
958
+ fenceToken: string;
959
+ }): Promise<void>;
960
+ /**
961
+ * Crash recovery: take over an EXPIRED pending lease. Returns `taken`
962
+ * with the NEW settlement fence token (the caller is the new owner;
963
+ * attempts incremented), `not-expired` (the previous owner may still be
964
+ * executing), or `missing`.
965
+ */
966
+ takeOverExpiredLease(input: {
967
+ actorId: string;
968
+ command: string;
969
+ idempotencyKey: string;
970
+ owner: string;
971
+ leaseUntil: number;
972
+ at: number;
973
+ }): Promise<CommandIdempotencyLeaseTakeover>;
974
+ }
975
+ /** The composed control-plane store set. */
976
+ export interface AgentControlStores {
977
+ readonly actors: ActorDirectory;
978
+ readonly control: ControlPlaneStore;
979
+ readonly turns: AgentTurnStore;
980
+ readonly invocations: AgentToolInvocationStore;
981
+ readonly approvals: AgentApprovalStore;
982
+ readonly streamLedger: AgentStreamLedgerStore;
983
+ /** Durable command idempotency (state-changing HTTP/CLI commands). */
984
+ readonly commandIdempotency: CommandIdempotencyStore;
985
+ }
986
+ /** Validation error for any control-plane structural violation. */
987
+ export declare class VictControlError extends Error {
988
+ readonly code: string;
989
+ constructor(code: string, message: string);
990
+ }
991
+ /** Canonical JSON of one stream event's safe payload fields (context minus stream identity). */
992
+ export declare function streamEventPayloadOf(event: object): string;
993
+ /**
994
+ * Validate ONE append against the final `vict.agent-stream@1` schema at
995
+ * the STORE boundary — before any sequence state is incremented or any
996
+ * storage mutated. The RAW ledger ports are the last line of defense:
997
+ * plain-JavaScript callers cannot bypass the schema through them.
998
+ *
999
+ * Enforced here:
1000
+ * - the payload is a JSON object in CANONICAL form (non-canonical JSON is
1001
+ * rejected, not silently re-serialized);
1002
+ * - the declared kind is inside the closed vocabulary and matches the
1003
+ * payload's `kind` member;
1004
+ * - the reconstructed event (payload + stream identity + the sequence that
1005
+ * WOULD be assigned) passes the complete field-level schema (unknown
1006
+ * kinds/fields, malformed correlation IDs, unsafe codes, raw text in
1007
+ * `content.completed`, invalid usage — all fail closed).
1008
+ *
1009
+ * Errors are stable and non-echoing: `VICT_STREAM_EVENT_INVALID` carries
1010
+ * schema issue CODES only, never the rejected values.
1011
+ */
1012
+ export declare function validateStreamLedgerAppend(command: {
1013
+ streamId: string;
1014
+ kind: AgentStreamEventKind;
1015
+ payload: string;
1016
+ assignedSeq: number;
1017
+ }): AgentStreamEvent;