@affiant/core 0.1.0-alpha.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 (96) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +354 -0
  3. package/dist/context.d.ts +136 -0
  4. package/dist/context.d.ts.map +1 -0
  5. package/dist/context.js +30 -0
  6. package/dist/context.js.map +1 -0
  7. package/dist/docket/entry.d.ts +421 -0
  8. package/dist/docket/entry.d.ts.map +1 -0
  9. package/dist/docket/entry.js +155 -0
  10. package/dist/docket/entry.js.map +1 -0
  11. package/dist/docket/expiry.d.ts +82 -0
  12. package/dist/docket/expiry.d.ts.map +1 -0
  13. package/dist/docket/expiry.js +106 -0
  14. package/dist/docket/expiry.js.map +1 -0
  15. package/dist/docket/memory.d.ts +163 -0
  16. package/dist/docket/memory.d.ts.map +1 -0
  17. package/dist/docket/memory.js +528 -0
  18. package/dist/docket/memory.js.map +1 -0
  19. package/dist/docket/store.d.ts +387 -0
  20. package/dist/docket/store.d.ts.map +1 -0
  21. package/dist/docket/store.js +51 -0
  22. package/dist/docket/store.js.map +1 -0
  23. package/dist/errors.d.ts +153 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +164 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/gate/coverage.d.ts +152 -0
  28. package/dist/gate/coverage.d.ts.map +1 -0
  29. package/dist/gate/coverage.js +114 -0
  30. package/dist/gate/coverage.js.map +1 -0
  31. package/dist/gate/decide.d.ts +207 -0
  32. package/dist/gate/decide.d.ts.map +1 -0
  33. package/dist/gate/decide.js +559 -0
  34. package/dist/gate/decide.js.map +1 -0
  35. package/dist/gate/gate.d.ts +212 -0
  36. package/dist/gate/gate.d.ts.map +1 -0
  37. package/dist/gate/gate.js +175 -0
  38. package/dist/gate/gate.js.map +1 -0
  39. package/dist/gate/pipeline.d.ts +285 -0
  40. package/dist/gate/pipeline.d.ts.map +1 -0
  41. package/dist/gate/pipeline.js +515 -0
  42. package/dist/gate/pipeline.js.map +1 -0
  43. package/dist/gate/policy.d.ts +272 -0
  44. package/dist/gate/policy.d.ts.map +1 -0
  45. package/dist/gate/policy.js +396 -0
  46. package/dist/gate/policy.js.map +1 -0
  47. package/dist/gate/wrap.d.ts +107 -0
  48. package/dist/gate/wrap.d.ts.map +1 -0
  49. package/dist/gate/wrap.js +164 -0
  50. package/dist/gate/wrap.js.map +1 -0
  51. package/dist/index.d.ts +95 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +111 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/model/affidavit.d.ts +354 -0
  56. package/dist/model/affidavit.d.ts.map +1 -0
  57. package/dist/model/affidavit.js +417 -0
  58. package/dist/model/affidavit.js.map +1 -0
  59. package/dist/model/amendments.d.ts +160 -0
  60. package/dist/model/amendments.d.ts.map +1 -0
  61. package/dist/model/amendments.js +183 -0
  62. package/dist/model/amendments.js.map +1 -0
  63. package/dist/model/canonical.d.ts +311 -0
  64. package/dist/model/canonical.d.ts.map +1 -0
  65. package/dist/model/canonical.js +665 -0
  66. package/dist/model/canonical.js.map +1 -0
  67. package/dist/model/money.d.ts +127 -0
  68. package/dist/model/money.d.ts.map +1 -0
  69. package/dist/model/money.js +177 -0
  70. package/dist/model/money.js.map +1 -0
  71. package/dist/model/provenance.d.ts +315 -0
  72. package/dist/model/provenance.d.ts.map +1 -0
  73. package/dist/model/provenance.js +223 -0
  74. package/dist/model/provenance.js.map +1 -0
  75. package/dist/ports.d.ts +269 -0
  76. package/dist/ports.d.ts.map +1 -0
  77. package/dist/ports.js +34 -0
  78. package/dist/ports.js.map +1 -0
  79. package/dist/store-memory.d.ts +21 -0
  80. package/dist/store-memory.d.ts.map +1 -0
  81. package/dist/store-memory.js +20 -0
  82. package/dist/store-memory.js.map +1 -0
  83. package/dist/telemetry-keys.d.ts +65 -0
  84. package/dist/telemetry-keys.d.ts.map +1 -0
  85. package/dist/telemetry-keys.js +72 -0
  86. package/dist/telemetry-keys.js.map +1 -0
  87. package/dist/telemetry.d.ts +77 -0
  88. package/dist/telemetry.d.ts.map +1 -0
  89. package/dist/telemetry.js +43 -0
  90. package/dist/telemetry.js.map +1 -0
  91. package/dist/testing.d.ts +574 -0
  92. package/dist/testing.d.ts.map +1 -0
  93. package/dist/testing.js +1291 -0
  94. package/dist/testing.js.map +1 -0
  95. package/package.json +75 -0
  96. package/telemetry-keys.json +92 -0
