@arnilo/prism 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -0
- package/README.md +12 -11
- package/dist/agent-approval.d.ts +15 -2
- package/dist/agent-approval.js +5 -1
- package/dist/agent-event-source.d.ts +9 -1
- package/dist/agent-event-source.js +10 -3
- package/dist/agent-loops.js +7 -4
- package/dist/agent-run-lifecycle.d.ts +15 -1
- package/dist/agent-run-lifecycle.js +91 -10
- package/dist/agent-run-state.d.ts +34 -2
- package/dist/agent-run-state.js +68 -6
- package/dist/agent-session/helpers.js +20 -1
- package/dist/agent-session/session/assemble.js +250 -27
- package/dist/agent-session/session/persist.d.ts +27 -0
- package/dist/agent-session/session/persist.js +94 -12
- package/dist/agent-session/session/provider-round.d.ts +14 -4
- package/dist/agent-session/session/provider-round.js +197 -25
- package/dist/agent-session/session/tool-round.js +24 -2
- package/dist/agent-session/session/types.d.ts +36 -2
- package/dist/agent-session/session.d.ts +40 -4
- package/dist/agent-session/session.js +78 -5
- package/dist/attention-compiler.d.ts +51 -2
- package/dist/attention-compiler.js +282 -21
- package/dist/cache-helpers.d.ts +4 -2
- package/dist/cache-helpers.js +8 -6
- package/dist/checkpoint-restore.d.ts +45 -0
- package/dist/checkpoint-restore.js +54 -0
- package/dist/checkpoints.js +7 -11
- package/dist/context-budget.d.ts +2 -1
- package/dist/context-budget.js +24 -2
- package/dist/contracts-core/agent.d.ts +30 -0
- package/dist/contracts-core/attention.d.ts +95 -0
- package/dist/contracts-core/content.d.ts +15 -0
- package/dist/contracts-core/guardrail-packs.d.ts +41 -0
- package/dist/contracts-core/guardrail-packs.js +2 -0
- package/dist/contracts-core/loop.d.ts +42 -0
- package/dist/contracts-core/provider.d.ts +25 -0
- package/dist/contracts-core/run-limits.d.ts +21 -0
- package/dist/contracts-core/session.d.ts +23 -5
- package/dist/contracts-core/session.js +21 -2
- package/dist/contracts-core/usage.d.ts +40 -0
- package/dist/contracts-core/usage.js +8 -0
- package/dist/contracts-core.d.ts +2 -0
- package/dist/contracts-core.js +2 -0
- package/dist/contracts-protocol.d.ts +90 -4
- package/dist/contracts-run-state.d.ts +82 -6
- package/dist/evidence-grounding.d.ts +29 -0
- package/dist/evidence-grounding.js +162 -0
- package/dist/guardrail-packs/coding-standard.d.ts +3 -0
- package/dist/guardrail-packs/coding-standard.js +63 -0
- package/dist/guardrail-packs/destructive-commands.d.ts +3 -0
- package/dist/guardrail-packs/destructive-commands.js +46 -0
- package/dist/guardrail-packs/errors.d.ts +7 -0
- package/dist/guardrail-packs/errors.js +9 -0
- package/dist/guardrail-packs/index.d.ts +4 -0
- package/dist/guardrail-packs/index.js +15 -0
- package/dist/guardrail-packs/secrets-hygiene.d.ts +3 -0
- package/dist/guardrail-packs/secrets-hygiene.js +23 -0
- package/dist/guardrail-packs/types.d.ts +16 -0
- package/dist/guardrail-packs/types.js +2 -0
- package/dist/guardrail-packs/validation-respect.d.ts +3 -0
- package/dist/guardrail-packs/validation-respect.js +53 -0
- package/dist/guardrails.d.ts +20 -1
- package/dist/guardrails.js +268 -0
- package/dist/host-composition.d.ts +13 -0
- package/dist/host-composition.js +33 -2
- package/dist/index.d.ts +19 -10
- package/dist/index.js +11 -6
- package/dist/input.d.ts +8 -1
- package/dist/input.js +68 -6
- package/dist/middleware.d.ts +37 -2
- package/dist/middleware.js +41 -0
- package/dist/node/session-store-jsonl.js +18 -3
- package/dist/observability.js +6 -0
- package/dist/provider-events.d.ts +11 -3
- package/dist/provider-events.js +62 -4
- package/dist/providers/openai-compatible.js +6 -3
- package/dist/providers/transport.d.ts +3 -1
- package/dist/providers/transport.js +36 -0
- package/dist/redaction.js +18 -2
- package/dist/run-bundle.d.ts +89 -0
- package/dist/run-bundle.js +150 -0
- package/dist/run-limits.d.ts +11 -1
- package/dist/run-limits.js +46 -0
- package/dist/session-stores.d.ts +12 -1
- package/dist/session-stores.js +21 -4
- package/dist/testing/agent-event-source-conformance.js +41 -2
- package/dist/testing/prefix-stability-conformance.d.ts +30 -0
- package/dist/testing/prefix-stability-conformance.js +104 -0
- package/dist/testing/session-store-conformance.d.ts +3 -2
- package/dist/testing/session-store-conformance.js +48 -0
- package/dist/testing/state-concurrency-conformance.js +5 -12
- package/dist/tools.d.ts +5 -0
- package/dist/tools.js +11 -3
- package/dist/usage-estimation.d.ts +29 -0
- package/dist/usage-estimation.js +79 -0
- package/docs/ag-ui.md +5 -0
- package/docs/agent-events.md +68 -1
- package/docs/agent-loops.md +33 -0
- package/docs/agent-session-runtime.md +5 -3
- package/docs/attention-compiler.md +89 -8
- package/docs/coding-agent-tools.md +1 -1
- package/docs/coding-security.md +1 -0
- package/docs/coding-tools.md +0 -1
- package/docs/compaction-and-retry.md +1 -1
- package/docs/compaction-observational-memory.md +34 -7
- package/docs/connected-apps.md +116 -0
- package/docs/context-and-skills.md +13 -0
- package/docs/core.md +1 -1
- package/docs/diagrams.md +6 -6
- package/docs/document-reader.md +9 -9
- package/docs/documents.md +32 -11
- package/docs/durable-runs.md +129 -0
- package/docs/embeddings.md +5 -0
- package/docs/enterprise-postgres-state.md +4 -0
- package/docs/evaluations.md +5 -0
- package/docs/execution-timeline.md +84 -1
- package/docs/guardrails.md +71 -2
- package/docs/history/079-messaging-primitive-review.md +391 -0
- package/docs/history/080-messaging-followon-primitive-review.md +234 -0
- package/docs/history/081-connected-apps-primitive-review.md +74 -0
- package/docs/history/083-prism-work-primitive-review.md +84 -0
- package/docs/history/084-primitive-review.md +96 -0
- package/docs/history/085-honesty-and-cut-primitive-review.md +91 -0
- package/docs/history/README.md +5 -0
- package/docs/history/release-handoffs.md +38 -0
- package/docs/host-compositions.md +8 -6
- package/docs/host-security.md +2 -2
- package/docs/index.md +66 -29
- package/docs/input-and-prompt-assembly.md +3 -3
- package/docs/knowledge-sync.md +4 -0
- package/docs/live-testing.md +5 -3
- package/docs/mcp-tools.md +1 -0
- package/docs/messaging-channel-operations.md +166 -0
- package/docs/messaging-channels.md +150 -0
- package/docs/middleware-hooks.md +38 -2
- package/docs/migrate-to-0.8.md +124 -0
- package/docs/migrate-to-0.9.md +210 -0
- package/docs/migration.md +43 -0
- package/docs/model-registry.md +12 -2
- package/docs/multi-agent-patterns.md +25 -2
- package/docs/node-jsonl-session-store.md +7 -1
- package/docs/observability.md +7 -3
- package/docs/openapi-tools.md +1 -1
- package/docs/operations.md +1 -3
- package/docs/options-index.md +36 -3
- package/docs/peer-dependencies.md +6 -6
- package/docs/policy-and-audit.md +13 -1
- package/docs/postgres-persistence.md +1 -1
- package/docs/prefix-stability-conformance.md +93 -0
- package/docs/provider-caching.md +4 -4
- package/docs/provider-conformance.md +16 -0
- package/docs/provider-layer.md +2 -2
- package/docs/provider-packages.md +20 -20
- package/docs/providers/neuralwatt.md +5 -1
- package/docs/public-contracts.md +2 -2
- package/docs/rag.md +102 -4
- package/docs/release-and-install.md +55 -47
- package/docs/run-bundle.md +92 -0
- package/docs/runs-and-usage.md +57 -6
- package/docs/scoped-agent-memory.md +262 -0
- package/docs/server.md +2 -0
- package/docs/session-store-conformance.md +1 -2
- package/docs/session-stores.md +17 -17
- package/docs/sheets.md +9 -9
- package/docs/signal-channel.md +112 -0
- package/docs/speech.md +5 -1
- package/docs/sqlite-persistence.md +1 -1
- package/docs/supervisors.md +32 -12
- package/docs/telegram-channel.md +157 -0
- package/docs/testing.md +2 -2
- package/docs/tools.md +17 -0
- package/docs/wiki.md +1 -1
- package/docs/work-artifacts-and-review.md +1 -1
- package/docs/work-connectors.md +9 -9
- package/docs/work-sandbox.md +115 -0
- package/docs/work-tools.md +38 -16
- package/docs/workflows.md +5 -0
- package/package.json +9 -3
- package/templates/business-worker/manifest.json +2 -1
- package/templates/business-worker/src/agent.ts.tmpl +1 -1
- package/templates/business-worker/src/tests/agent.test.ts.tmpl +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { AgentLoopOptions, AgentLoopStrategy, ArtifactValidation, CompactionOptions, ContentBlock, ErrorInfo, GuardrailRecord, Guardrails, InstructionInjector, JsonObject, Message, ModelConfig, OwnershipScope, ProviderRequestOptions, ProviderRequestPolicy, ProviderResolver, RetryOptions, RunLimitBreach, RunLimits, Skill, SubscriberOverflowPolicy, SystemPromptConfig, ToolCallAuthority, ToolCallContent, Usage } from "./contracts-core.js";
|
|
1
|
+
import type { AgentLoopOptions, AgentLoopStrategy, ArtifactValidation, BudgetAxisUsage, BudgetConsumedCounters, CompactionOptions, ContentBlock, ErrorInfo, GuardrailRecord, Guardrails, InstructionInjector, JsonObject, Message, ModelConfig, OwnershipScope, ProviderRequestOptions, ProviderRequestPolicy, ProviderResolver, ProviderStopReason, RetryOptions, RunLimitBreach, RunLimitName, RunLimits, Skill, SubscriberOverflowPolicy, SystemPromptConfig, ToolCallAuthority, ToolCallContent, ToolCallSummary, TurnBudgets, TurnPolicyOptions, Usage } from "./contracts-core.js";
|
|
2
|
+
import type { CacheUsageReport } from "./cache-helpers.js";
|
|
3
|
+
import type { CheckpointRestoreAudit } from "./checkpoint-restore.js";
|
|
2
4
|
import type { AgentRunInterruption, AgentRunStateOptions } from "./contracts-run-state.js";
|
|
3
5
|
import type { SecretRedactor } from "./redaction.js";
|
|
4
6
|
import type { ToolValidator } from "./tools.js";
|
|
@@ -28,6 +30,7 @@ export type ProviderEvent = {
|
|
|
28
30
|
} | {
|
|
29
31
|
readonly type: "done";
|
|
30
32
|
readonly usage?: Usage;
|
|
33
|
+
readonly stopReason?: ProviderStopReason;
|
|
31
34
|
} | {
|
|
32
35
|
readonly type: "error";
|
|
33
36
|
readonly error: ErrorInfo;
|
|
@@ -108,6 +111,10 @@ export interface RunOptions {
|
|
|
108
111
|
readonly toolNames?: readonly string[];
|
|
109
112
|
/** Tools disclosure: "all" (default) sends every active tool schema; "search" sends top-k + the generated `search_tools` tool. */
|
|
110
113
|
readonly toolsDisclosure?: import("./tool-search.js").ToolsDisclosure;
|
|
114
|
+
/** Per-turn restrictive allow-list over the run grant. Overrides `AgentConfig.toolNarrowing`. */
|
|
115
|
+
readonly toolNarrowing?: import("./contracts-core/agent.js").ToolNarrowing;
|
|
116
|
+
/** Opt-in: tools hidden this turn stay callable by name (default off). Overrides agent config. */
|
|
117
|
+
readonly allowHiddenToolCalls?: true;
|
|
111
118
|
readonly toolsSearch?: import("./tool-search.js").ToolsSearchOptions;
|
|
112
119
|
/** Opt-in projection-only fold for aged large tool results in provider view; store untouched. */
|
|
113
120
|
readonly toolResultFold?: import("./tool-result-fold.js").ToolResultFoldOptions;
|
|
@@ -125,6 +132,11 @@ export interface RunOptions {
|
|
|
125
132
|
readonly runState?: AgentRunStateOptions;
|
|
126
133
|
/** Prompt provenance: copied verbatim onto this run's start and finish ledger records. */
|
|
127
134
|
readonly promptVersion?: PromptVersionRef;
|
|
135
|
+
/**
|
|
136
|
+
* Host turn policy (plan 084 Task 2): a clean turn cap and/or a synchronous stop callback
|
|
137
|
+
* evaluated before every provider request. Omitted → no callback runs.
|
|
138
|
+
*/
|
|
139
|
+
readonly turnPolicy?: TurnPolicyOptions;
|
|
128
140
|
}
|
|
129
141
|
export interface ProviderTurnMetadata {
|
|
130
142
|
readonly providerId: string;
|
|
@@ -135,6 +147,17 @@ export interface ProviderTurnMetadata {
|
|
|
135
147
|
readonly httpStatus?: number;
|
|
136
148
|
readonly rateLimitRemaining?: number;
|
|
137
149
|
readonly rateLimitResetMs?: number;
|
|
150
|
+
/** Why the provider turn stopped (plan 087 T1); present on `provider_turn_finished` only. */
|
|
151
|
+
readonly stopReason?: ProviderStopReason;
|
|
152
|
+
/** Effective budget state at turn end (plan 087 T1); present on `provider_turn_finished` only. */
|
|
153
|
+
readonly budgets?: TurnBudgets;
|
|
154
|
+
/** Provider-reported cache usage and derived hit rate; absent when cache usage is unknown. */
|
|
155
|
+
readonly cache?: CacheUsageReport;
|
|
156
|
+
/** Effective tool menu this turn. Names hashed in request order; never includes args. */
|
|
157
|
+
readonly tools?: {
|
|
158
|
+
readonly count: number;
|
|
159
|
+
readonly idsHash: string;
|
|
160
|
+
};
|
|
138
161
|
}
|
|
139
162
|
export interface ToolExecutionMetadata {
|
|
140
163
|
readonly durationMs: number;
|
|
@@ -170,8 +193,20 @@ export interface DelegatedAgentStep {
|
|
|
170
193
|
readonly label?: string;
|
|
171
194
|
};
|
|
172
195
|
}
|
|
173
|
-
|
|
174
|
-
export type
|
|
196
|
+
/** Why a run stopped cleanly. `host_policy` is a `RunOptions.turnPolicy` stop; the rest are loop ceilings (F4). */
|
|
197
|
+
export type AgentFinishReason = "turn_limit" | "token_limit" | "refusal" | "host_policy";
|
|
198
|
+
/**
|
|
199
|
+
* Origin of an agent event forwarded from a delegated child (supervisor child-event passthrough).
|
|
200
|
+
* Present only on child events routed onto a parent stream; absent on a session's own events.
|
|
201
|
+
*/
|
|
202
|
+
export interface ChildEventOrigin {
|
|
203
|
+
readonly childId: string;
|
|
204
|
+
readonly delegationId: string;
|
|
205
|
+
/** Delegation depth: 1 is a direct child of the hosting supervisor. */
|
|
206
|
+
readonly depth: number;
|
|
207
|
+
}
|
|
208
|
+
/** Payload union of every agent event; the exported `AgentEvent` adds the optional child origin tag. */
|
|
209
|
+
type AgentEventPayload = {
|
|
175
210
|
readonly type: "agent_started";
|
|
176
211
|
readonly sessionId: string;
|
|
177
212
|
readonly runId: string;
|
|
@@ -180,8 +215,10 @@ export type AgentEvent = {
|
|
|
180
215
|
readonly sessionId: string;
|
|
181
216
|
readonly runId: string;
|
|
182
217
|
readonly usage?: Usage;
|
|
183
|
-
/** Why the loop stopped, when a limit/ceiling ended the run cleanly (F4). Absent = natural end. */
|
|
218
|
+
/** Why the loop stopped, when a limit/ceiling or a host turn policy ended the run cleanly (F4). Absent = natural end. */
|
|
184
219
|
readonly finishReason?: AgentFinishReason;
|
|
220
|
+
/** Host stop detail from `TurnPolicyOptions.stop` (≤256 bytes, redacted). Present only with `finishReason: "host_policy"`. */
|
|
221
|
+
readonly stopDetail?: string;
|
|
185
222
|
} | {
|
|
186
223
|
readonly type: "agent_suspended";
|
|
187
224
|
readonly sessionId: string;
|
|
@@ -193,6 +230,8 @@ export type AgentEvent = {
|
|
|
193
230
|
readonly sessionId: string;
|
|
194
231
|
readonly runId: string;
|
|
195
232
|
readonly version: number;
|
|
233
|
+
/** Plan 094 Task 3: audit of the external-state restore hooks that ran before this claim. */
|
|
234
|
+
readonly restore?: CheckpointRestoreAudit;
|
|
196
235
|
} | {
|
|
197
236
|
readonly type: "agent_denied";
|
|
198
237
|
readonly sessionId: string;
|
|
@@ -209,6 +248,17 @@ export type AgentEvent = {
|
|
|
209
248
|
readonly sessionId: string;
|
|
210
249
|
readonly runId: string;
|
|
211
250
|
readonly turn: number;
|
|
251
|
+
} | {
|
|
252
|
+
/**
|
|
253
|
+
* Host middleware completed this turn without a provider request (plan 096). No `usage` field:
|
|
254
|
+
* a deterministic turn has no provider cost, so accounting must never zero-fill one.
|
|
255
|
+
*/
|
|
256
|
+
readonly type: "deterministic_turn";
|
|
257
|
+
readonly sessionId: string;
|
|
258
|
+
readonly runId: string;
|
|
259
|
+
readonly turn: number;
|
|
260
|
+
/** Answering middleware id (provenance); ids only, never free host code. */
|
|
261
|
+
readonly middleware: string;
|
|
212
262
|
} | {
|
|
213
263
|
readonly type: "provider_turn_started";
|
|
214
264
|
readonly sessionId: string;
|
|
@@ -273,6 +323,13 @@ export type AgentEvent = {
|
|
|
273
323
|
readonly reason: string;
|
|
274
324
|
readonly error: ErrorInfo;
|
|
275
325
|
readonly metadata: ToolExecutionMetadata;
|
|
326
|
+
} | {
|
|
327
|
+
/** Host `toolNarrowing` asked for names outside the run grant; those names were dropped. */
|
|
328
|
+
readonly type: "tool_narrowing_clamped";
|
|
329
|
+
readonly sessionId: string;
|
|
330
|
+
readonly runId: string;
|
|
331
|
+
readonly turn: number;
|
|
332
|
+
readonly dropped: readonly string[];
|
|
276
333
|
} | {
|
|
277
334
|
readonly type: "guardrail_decision";
|
|
278
335
|
readonly sessionId: string;
|
|
@@ -285,6 +342,16 @@ export type AgentEvent = {
|
|
|
285
342
|
readonly sessionId: string;
|
|
286
343
|
readonly runId: string;
|
|
287
344
|
readonly breach: RunLimitBreach;
|
|
345
|
+
} | {
|
|
346
|
+
/** Terminal attribution for a run that died on a run limit (plan 087 T2): which axis fired,
|
|
347
|
+
* counters at exhaustion, how close the other axes were, and hashes of recent tool calls. */
|
|
348
|
+
readonly type: "budget_exhausted";
|
|
349
|
+
readonly sessionId: string;
|
|
350
|
+
readonly runId: string;
|
|
351
|
+
readonly limit: RunLimitName;
|
|
352
|
+
readonly consumed: BudgetConsumedCounters;
|
|
353
|
+
readonly closestOtherAxes: readonly BudgetAxisUsage[];
|
|
354
|
+
readonly recentToolCalls: readonly ToolCallSummary[];
|
|
288
355
|
} | {
|
|
289
356
|
readonly type: "queue_updated";
|
|
290
357
|
readonly sessionId: string;
|
|
@@ -375,6 +442,14 @@ export type AgentEvent = {
|
|
|
375
442
|
readonly attempt: number;
|
|
376
443
|
readonly result: ArtifactValidation;
|
|
377
444
|
};
|
|
445
|
+
/**
|
|
446
|
+
* One agent event. `child` is set only when the event was forwarded from a delegated child
|
|
447
|
+
* (e.g. supervisor `report: "stream"` passthrough), so hosts can route it onto a parent stream
|
|
448
|
+
* without per-event-type special cases. It never replaces the event's own `sessionId`/`runId`.
|
|
449
|
+
*/
|
|
450
|
+
export type AgentEvent = AgentEventPayload & {
|
|
451
|
+
readonly child?: ChildEventOrigin;
|
|
452
|
+
};
|
|
378
453
|
export type ToolEffectKind = "none" | "local_mutation" | "external_mutation";
|
|
379
454
|
export type ToolEffectIdempotency = "none" | "optional" | "required" | "tool_managed" | "unsupported";
|
|
380
455
|
/** Static or validated-argument classification of one tool call's side-effect behavior. */
|
|
@@ -538,6 +613,10 @@ export interface RunRecord extends OwnershipScope {
|
|
|
538
613
|
readonly startedAt: string;
|
|
539
614
|
readonly finishedAt?: string;
|
|
540
615
|
readonly abortReason?: string;
|
|
616
|
+
/** Present when the loop stopped on a ceiling or host policy instead of a natural end. */
|
|
617
|
+
readonly stopReason?: AgentFinishReason;
|
|
618
|
+
/** Host stop detail from `TurnPolicyOptions.stop` (≤256 bytes, redacted). */
|
|
619
|
+
readonly stopDetail?: string;
|
|
541
620
|
readonly error?: ErrorInfo;
|
|
542
621
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
543
622
|
/** Provenance ref copied from `RunOptions.promptVersion` when the host supplied one. */
|
|
@@ -676,4 +755,11 @@ export interface ProviderTurnResult {
|
|
|
676
755
|
readonly messageId?: string;
|
|
677
756
|
readonly started: boolean;
|
|
678
757
|
readonly usage?: Usage;
|
|
758
|
+
/**
|
|
759
|
+
* Provenance for turns that did not come from the provider (plan 096):
|
|
760
|
+
* `{ deterministic: { middleware } }`. Copied onto the assistant `Message.metadata`, so it
|
|
761
|
+
* serializes with the transcript and survives replay. Absent for provider turns.
|
|
762
|
+
*/
|
|
763
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
679
764
|
}
|
|
765
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { AgentSessionCloneOptions, AgentSessionForkOptions, CheckpointStore, CompactionOptions, CompactionResult, ContentBlock, ErrorInfo, JsonObject, JsonValue, Message, ModelConfig, OwnershipScope, RunLimitBreach, SessionEntry, SubscribeOptions, ToolCallContent, Usage } from "./contracts-core.js";
|
|
2
|
-
import type { AgentEvent, RunOptions, ToolEffectKind } from "./contracts-protocol.js";
|
|
1
|
+
import type { AgentSessionCloneOptions, AgentSessionForkOptions, CheckpointRecord, CheckpointStore, CompactionOptions, CompactionResult, ContentBlock, ContextMeter, ErrorInfo, JsonObject, JsonValue, Message, ModelConfig, OwnershipScope, RunLimitBreach, SessionEntry, SubscribeOptions, ToolCallContent, Usage } from "./contracts-core.js";
|
|
2
|
+
import type { AgentEvent, AgentFinishReason, RunOptions, ToolEffectKind } from "./contracts-protocol.js";
|
|
3
|
+
import type { CheckpointRestoreHook } from "./checkpoint-restore.js";
|
|
3
4
|
export type AgentRunStatus = "succeeded" | "failed" | "aborted" | "suspended" | "denied";
|
|
4
5
|
export type AgentRunInterruptionKind = "input_guardrail" | "tool_approval" | "elicitation";
|
|
5
6
|
export type ApprovalOutcome = "allow_once" | "allow_for_run" | "reject_once" | "reject_for_run";
|
|
@@ -139,12 +140,38 @@ export declare const HARD_MAX_ACTION_CONSTRAINTS = 64;
|
|
|
139
140
|
export declare const MAX_ATTRIBUTION_DEPTH = 8;
|
|
140
141
|
export declare const MAX_ACTION_CONSTRAINT_BYTES: number;
|
|
141
142
|
export declare const HARD_MAX_ACTION_CONSTRAINT_BYTES: number;
|
|
143
|
+
/**
|
|
144
|
+
* Opaque host sidecar pinned to one checkpoint *record* (git commit, document version,
|
|
145
|
+
* workspace fingerprint) — never part of the run-state value, so it costs no `maxStateBytes`
|
|
146
|
+
* budget and is invisible to state parsing. Bounded to `MAX_AGENT_RUN_METADATA_BYTES` (4 KiB)
|
|
147
|
+
* and redacted like the state value at every write.
|
|
148
|
+
*/
|
|
149
|
+
export type AgentRunCheckpointMetadata = Readonly<Record<string, string>>;
|
|
150
|
+
/** Host source for checkpoint sidecar metadata: a fixed map or a live provider resolved per write. */
|
|
151
|
+
export type AgentRunCheckpointMetadataSource = AgentRunCheckpointMetadata | (() => AgentRunCheckpointMetadata | undefined);
|
|
142
152
|
export interface AgentRunStateOptions {
|
|
143
153
|
readonly checkpoints: CheckpointStore;
|
|
144
154
|
/** Host-authored immutable revision required for durable runs. */
|
|
145
155
|
readonly definitionRevision: string;
|
|
156
|
+
/**
|
|
157
|
+
* Durable checkpoint cadence (plan 084 Task 1). `"decision"` (default) persists only on
|
|
158
|
+
* suspension and terminal status. `"every-turn"` additionally persists a running-state
|
|
159
|
+
* checkpoint at each provider-turn boundary — after the previous turn's tool results are in
|
|
160
|
+
* the session store, before the next provider request — so a host process that dies mid-run
|
|
161
|
+
* can `resumeAgentRun(..., { decision: "continue" })` from the last turn instead of re-running
|
|
162
|
+
* the investigation. Costs one bounded, redacted checkpoint write per provider turn; the
|
|
163
|
+
* policy is recorded in the checkpoint, so a later resume keeps checkpointing without the host
|
|
164
|
+
* repeating the option.
|
|
165
|
+
*/
|
|
166
|
+
readonly checkpointPolicy?: "decision" | "every-turn";
|
|
146
167
|
/** Suspend every tool call before its side effect. */
|
|
147
168
|
readonly interruptBeforeTool?: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Sidecar metadata written with every checkpoint of this run (and carried into a resumed
|
|
171
|
+
* run). A provider is resolved at each checkpoint write, so a host closure can pin state
|
|
172
|
+
* that moves mid-run (e.g. the current git commit). Absent = records stay byte-identical.
|
|
173
|
+
*/
|
|
174
|
+
readonly checkpointMetadata?: AgentRunCheckpointMetadataSource;
|
|
148
175
|
readonly maxStateBytes?: number;
|
|
149
176
|
readonly fencingToken?: number;
|
|
150
177
|
/** Enables sticky auto-apply when a nested suspension first surfaces during this run. */
|
|
@@ -183,17 +210,41 @@ export interface AgentRunState {
|
|
|
183
210
|
}
|
|
184
211
|
export interface AgentRunResume {
|
|
185
212
|
readonly expectedVersion: number;
|
|
186
|
-
/**
|
|
187
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Legacy decision path. `approve` allows all pending decisions once; `deny` terminates the run
|
|
215
|
+
* as `denied`; `continue` resumes a running-state `"every-turn"` checkpoint that has no pending
|
|
216
|
+
* decisions (crash recovery). A suspended run still requires `approve`/`deny` or a decision
|
|
217
|
+
* batch — `continue` never bypasses an approval gate and is a host-API-only action.
|
|
218
|
+
*/
|
|
219
|
+
readonly decision?: "approve" | "deny" | "continue";
|
|
188
220
|
/** Batch decision path; exactly one of decision/decisions. Applied as one atomic CAS transition. */
|
|
189
221
|
readonly decisions?: readonly RunDecision[];
|
|
190
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Checkpoint handed to a restore hook (plan 094 Task 3). `checkpoint.value` is the raw stored
|
|
225
|
+
* run-state value; `metadata` is the redacted, bounded sidecar map hosts write via
|
|
226
|
+
* `AgentRunStateOptions.checkpointMetadata`.
|
|
227
|
+
*/
|
|
228
|
+
export interface AgentCheckpointRestoreContext {
|
|
229
|
+
readonly runId: string;
|
|
230
|
+
readonly sessionId: string;
|
|
231
|
+
/** Version of the checkpoint being claimed; a hook may pass it to an external system's own CAS. */
|
|
232
|
+
readonly version: number;
|
|
233
|
+
/** State being claimed: `running` for crash recovery, `suspended` for a decision resume. */
|
|
234
|
+
readonly status: AgentRunStatus | "running";
|
|
235
|
+
readonly metadata?: AgentRunCheckpointMetadata;
|
|
236
|
+
readonly checkpoint: CheckpointRecord;
|
|
237
|
+
}
|
|
238
|
+
/** Host code restoring one external layer before a durable resume applies. */
|
|
239
|
+
export type AgentCheckpointRestoreHook = CheckpointRestoreHook<AgentCheckpointRestoreContext>;
|
|
191
240
|
export interface AgentRunResumeOptions {
|
|
192
241
|
readonly checkpoints: CheckpointStore;
|
|
193
242
|
/** Current host-authored revision; must exactly match the checkpoint. */
|
|
194
243
|
readonly definitionRevision: string;
|
|
195
244
|
readonly ownership?: OwnershipScope;
|
|
196
245
|
readonly fencingToken?: number;
|
|
246
|
+
/** Host abort for the resume: checked between steps and threaded into the resumed provider/tool turn. */
|
|
247
|
+
readonly signal?: AbortSignal;
|
|
197
248
|
/** Routes root decisions for nested-run approvals back to the child (e.g. supervisor). */
|
|
198
249
|
readonly resumeNestedRun?: ResumeNestedRun;
|
|
199
250
|
/**
|
|
@@ -206,10 +257,23 @@ export interface AgentRunResumeOptions {
|
|
|
206
257
|
readonly persistSessionState?: boolean;
|
|
207
258
|
/** Opt-in (plan 018 Task 6): restore persisted loaded-skill bodies (requires `persistSessionState` too). */
|
|
208
259
|
readonly includeSkillBodies?: boolean;
|
|
260
|
+
/**
|
|
261
|
+
* Checkpoint sidecar metadata for the claim write (and the resumed run's later checkpoints).
|
|
262
|
+
* Absent = the record's existing metadata is preserved unchanged.
|
|
263
|
+
*/
|
|
264
|
+
readonly checkpointMetadata?: AgentRunCheckpointMetadataSource;
|
|
265
|
+
/**
|
|
266
|
+
* Plan 094 Task 3: external-state restore hooks. Every hook must succeed (sequentially, each
|
|
267
|
+
* within `restoreHookTimeoutMs`) before the claim write and the conversation restore apply;
|
|
268
|
+
* the first failure throws `CheckpointRestoreError` naming the hook and leaves the checkpoint
|
|
269
|
+
* suspended. Hosts that register hooks on the lifecycle instead pass them once there.
|
|
270
|
+
*/
|
|
271
|
+
readonly restoreHooks?: readonly AgentCheckpointRestoreHook[];
|
|
272
|
+
/** Per-hook restore ceiling in ms; defaults to `DEFAULT_CHECKPOINT_RESTORE_TIMEOUT_MS`. */
|
|
273
|
+
readonly restoreHookTimeoutMs?: number;
|
|
209
274
|
}
|
|
210
|
-
/** Bounded
|
|
275
|
+
/** Bounded live-event options for `resumeAgentRunStream()`; `signal` is inherited from the base resume options. */
|
|
211
276
|
export interface AgentRunResumeStreamOptions extends AgentRunResumeOptions, SubscribeOptions {
|
|
212
|
-
readonly signal?: AbortSignal;
|
|
213
277
|
}
|
|
214
278
|
export interface AgentRunRef {
|
|
215
279
|
readonly runId: string;
|
|
@@ -218,6 +282,8 @@ export interface AgentRunRef {
|
|
|
218
282
|
export interface AgentRunStatusResult {
|
|
219
283
|
readonly state: AgentRunState;
|
|
220
284
|
readonly version: number;
|
|
285
|
+
/** Checkpoint sidecar metadata; absent when the record carries none (or carries only malformed entries). */
|
|
286
|
+
readonly metadata?: AgentRunCheckpointMetadata;
|
|
221
287
|
}
|
|
222
288
|
export declare class AgentRunStateError extends Error {
|
|
223
289
|
readonly code = "ERR_PRISM_AGENT_RUN_STATE";
|
|
@@ -251,6 +317,10 @@ export interface AgentRunResult {
|
|
|
251
317
|
readonly error?: ErrorInfo;
|
|
252
318
|
/** String form of the abort reason when `status` is `"aborted"`. */
|
|
253
319
|
readonly abortReason?: string;
|
|
320
|
+
/** Present when the loop stopped on a ceiling or host turn policy instead of a natural end. */
|
|
321
|
+
readonly stopReason?: AgentFinishReason;
|
|
322
|
+
/** Host stop detail from `TurnPolicyOptions.stop` (≤256 bytes, redacted). */
|
|
323
|
+
readonly stopDetail?: string;
|
|
254
324
|
/** Present for durable suspended/terminal runs. Payload is redacted and bounded. */
|
|
255
325
|
readonly runState?: AgentRunState;
|
|
256
326
|
/** Present only while awaiting an operator decision. */
|
|
@@ -298,6 +368,12 @@ export interface AgentSession {
|
|
|
298
368
|
abort(reason?: unknown): void;
|
|
299
369
|
entries(): Promise<readonly SessionEntry[]>;
|
|
300
370
|
checkout(leafId?: string): Promise<void>;
|
|
371
|
+
/**
|
|
372
|
+
* Context-fill read (plan 091 T2): latest provider turn's input tokens
|
|
373
|
+
* (reported or labeled estimate) plus the resolved per-request cap, run input
|
|
374
|
+
* budget, and used ratio. Before any provider turn it estimates stored history.
|
|
375
|
+
*/
|
|
376
|
+
contextMeter(): ContextMeter;
|
|
301
377
|
fork(options?: AgentSessionForkOptions): AgentSession;
|
|
302
378
|
clone(options?: AgentSessionCloneOptions): Promise<AgentSession>;
|
|
303
379
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Guardrail, ToolResult } from "./contracts.js";
|
|
2
|
+
export interface ClaimGroundingEvidence {
|
|
3
|
+
readonly value: number;
|
|
4
|
+
/** Refer to this governed figure as `[evidence:<ref>]` immediately after a claim. */
|
|
5
|
+
readonly ref?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ClaimGroundingEvidenceExtractorContext {
|
|
8
|
+
readonly sessionId: string;
|
|
9
|
+
readonly runId: string;
|
|
10
|
+
readonly metadata: Readonly<Record<string, unknown>>;
|
|
11
|
+
readonly toolResults: readonly ToolResult[];
|
|
12
|
+
}
|
|
13
|
+
/** Supplies host-governed figures without coupling this primitive to a host store or package. */
|
|
14
|
+
export type ClaimGroundingEvidenceExtractor = (context: ClaimGroundingEvidenceExtractorContext) => readonly ClaimGroundingEvidence[];
|
|
15
|
+
export interface ClaimGroundingGuardrailOptions {
|
|
16
|
+
/** False makes the returned guardrail a no-op. */
|
|
17
|
+
readonly requireEvidenceForNumbers: boolean;
|
|
18
|
+
/** Defaults to same-run tool results. A host extractor may return its own governed figures. */
|
|
19
|
+
readonly evidenceSources?: "tool_results" | ClaimGroundingEvidenceExtractor;
|
|
20
|
+
/** Defaults to block. Flag emits an allow record with `metadata.violation: true`. */
|
|
21
|
+
readonly onViolation?: "block" | "flag";
|
|
22
|
+
/** Exact numeric equality by default; rounded accepts half the final printed unit. */
|
|
23
|
+
readonly tolerance?: "exact" | "rounded";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Deterministic output guardrail for figures that must be grounded in same-run tool results
|
|
27
|
+
* or host-governed evidence. It never calls a provider, store, or extractor asynchronously.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createClaimGroundingGuardrail(options: ClaimGroundingGuardrailOptions): Guardrail<"output">;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const MAX_EVIDENCE_FIGURES = 4096;
|
|
2
|
+
const MAX_EVIDENCE_DEPTH = 16;
|
|
3
|
+
const MAX_EVIDENCE_NODES = 16 * 1024;
|
|
4
|
+
const MAX_EVIDENCE_TEXT_CHARS = 128 * 1024;
|
|
5
|
+
const MAX_CLAIM_CHARS = 128;
|
|
6
|
+
const MAX_CITATION_DISTANCE = 96;
|
|
7
|
+
const EVIDENCE_CITATION = /\[evidence:([a-zA-Z0-9._:-]{1,128})\]/g;
|
|
8
|
+
const NUMERIC_CLAIM = /(?<![\p{L}\p{N}_])~?[$€£¥]?[-+]?(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?[kKmMbB]?%?(?![\p{L}\p{N}_])/gu;
|
|
9
|
+
/**
|
|
10
|
+
* Deterministic output guardrail for figures that must be grounded in same-run tool results
|
|
11
|
+
* or host-governed evidence. It never calls a provider, store, or extractor asynchronously.
|
|
12
|
+
*/
|
|
13
|
+
export function createClaimGroundingGuardrail(options) {
|
|
14
|
+
const resolved = resolveOptions(options);
|
|
15
|
+
return {
|
|
16
|
+
name: "claim-grounding",
|
|
17
|
+
stage: "output",
|
|
18
|
+
revision: "1",
|
|
19
|
+
evaluate(context) {
|
|
20
|
+
if (!resolved.requireEvidenceForNumbers)
|
|
21
|
+
return { action: "allow" };
|
|
22
|
+
const figures = evidenceFigures(resolved.evidenceSources, context);
|
|
23
|
+
const refs = new Set(figures.flatMap((figure) => (figure.ref === undefined ? [] : [figure.ref])));
|
|
24
|
+
for (const claim of outputClaims(context.value.content)) {
|
|
25
|
+
const block = context.value.content[claim.contentIndex];
|
|
26
|
+
if (matchesEvidence(claim, figures, resolved.tolerance) || (block?.type === "text" && citesEvidence(block.text, claim.end, refs)))
|
|
27
|
+
continue;
|
|
28
|
+
const metadata = {
|
|
29
|
+
violation: true,
|
|
30
|
+
claim: claim.text.slice(0, MAX_CLAIM_CHARS),
|
|
31
|
+
contentIndex: claim.contentIndex,
|
|
32
|
+
start: claim.end - claim.text.length,
|
|
33
|
+
end: claim.end,
|
|
34
|
+
};
|
|
35
|
+
return resolved.onViolation === "block"
|
|
36
|
+
? { action: "block", reason: "claim_ungrounded", metadata }
|
|
37
|
+
: { action: "allow", reason: "claim_ungrounded", metadata };
|
|
38
|
+
}
|
|
39
|
+
return { action: "allow" };
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function resolveOptions(options) {
|
|
44
|
+
if (!options || typeof options.requireEvidenceForNumbers !== "boolean")
|
|
45
|
+
throw new TypeError("Claim grounding requireEvidenceForNumbers must be boolean");
|
|
46
|
+
const evidenceSources = options.evidenceSources ?? "tool_results";
|
|
47
|
+
if (evidenceSources !== "tool_results" && typeof evidenceSources !== "function")
|
|
48
|
+
throw new TypeError('Claim grounding evidenceSources must be "tool_results" or an extractor');
|
|
49
|
+
const onViolation = options.onViolation ?? "block";
|
|
50
|
+
if (onViolation !== "block" && onViolation !== "flag")
|
|
51
|
+
throw new TypeError('Claim grounding onViolation must be "block" or "flag"');
|
|
52
|
+
const tolerance = options.tolerance ?? "exact";
|
|
53
|
+
if (tolerance !== "exact" && tolerance !== "rounded")
|
|
54
|
+
throw new TypeError('Claim grounding tolerance must be "exact" or "rounded"');
|
|
55
|
+
return { requireEvidenceForNumbers: options.requireEvidenceForNumbers, evidenceSources, onViolation, tolerance };
|
|
56
|
+
}
|
|
57
|
+
function evidenceFigures(source, context) {
|
|
58
|
+
if (source !== "tool_results")
|
|
59
|
+
return source({
|
|
60
|
+
sessionId: context.sessionId,
|
|
61
|
+
runId: context.runId,
|
|
62
|
+
metadata: context.metadata,
|
|
63
|
+
toolResults: context.toolResults ?? [],
|
|
64
|
+
})
|
|
65
|
+
.filter(validFigure)
|
|
66
|
+
.slice(0, MAX_EVIDENCE_FIGURES);
|
|
67
|
+
const figures = [];
|
|
68
|
+
const budget = { chars: MAX_EVIDENCE_TEXT_CHARS, nodes: MAX_EVIDENCE_NODES };
|
|
69
|
+
for (const result of context.toolResults ?? []) {
|
|
70
|
+
if (result.error)
|
|
71
|
+
continue;
|
|
72
|
+
const ref = `tool:${result.toolCallId}`;
|
|
73
|
+
collectFigures(result.value, figures, ref, new WeakSet(), 0, budget);
|
|
74
|
+
for (const block of result.content ?? [])
|
|
75
|
+
if (block.type === "text")
|
|
76
|
+
collectTextFigures(block.text, figures, ref, budget);
|
|
77
|
+
}
|
|
78
|
+
return figures;
|
|
79
|
+
}
|
|
80
|
+
function validFigure(value) {
|
|
81
|
+
return typeof value?.value === "number" && Number.isFinite(value.value) && (value.ref === undefined || citationRef(value.ref));
|
|
82
|
+
}
|
|
83
|
+
function collectFigures(value, figures, ref, seen = new WeakSet(), depth = 0, budget = { chars: MAX_EVIDENCE_TEXT_CHARS, nodes: MAX_EVIDENCE_NODES }) {
|
|
84
|
+
if (figures.length >= MAX_EVIDENCE_FIGURES || depth > MAX_EVIDENCE_DEPTH || budget.nodes-- < 1)
|
|
85
|
+
return;
|
|
86
|
+
if (typeof value === "number") {
|
|
87
|
+
if (Number.isFinite(value))
|
|
88
|
+
figures.push({ value, ref });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === "string") {
|
|
92
|
+
collectTextFigures(value, figures, ref, budget);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (!value || typeof value !== "object" || seen.has(value))
|
|
96
|
+
return;
|
|
97
|
+
seen.add(value);
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
for (const item of value)
|
|
100
|
+
collectFigures(item, figures, ref, seen, depth + 1, budget);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
for (const item of Object.values(value))
|
|
104
|
+
collectFigures(item, figures, ref, seen, depth + 1, budget);
|
|
105
|
+
}
|
|
106
|
+
function collectTextFigures(text, figures, ref, budget) {
|
|
107
|
+
const bounded = text.slice(0, budget.chars);
|
|
108
|
+
budget.chars -= bounded.length;
|
|
109
|
+
for (const claim of claims(bounded, 0)) {
|
|
110
|
+
if (figures.length >= MAX_EVIDENCE_FIGURES)
|
|
111
|
+
return;
|
|
112
|
+
figures.push({ value: claim.value, ref });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function outputClaims(content) {
|
|
116
|
+
const out = [];
|
|
117
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
118
|
+
const block = content[index];
|
|
119
|
+
if (block?.type !== "text")
|
|
120
|
+
continue;
|
|
121
|
+
out.push(...claims(block.text, index));
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function claims(text, contentIndex) {
|
|
126
|
+
const out = [];
|
|
127
|
+
NUMERIC_CLAIM.lastIndex = 0;
|
|
128
|
+
for (let match = NUMERIC_CLAIM.exec(text); match; match = NUMERIC_CLAIM.exec(text)) {
|
|
129
|
+
const token = match[0];
|
|
130
|
+
const parsed = parseNumber(token);
|
|
131
|
+
if (parsed === undefined)
|
|
132
|
+
continue;
|
|
133
|
+
out.push({ text: token, value: parsed.value, tolerance: parsed.tolerance, end: match.index + token.length, contentIndex });
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
function parseNumber(token) {
|
|
138
|
+
const compact = token.replace(/^~?[$€£¥]?[-+]?/, "").replace(/%$/, "");
|
|
139
|
+
const suffix = compact.at(-1)?.toLowerCase();
|
|
140
|
+
const multiplier = suffix === "k" ? 1_000 : suffix === "m" ? 1_000_000 : suffix === "b" ? 1_000_000_000 : 1;
|
|
141
|
+
const numberText = (multiplier === 1 ? compact : compact.slice(0, -1)).replace(/,/g, "");
|
|
142
|
+
const value = Number(numberText) * multiplier;
|
|
143
|
+
if (!Number.isFinite(value))
|
|
144
|
+
return undefined;
|
|
145
|
+
const fraction = numberText.split(".")[1]?.length ?? 0;
|
|
146
|
+
return { value, tolerance: 0.5 * multiplier * 10 ** -fraction };
|
|
147
|
+
}
|
|
148
|
+
function matchesEvidence(claim, figures, tolerance) {
|
|
149
|
+
return figures.some((figure) => Math.abs(figure.value - claim.value) <= (tolerance === "rounded" ? claim.tolerance : 0));
|
|
150
|
+
}
|
|
151
|
+
function citesEvidence(text, end, refs) {
|
|
152
|
+
const nearby = text.slice(end, end + MAX_CITATION_DISTANCE);
|
|
153
|
+
EVIDENCE_CITATION.lastIndex = 0;
|
|
154
|
+
for (let match = EVIDENCE_CITATION.exec(nearby); match; match = EVIDENCE_CITATION.exec(nearby))
|
|
155
|
+
if (refs.has(match[1] ?? ""))
|
|
156
|
+
return true;
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
function citationRef(value) {
|
|
160
|
+
return /^[a-zA-Z0-9._:-]{1,128}$/.test(value);
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=evidence-grounding.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { resolve as resolvePath, sep } from "node:path";
|
|
2
|
+
import { GuardrailPackError } from "./errors.js";
|
|
3
|
+
/** File-mutating prism coding tool names (plan 092 Task 1: `shell`/`read` are not mutations). */
|
|
4
|
+
const MUTATING_TOOLS = ["write", "edit", "delete", "move"];
|
|
5
|
+
const PATH_ARGS = ["path", "paths", "from", "to"];
|
|
6
|
+
const TEST_FILE_PATTERN = /(^|[\\/])(?:__tests__|tests?|specs?)[\\/]|\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
7
|
+
const MAX_ROOTS = 16;
|
|
8
|
+
function pathStrings(args) {
|
|
9
|
+
const paths = [];
|
|
10
|
+
for (const key of PATH_ARGS) {
|
|
11
|
+
const value = args[key];
|
|
12
|
+
if (typeof value === "string")
|
|
13
|
+
paths.push(value);
|
|
14
|
+
else if (Array.isArray(value))
|
|
15
|
+
paths.push(...value.filter((item) => typeof item === "string"));
|
|
16
|
+
}
|
|
17
|
+
return paths;
|
|
18
|
+
}
|
|
19
|
+
function readRoots(value, cwd) {
|
|
20
|
+
if (value === undefined)
|
|
21
|
+
return [cwd];
|
|
22
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > MAX_ROOTS) {
|
|
23
|
+
throw new GuardrailPackError(`coding-standard options.roots must be a non-empty string array (max ${MAX_ROOTS})`);
|
|
24
|
+
}
|
|
25
|
+
return value.map((root) => {
|
|
26
|
+
if (typeof root !== "string" || !root.trim())
|
|
27
|
+
throw new GuardrailPackError("coding-standard options.roots entries must be non-empty strings");
|
|
28
|
+
return resolvePath(cwd, root);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** Lazy containment: no symlink resolution (a link inside a root can still point out); execution policy/sandbox remains the hard boundary. */
|
|
32
|
+
function outsideRoots(candidate, roots, cwd) {
|
|
33
|
+
const resolved = resolvePath(cwd, candidate);
|
|
34
|
+
return !roots.some((root) => resolved === root || resolved.startsWith(root.endsWith(sep) ? root : `${root}${sep}`));
|
|
35
|
+
}
|
|
36
|
+
/** Canned coding hygiene: file mutations confined to configured roots, test files read-only. */
|
|
37
|
+
export const codingStandardPack = {
|
|
38
|
+
id: "coding-standard",
|
|
39
|
+
version: 1,
|
|
40
|
+
description: "Restricts file mutation to configured workspace roots and blocks test-file rewrites.",
|
|
41
|
+
build(options) {
|
|
42
|
+
const cwd = resolvePath(typeof options.cwd === "string" ? options.cwd : process.cwd());
|
|
43
|
+
const roots = readRoots(options.roots, cwd);
|
|
44
|
+
return {
|
|
45
|
+
rules: [
|
|
46
|
+
{
|
|
47
|
+
id: "no-unrelated-file-edits",
|
|
48
|
+
tool: MUTATING_TOOLS,
|
|
49
|
+
reason: "File edits are restricted to the configured workspace roots",
|
|
50
|
+
deny: (args) => pathStrings(args).some((candidate) => outsideRoots(candidate, roots, cwd)),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "no-test-rewrites",
|
|
54
|
+
tool: MUTATING_TOOLS,
|
|
55
|
+
pattern: TEST_FILE_PATTERN,
|
|
56
|
+
argPath: PATH_ARGS,
|
|
57
|
+
reason: "Test files are read-only under this pack",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=coding-standard.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Shell commands that destroy data or history. Denies `--force-with-lease` too: both rewrite remote history. */
|
|
2
|
+
const DESTRUCTIVE_COMMANDS = [
|
|
3
|
+
{
|
|
4
|
+
id: "no-recursive-force-delete",
|
|
5
|
+
pattern: /\brm\s+(?:[^\n;&|]*?\s)?-(?=[a-z]*r)(?=[a-z]*f)[a-z]+/i,
|
|
6
|
+
reason: "Recursive force delete is not allowed",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "no-long-flag-force-delete",
|
|
10
|
+
pattern: /\brm\s+[^\n;&|]*--recursive\b[^\n;&|]*--force\b|\brm\s+[^\n;&|]*--force\b[^\n;&|]*--recursive\b/i,
|
|
11
|
+
reason: "Recursive force delete is not allowed",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: "no-force-push",
|
|
15
|
+
pattern: /\bgit\s+push\b[^\n;&|]*(?:--force\b|(?:^|\s)-f(?:\s|$))/i,
|
|
16
|
+
reason: "Force push is not allowed",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "no-destructive-sql",
|
|
20
|
+
pattern: /\b(?:drop|truncate)\s+table\b/i,
|
|
21
|
+
reason: "Destructive SQL is not allowed",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "no-device-overwrite",
|
|
25
|
+
pattern: /\bmkfs(?:\.\w+)?\b|\bdd\b[^\n;&|]*\bof=\/dev\//i,
|
|
26
|
+
reason: "Raw device overwrite is not allowed",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
/** Canned destructive shell/SQL patterns on the `shell` tool's `command` argument. */
|
|
30
|
+
export const destructiveCommandsPack = {
|
|
31
|
+
id: "destructive-commands",
|
|
32
|
+
version: 1,
|
|
33
|
+
description: "Blocks destructive shell and SQL commands (recursive force delete, force push, drop table, device overwrite).",
|
|
34
|
+
build() {
|
|
35
|
+
return {
|
|
36
|
+
rules: DESTRUCTIVE_COMMANDS.map(({ id, pattern, reason }) => ({
|
|
37
|
+
id,
|
|
38
|
+
tool: "shell",
|
|
39
|
+
pattern,
|
|
40
|
+
argPath: "command",
|
|
41
|
+
reason,
|
|
42
|
+
})),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=destructive-commands.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Guardrail pack config/compile error (plan 092 Task 2). Config mistakes fail closed at compile time. */
|
|
2
|
+
export declare class GuardrailPackError extends Error {
|
|
3
|
+
readonly code = "ERR_PRISM_GUARDRAIL_PACK";
|
|
4
|
+
constructor(message: string, options?: {
|
|
5
|
+
readonly cause?: unknown;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Guardrail pack config/compile error (plan 092 Task 2). Config mistakes fail closed at compile time. */
|
|
2
|
+
export class GuardrailPackError extends Error {
|
|
3
|
+
code = "ERR_PRISM_GUARDRAIL_PACK";
|
|
4
|
+
constructor(message, options) {
|
|
5
|
+
super(message, options);
|
|
6
|
+
this.name = "GuardrailPackError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=errors.js.map
|