@@ -0,0 +1,212 @@
1
+ /**
2
+ * `createGate` — the object a host builds once, and the wire-up checks it refuses at.
3
+ *
4
+ * **Rules served: CV-1** (a misconfiguration the framework can detect fails at
5
+ * wire-up with a stated error; no option turns the gate off), **GT-2** (every entry
6
+ * point takes the turn context as a parameter), **GT-5** (a policy that can declare a
7
+ * risk threshold needs a host-supplied scorer, and its absence is caught here rather
8
+ * than on the unlucky request that first reaches the threshold branch), **GT-4**
9
+ * (`defaultTtlMs` is required and so is every policy's own default: `expiresAt` is
10
+ * not nullable, so every filed entry carries a deadline and there is no wiring in
11
+ * which one is missing), **CV-4** (`declareUncovered`), **DK-3** (`expireDue` is
12
+ * host-scheduled — this package owns no timer).
13
+ *
14
+ * ## What is deliberately not on this object
15
+ *
16
+ * There is no `execute`, and no executor port in {@link GateOptions} for one to be
17
+ * built from. AZ-7 says no package in an implementation writes to a host's store, so
18
+ * the gate has nothing to call: the only path to `execution: "executed"` is
19
+ * {@link Gate.markExecuted}, the host's own executor reporting what it did.
20
+ *
21
+ * ## Two ways in
22
+ *
23
+ * {@link Gate.wrap} is Sequence A: a chat turn, a tool the model calls, the pipeline
24
+ * from the top. {@link Gate.file} is Sequence C: a capture that arrived over a trusted
25
+ * relay's MCP surface with its provenance already settled, entering at the projection
26
+ * step. They are the same pipeline; the second skips the two steps that have nothing
27
+ * to do.
28
+ *
29
+ * @packageDocumentation
30
+ */
31
+ import type { TurnContext } from "../context.js";
32
+ import type { DocketEntry } from "../docket/entry.js";
33
+ import type { DocketStore, Page, PageResult, Scope, SessionStore } from "../docket/store.js";
34
+ import type { JsonValue } from "../model/affidavit.js";
35
+ import type { Clock, FieldInterceptor, FieldSchema, InferencePort, Operation, ProjectionPort, RiskScorer, TelemetryPort } from "../ports.js";
36
+ import type { AuthorizationPort } from "../ports.js";
37
+ import type { CoverageRegistry, ToolDefinition, UncoveredCategory } from "./coverage.js";
38
+ import type { Decision, ExecutionReport } from "./decide.js";
39
+ import type { FiledEntry, PreparedField } from "./pipeline.js";
40
+ import type { ApprovalPolicy } from "./policy.js";
41
+ import type { GatedTool } from "./wrap.js";
42
+ /**
43
+ * Everything a host supplies. The four ports and the deadline are required; the rest
44
+ * have defaults that are the safe reading of "the host said nothing".
45
+ *
46
+ * `policies` defaults to none, which files everything `ReviewerConfirmation` — a
47
+ * person confirms every write. `interceptors` defaults to none. `telemetry` defaults
48
+ * to a port that drops events. `clock` defaults to the system clock, which is the one
49
+ * dependency a fixture always replaces.
50
+ */
51
+ export interface GateOptions {
52
+ /** The Docket (DK-1). */
53
+ readonly store: DocketStore;
54
+ /**
55
+ * The rehydration surface, if the host has one (DK-5). Not used by the pipeline;
56
+ * {@link Gate.rehydrate} refuses when it is absent.
57
+ */
58
+ readonly sessions?: SessionStore;
59
+ /** The host's structured inference (GT-1 step 3). */
60
+ readonly inference: InferencePort;
61
+ /** The host's previous-value lookup (AF-3). */
62
+ readonly projection: ProjectionPort;
63
+ /** Who may decide an entry (AZ-2). Consulted on every decision, execution report and resubmission. */
64
+ readonly authorization: AuthorizationPort;
65
+ /** The approval chain, in order (AZ-4). */
66
+ readonly policies?: readonly ApprovalPolicy[];
67
+ /** The host's risk function (GT-5). Required if any policy declares a threshold. */
68
+ readonly riskScorer?: RiskScorer;
69
+ /** Deterministic resolvers (PV-2). */
70
+ readonly interceptors?: readonly FieldInterceptor[];
71
+ /** Where every instant on the record comes from. */
72
+ readonly clock?: Clock;
73
+ /** Where the TL-1 events go. */
74
+ readonly telemetry?: TelemetryPort;
75
+ /**
76
+ * The deadline applied when neither the verdict nor the policy names one (GT-4).
77
+ * **Required**: `expiresAt` is not nullable, so every filed entry carries a
78
+ * deadline and there is no wiring in which one is missing.
79
+ */
80
+ readonly defaultTtlMs: number;
81
+ }
82
+ /**
83
+ * A capture the host has already assembled — Sequence C's way in.
84
+ *
85
+ * Supply `fields` when the provenance is settled (a relay's capture, a form
86
+ * submission, a replay): the pipeline skips the interceptors and the inference and
87
+ * starts at projection. Supply `schema` instead to run the whole pipeline against the
88
+ * turn without a {@link ToolDefinition}. Supplying neither is a wire-up error — there
89
+ * would be nothing to swear to and no way to find out.
90
+ */
91
+ export interface WriteProposal {
92
+ /** The write being proposed. */
93
+ readonly operation: Operation;
94
+ /** The tool or capture source's name — the key a coverage declaration matches (CV-4). */
95
+ readonly toolName: string;
96
+ /** Host-tagged fields. Present ⇒ interceptors and inference are skipped. */
97
+ readonly fields?: readonly PreparedField[];
98
+ /** The field schema, for a proposal that should run inference. */
99
+ readonly schema?: FieldSchema;
100
+ /**
101
+ * The arguments this capture came from, part of the entry id's derivation.
102
+ *
103
+ * A {@link JsonValue}, because that is what tool arguments are: the id derivation
104
+ * canonicalises them, and a value with no canonical form is a caller bug the type
105
+ * says so about rather than a `TypeError` at the first filing.
106
+ */
107
+ readonly args?: JsonValue;
108
+ /** The host's own verb for the operation, carried onto the card. */
109
+ readonly operationLabel?: string;
110
+ }
111
+ /** The gate a host builds once and calls from every seam (CV-2). */
112
+ export interface Gate {
113
+ /**
114
+ * Put the gate in front of `tool` for the turn `ctx` describes (GT-2, GT-6, CV-4).
115
+ *
116
+ * @throws AffiantError `"coverage-refused"` or `"wireup-invalid"` at wire-up.
117
+ */
118
+ wrap<TArgs, TResult>(tool: ToolDefinition<TArgs, TResult>, ctx: TurnContext): GatedTool<TArgs, TResult>;
119
+ /**
120
+ * File a host-assembled proposal — Sequence C's entry point.
121
+ *
122
+ * @throws AffiantError `"substance-refused"` when the capture swears to nothing
123
+ * (GT-3), or `"wireup-invalid"` when the proposal carries neither prepared
124
+ * fields nor a schema.
125
+ */
126
+ file(proposal: WriteProposal, ctx: TurnContext): Promise<FiledEntry>;
127
+ /**
128
+ * Record that the gate cannot intercept `tool`, so every later proposal from it is
129
+ * filed `blocked` rather than refused at wire-up (CV-4). It never allows the tool.
130
+ */
131
+ declareUncovered(tool: Pick<ToolDefinition, "name">, category: UncoveredCategory): void;
132
+ /**
133
+ * Approve, amend or reject an entry, as `ctx.principal` (DK-1, AZ-1, AZ-2, AZ-3).
134
+ *
135
+ * @throws AffiantError `"decision-unauthorized"`, `"entry-not-found"`,
136
+ * `"decision-expired"`, `"decision-not-pending"` or `"decision-lost-race"`.
137
+ */
138
+ decide(entryId: string, decision: Decision, ctx: TurnContext): Promise<DocketEntry>;
139
+ /**
140
+ * Record what the host's executor did with an approved write (DK-1, AZ-5, AZ-7).
141
+ *
142
+ * The only public path to `execution: "executed"`. The gate never calls an executor
143
+ * to get there — a host reads its approved-and-unexecuted rows, writes, and reports.
144
+ *
145
+ * @throws AffiantError `"decision-unauthorized"`, `"entry-not-found"`, or
146
+ * `"decision-not-pending"` when the row is not approved.
147
+ */
148
+ markExecuted(entryId: string, outcome: ExecutionReport, detail: string | null, ctx: TurnContext): Promise<DocketEntry>;
149
+ /**
150
+ * File an expired entry again as a new entry whose lineage names it (DK-1).
151
+ *
152
+ * @throws AffiantError `"decision-unauthorized"`, `"entry-not-found"`, or
153
+ * `"decision-not-pending"` when the entry does not read `expired`.
154
+ */
155
+ resubmit(entryId: string, ctx: TurnContext): Promise<FiledEntry>;
156
+ /**
157
+ * The entry as it reads now, within `ctx`'s tenant, or `null` (DK-1, expiry as
158
+ * state).
159
+ *
160
+ * **Tenant scope, not conversation scope, and that widening is deliberate.** A
161
+ * reviewer surface is not the conversation that proposed the write: a person opens
162
+ * a queue, or follows a link, and reads an entry filed in some other conversation
163
+ * of the same tenant. AZ-2's boundary is the tenant, and GT-2's "two conversations
164
+ * never observe each other" is about the turn's *own* state — its fields, its
165
+ * pending inference, its proposals — not about a Docket a tenant's reviewers
166
+ * share. Narrowing this to `{ tenantId, conversationId }` would break every
167
+ * reviewer surface; `gate-scope.test.ts` pins the widening so it cannot be
168
+ * "tightened" with a green suite.
169
+ */
170
+ get(entryId: string, ctx: TurnContext): Promise<DocketEntry | null>;
171
+ /**
172
+ * One page of what a reconnecting client needs: everything that reads `pending`,
173
+ * then everything `approved` and `unexecuted`, each in filing order (DK-5).
174
+ *
175
+ * @throws AffiantError `"wireup-invalid"` when the host supplied no
176
+ * {@link GateOptions.sessions}. It is the one wiring check that cannot
177
+ * happen at `createGate`: a host with no reconnecting client needs no
178
+ * session store, and refusing to build a gate without one would be the
179
+ * framework insisting on a surface the host does not have.
180
+ */
181
+ rehydrate(scope: Scope, page: Page): Promise<PageResult<DocketEntry>>;
182
+ /**
183
+ * Expire the pending entries in `scope` that are past their deadline, at most
184
+ * `limit` of them (DK-3). The host schedules this; the core owns no timer.
185
+ */
186
+ expireDue(now: string, scope: Scope, limit: number): Promise<{
187
+ readonly expired: readonly string[];
188
+ readonly more: boolean;
189
+ }>;
190
+ /** The uncovered declarations this gate holds (CV-4). */
191
+ readonly coverage: CoverageRegistry;
192
+ }
193
+ /**
194
+ * Build the gate, refusing a wiring it can tell is wrong (CV-1).
195
+ *
196
+ * Every refusal names the missing piece, because a wire-up error a host has to bisect
197
+ * is a wire-up error a host works around. The checks:
198
+ *
199
+ * - the four ports — `store`, `inference`, `projection`, `authorization` — are present;
200
+ * - `defaultTtlMs` is a positive, finite integer of milliseconds (GT-4);
201
+ * - every policy carrying its own {@link ApprovalPolicy.defaultTtlMs} states it the
202
+ * same way (GT-4), so a policy that would file an entry nobody can decide is
203
+ * refused here rather than on the request that first falls through to it;
204
+ * - every policy that says {@link ApprovalPolicy.declaresThreshold} has a
205
+ * `riskScorer` to compare against (GT-5). This is checked from the **static**
206
+ * declaration, before any evaluation, because a check that only fires when a policy
207
+ * happens to return a threshold is exactly the "silent non-fire" GT-5 forbids.
208
+ *
209
+ * @throws AffiantError `"wireup-invalid"`, naming what is missing.
210
+ */
211
+ export declare function createGate(options: GateOptions): Gate;
212
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * `createGate` — the object a host builds once, and the wire-up checks it refuses at.
3
+ *
4
+ * **Rules served: CV-1** (a misconfiguration the framework can detect fails at
5
+ * wire-up with a stated error; no option turns the gate off), **GT-2** (every entry
6
+ * point takes the turn context as a parameter), **GT-5** (a policy that can declare a
7
+ * risk threshold needs a host-supplied scorer, and its absence is caught here rather
8
+ * than on the unlucky request that first reaches the threshold branch), **GT-4**
9
+ * (`defaultTtlMs` is required and so is every policy's own default: `expiresAt` is
10
+ * not nullable, so every filed entry carries a deadline and there is no wiring in
11
+ * which one is missing), **CV-4** (`declareUncovered`), **DK-3** (`expireDue` is
12
+ * host-scheduled — this package owns no timer).
13
+ *
14
+ * ## What is deliberately not on this object
15
+ *
16
+ * There is no `execute`, and no executor port in {@link GateOptions} for one to be
17
+ * built from. AZ-7 says no package in an implementation writes to a host's store, so
18
+ * the gate has nothing to call: the only path to `execution: "executed"` is
19
+ * {@link Gate.markExecuted}, the host's own executor reporting what it did.
20
+ *
21
+ * ## Two ways in
22
+ *
23
+ * {@link Gate.wrap} is Sequence A: a chat turn, a tool the model calls, the pipeline
24
+ * from the top. {@link Gate.file} is Sequence C: a capture that arrived over a trusted
25
+ * relay's MCP surface with its provenance already settled, entering at the projection
26
+ * step. They are the same pipeline; the second skips the two steps that have nothing
27
+ * to do.
28
+ *
29
+ * @packageDocumentation
30
+ */
31
+ import { AffiantError } from "../errors.js";
32
+ import { defaultClock } from "../ports.js";
33
+ import { noopTelemetry } from "../telemetry.js";
34
+ import { createCoverageRegistry, declareUncovered } from "./coverage.js";
35
+ import { decide, markExecuted, resubmit } from "./decide.js";
36
+ import { runPipeline } from "./pipeline.js";
37
+ import { isUsableTtlMs, unusableTtlMessage } from "./policy.js";
38
+ import { wrapTool } from "./wrap.js";
39
+ // ---------------------------------------------------------------------------
40
+ // Wire-up
41
+ // ---------------------------------------------------------------------------
42
+ /**
43
+ * Build the gate, refusing a wiring it can tell is wrong (CV-1).
44
+ *
45
+ * Every refusal names the missing piece, because a wire-up error a host has to bisect
46
+ * is a wire-up error a host works around. The checks:
47
+ *
48
+ * - the four ports — `store`, `inference`, `projection`, `authorization` — are present;
49
+ * - `defaultTtlMs` is a positive, finite integer of milliseconds (GT-4);
50
+ * - every policy carrying its own {@link ApprovalPolicy.defaultTtlMs} states it the
51
+ * same way (GT-4), so a policy that would file an entry nobody can decide is
52
+ * refused here rather than on the request that first falls through to it;
53
+ * - every policy that says {@link ApprovalPolicy.declaresThreshold} has a
54
+ * `riskScorer` to compare against (GT-5). This is checked from the **static**
55
+ * declaration, before any evaluation, because a check that only fires when a policy
56
+ * happens to return a threshold is exactly the "silent non-fire" GT-5 forbids.
57
+ *
58
+ * @throws AffiantError `"wireup-invalid"`, naming what is missing.
59
+ */
60
+ export function createGate(options) {
61
+ requirePort(options.store, "store", "the Docket every entry is filed in (DK-1)");
62
+ requirePort(options.inference, "inference", "the host's structured inference (GT-1 step 3)");
63
+ requirePort(options.projection, "projection", "the host's previous-value lookup (AF-3)");
64
+ requirePort(options.authorization, "authorization", "who may decide an entry (AZ-2)");
65
+ const defaultTtlMs = options.defaultTtlMs;
66
+ if (!Number.isInteger(defaultTtlMs) || defaultTtlMs <= 0) {
67
+ throw new AffiantError("wireup-invalid", `CV-1: GateOptions.defaultTtlMs must be a positive whole number of milliseconds; ` +
68
+ `received ${JSON.stringify(defaultTtlMs)}. Every filed entry carries a deadline (GT-4), ` +
69
+ `so there is no wiring in which this is absent.`, { option: "defaultTtlMs" });
70
+ }
71
+ const policies = options.policies ?? [];
72
+ for (const policy of policies) {
73
+ // The same rule as `GateOptions.defaultTtlMs`, applied to the other place the
74
+ // same value comes from. A verdict's own `ttlMs` cannot be seen from here — it
75
+ // exists only once a policy has spoken — so `evaluatePolicies` holds that one to
76
+ // this definition at the moment it reads it.
77
+ if (policy.defaultTtlMs !== undefined && !isUsableTtlMs(policy.defaultTtlMs)) {
78
+ throw new AffiantError("wireup-invalid", unusableTtlMessage(policy.id, "defaultTtlMs", policy.defaultTtlMs), { option: "defaultTtlMs", policyId: policy.id, ttlMs: String(policy.defaultTtlMs) });
79
+ }
80
+ }
81
+ if (options.riskScorer === undefined) {
82
+ const needsScorer = policies.filter((policy) => policy.declaresThreshold === true);
83
+ if (needsScorer.length > 0) {
84
+ throw new AffiantError("wireup-invalid", `CV-1: policy ${needsScorer.map((policy) => JSON.stringify(policy.id)).join(", ")} ` +
85
+ `declares a risk threshold and no GateOptions.riskScorer was supplied. This package ` +
86
+ `ships no scoring formula and no floor (GT-5): the host owns the risk function, and ` +
87
+ `a declared threshold with nothing to compare against is a configuration error, never ` +
88
+ `a silent non-fire.`, { option: "riskScorer", policyIds: needsScorer.map((policy) => policy.id) });
89
+ }
90
+ }
91
+ const coverage = createCoverageRegistry();
92
+ const clock = options.clock ?? defaultClock;
93
+ const telemetry = options.telemetry ?? noopTelemetry;
94
+ const store = options.store;
95
+ const deps = {
96
+ store,
97
+ inference: options.inference,
98
+ projection: options.projection,
99
+ policies,
100
+ interceptors: options.interceptors ?? [],
101
+ riskScorer: options.riskScorer,
102
+ clock,
103
+ telemetry,
104
+ defaultTtlMs,
105
+ coverage,
106
+ };
107
+ const sessions = options.sessions ?? null;
108
+ const decideDeps = { ...deps, authorization: options.authorization, sessions };
109
+ return {
110
+ coverage,
111
+ wrap(tool, ctx) {
112
+ return wrapTool(tool, ctx, deps);
113
+ },
114
+ async file(proposal, ctx) {
115
+ const fields = proposal.fields ?? null;
116
+ const schema = proposal.schema ?? null;
117
+ if (fields === null && schema === null) {
118
+ throw new AffiantError("wireup-invalid", `CV-1: a proposal for ${JSON.stringify(proposal.toolName)} carries neither prepared ` +
119
+ `fields nor a field schema. Supply \`fields\` when the capture's provenance is ` +
120
+ `already settled (Sequence C), or \`schema\` to run the inference step against the ` +
121
+ `turn. With neither there is nothing to swear to.`, { toolName: proposal.toolName });
122
+ }
123
+ return runPipeline({
124
+ operation: proposal.operation,
125
+ toolName: proposal.toolName,
126
+ schema,
127
+ args: proposal.args ?? null,
128
+ preparedFields: fields,
129
+ operationLabel: proposal.operationLabel ?? null,
130
+ supersedes: null,
131
+ priorAmendments: null,
132
+ }, ctx, deps);
133
+ },
134
+ declareUncovered(tool, category) {
135
+ declareUncovered(coverage, tool, category);
136
+ },
137
+ async decide(entryId, decision, ctx) {
138
+ return decide(entryId, decision, ctx, decideDeps);
139
+ },
140
+ async markExecuted(entryId, outcome, detail, ctx) {
141
+ return markExecuted(entryId, outcome, detail, ctx, decideDeps);
142
+ },
143
+ async resubmit(entryId, ctx) {
144
+ return resubmit(entryId, ctx, decideDeps);
145
+ },
146
+ async rehydrate(scope, page) {
147
+ if (sessions === null) {
148
+ throw new AffiantError("wireup-invalid", `DK-5: GateOptions.sessions is required to rehydrate — it is the surface that ` +
149
+ `returns pending entries and then approved, unexecuted ones, in that order. ` +
150
+ `Supply a SessionStore, or do not call this.`, { option: "sessions" });
151
+ }
152
+ return sessions.rehydrate(scope, page);
153
+ },
154
+ async get(entryId, ctx) {
155
+ // Tenant scope, not conversation scope: a reviewer surface reads an entry filed
156
+ // in another conversation of the same tenant, and AZ-2's boundary is the tenant.
157
+ return store.get(entryId, { tenantId: ctx.tenantId });
158
+ },
159
+ async expireDue(now, scope, limit) {
160
+ const result = await store.expireDue(now, scope, limit);
161
+ for (const entryId of result.expired) {
162
+ telemetry.emit({ key: "docket.expired", at: now, attributes: { "entry.id": entryId } });
163
+ }
164
+ return result;
165
+ },
166
+ };
167
+ }
168
+ /** Refuse a missing port by name, so a host is told what to supply rather than what broke. */
169
+ function requirePort(value, option, what) {
170
+ if (value === null || value === undefined) {
171
+ throw new AffiantError("wireup-invalid", `CV-1: GateOptions.${option} is required — ${what}. A misconfiguration the gate can ` +
172
+ `detect fails here, at wire-up, and there is no option that turns the gate off.`, { option });
173
+ }
174
+ }
175
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"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"}