@sema-agent/core 7.37.1 → 7.38.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 +135 -0
- package/dist/agents/observer.js +1 -1
- package/dist/agents/peer-notices.js +4 -4
- package/dist/agents/subagent.js +1 -1
- package/dist/agents/team-role.js +2 -2
- package/dist/brain/anthropic.js +10 -9
- package/dist/brain/errors.d.ts +26 -0
- package/dist/brain/errors.js +34 -0
- package/dist/brain/input-too-long.d.ts +0 -31
- package/dist/brain/input-too-long.js +1 -18
- package/dist/brain/open-responses.js +10 -9
- package/dist/brain/openai.js +10 -9
- package/dist/brain/status-sink.d.ts +1 -1
- package/dist/brain/stream-engine.js +10 -2
- package/dist/brain/stream-shared.d.ts +11 -3
- package/dist/brain/stream-shared.js +4 -2
- package/dist/brain/tool-call-id.d.ts +29 -8
- package/dist/brain/tool-call-id.js +36 -6
- package/dist/core/a2a-task-state.js +2 -2
- package/dist/core/artifact-host.js +6 -6
- package/dist/core/ask-origin.js +6 -6
- package/dist/core/ask-unresolvable-notice.js +1 -1
- package/dist/core/auto-mode.js +4 -4
- package/dist/core/background-agent-store.js +2 -2
- package/dist/core/checkpoint-execution-record.js +1 -1
- package/dist/core/checkpoint-store.d.ts +2 -2
- package/dist/core/checkpoint-store.js +2 -2
- package/dist/core/child-clamp-seats.js +3 -3
- package/dist/core/compliance.js +1 -1
- package/dist/core/context-edit.js +1 -1
- package/dist/core/context-guard.d.ts +0 -19
- package/dist/core/context-guard.js +0 -38
- package/dist/core/decision-receipt.d.ts +27 -1
- package/dist/core/decision-receipt.js +176 -1
- package/dist/core/engine-notice.d.ts +59 -8
- package/dist/core/engine-notice.js +2 -1
- package/dist/core/gate-fold.js +3 -2
- package/dist/core/gate-outcome.js +4 -4
- package/dist/core/governance-codes.d.ts +221 -13
- package/dist/core/governance-codes.js +104 -4
- package/dist/core/hook-seats.js +2 -2
- package/dist/core/hooks.d.ts +21 -2
- package/dist/core/hooks.js +4 -1
- package/dist/core/mailbox-store.js +3 -3
- package/dist/core/mcp-failure.js +4 -4
- package/dist/core/mcp-injection-drop.js +1 -1
- package/dist/core/memory-engine/consolidation.js +1 -1
- package/dist/core/memory-engine/delegation-settlement.d.ts +8 -0
- package/dist/core/memory-engine/delegation-settlement.js +7 -0
- package/dist/core/memory-engine/engine.d.ts +12 -2
- package/dist/core/memory-engine/engine.js +10 -6
- package/dist/core/memory-engine/export-bundle.js +1 -1
- package/dist/core/memory-engine/layout.d.ts +6 -6
- package/dist/core/memory-engine/layout.js +6 -8
- package/dist/core/memory-engine/session-write-home.d.ts +30 -0
- package/dist/core/memory-engine/session-write-home.js +34 -0
- package/dist/core/memory-engine/tools.js +1 -1
- package/dist/core/memory-engine/types.js +1 -1
- package/dist/core/memory.js +1 -1
- package/dist/core/mode-policy.d.ts +10 -1
- package/dist/core/mode-policy.js +15 -7
- package/dist/core/model-seat.js +1 -1
- package/dist/core/permission-rule-model.js +3 -3
- package/dist/core/permission-rule-store.js +2 -2
- package/dist/core/read-only-shell-table.js +8 -8
- package/dist/core/retention.js +2 -2
- package/dist/core/runner/assemble-result.js +15 -5
- package/dist/core/runner/classifier-ladder.js +1 -1
- package/dist/core/runner/denial-limit-arms.d.ts +2 -0
- package/dist/core/runner/denial-limit-arms.js +6 -2
- package/dist/core/runner/inherited-ask-grants.d.ts +6 -5
- package/dist/core/runner/inherited-ask-grants.js +3 -2
- package/dist/core/runner/prepare-context-lane.d.ts +19 -15
- package/dist/core/runner/prepare-context-lane.js +11 -15
- package/dist/core/runner/prepare-gate-stations.js +5 -5
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-policy-chain.js +10 -10
- package/dist/core/runner/run-harness-handlers.js +3 -0
- package/dist/core/runner/run-identity-wiring.d.ts +1 -1
- package/dist/core/runner/run-identity-wiring.js +0 -2
- package/dist/core/runner/run-leg.js +2 -2
- package/dist/core/runner/run-stop-and-final-verify.js +1 -0
- package/dist/core/runner/run-turn-boundary.js +1 -1
- package/dist/core/runner/session-mode-seats.d.ts +11 -3
- package/dist/core/runner/session-mode-seats.js +6 -0
- package/dist/core/runner/session-rule-policy.js +2 -3
- package/dist/core/runner/stream-steer-verb.js +1 -1
- package/dist/core/runner/tool-call-id-fold-seat.js +16 -9
- package/dist/core/runner/tool-turn-repair-seat.d.ts +11 -1
- package/dist/core/runner/tool-turn-repair-seat.js +35 -0
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runtime-caps.js +1 -1
- package/dist/core/safety-axis-vocab.d.ts +5 -0
- package/dist/core/safety-axis-vocab.js +5 -0
- package/dist/core/session-reconcile.d.ts +9 -7
- package/dist/core/session-reconcile.js +21 -1
- package/dist/core/shared-memory/types.js +1 -1
- package/dist/core/side-query.d.ts +6 -1
- package/dist/core/store-contracts/read-back-shape-contract.js +1 -1
- package/dist/core/swappable-deps.js +1 -1
- package/dist/core/task-notification.js +4 -4
- package/dist/core/task-registry-shared.js +1 -1
- package/dist/core/terminal-cause.d.ts +19 -0
- package/dist/core/tool-face.js +6 -6
- package/dist/core/tool-policy.d.ts +17 -11
- package/dist/core/tool-policy.js +38 -72
- package/dist/core/tool-roster.js +1 -1
- package/dist/core/tool-search-setting.js +1 -1
- package/dist/core/trace.d.ts +5 -4
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/wiring-manifest.js +2 -2
- package/dist/core/write-protect.js +1 -1
- package/dist/engine/harness/agent-harness.js +10 -6
- package/dist/engine/llm/tool-turns.d.ts +93 -4
- package/dist/engine/llm/tool-turns.js +63 -9
- package/dist/engine/llm/types.d.ts +11 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/workflow-governance.js +2 -2
- package/dist/orchestration/workflow-types.js +2 -2
- package/dist/shell/lex.js +7 -7
- package/dist/tools/fs/bash-program-position.js +2 -2
- package/dist/tools/fs/read-deny.js +2 -2
- package/dist/tools/fs/safety.js +1 -1
- package/dist/tools/fs/shell-read-judge.js +2 -2
- package/dist/tools/fs/shell-read-plan.js +2 -2
- package/dist/tools/fs/shell-read-write-targets.js +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +17 -1
- package/wire-changes/core@7.38.0.json +664 -0
|
@@ -16,9 +16,9 @@ import type { ToolEffect } from "./types.js";
|
|
|
16
16
|
* Contract enforced here:
|
|
17
17
|
* - **Never auto-replay a tool.** Reconstruction (`buildSessionContext`) already does not re-run
|
|
18
18
|
* tools; this guarantees we also never synthesize a "success" that hides an unknown outcome.
|
|
19
|
-
* - For each orphan
|
|
20
|
-
* interrupted and its outcome is unknown — making the
|
|
21
|
-
* decision to the model under its verification discipline.
|
|
19
|
+
* - For each orphan an append can close (an id's last call site), append a synthetic `toolResult`
|
|
20
|
+
* (isError) that tells the model the call was interrupted and its outcome is unknown — making the
|
|
21
|
+
* transcript API-valid AND handing the decision to the model under its verification discipline.
|
|
22
22
|
* - Use the tool's {@link ToolEffect} to tune the message: `read` carries no risk at all; `idempotent`
|
|
23
23
|
* is replayable (re-issuing converges the state, but the interrupted attempt may have partially
|
|
24
24
|
* landed — RB-221); `write` (and unknown tools, conservatively) must be verified/confirmed
|
|
@@ -38,9 +38,9 @@ export interface OrphanToolCall {
|
|
|
38
38
|
* with a synthetic interrupted result).
|
|
39
39
|
* - `"result"` = a **result-orphan**: a `toolResult` with no matching assistant `toolCall` — a
|
|
40
40
|
* protocol-invalid context every mainstream chat API rejects. The oracle used to walk only
|
|
41
|
-
* assistant→result, so it reported a context in exactly this state as clean while the
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* assistant→result, so it reported a context in exactly this state as clean while the request
|
|
42
|
+
* builder dropped the result (the request view's tool-turn law leaves it out and announces it,
|
|
43
|
+
* `orphan_result`). NOT reconcile-actionable (appending another result would not repair it); consumers
|
|
44
44
|
* deciding on repair must filter on this field.
|
|
45
45
|
* Additive: absent = call-orphan, so pre-existing consumers and persisted expectations are
|
|
46
46
|
* byte-identical for the historical direction.
|
|
@@ -153,7 +153,9 @@ export declare function interruptedResultMessage(result: InterruptedResult, time
|
|
|
153
153
|
}>;
|
|
154
154
|
/**
|
|
155
155
|
* Reconcile a resumed session's active branch: close any orphan tool calls with a synthetic
|
|
156
|
-
* interrupted `toolResult` (never re-running the tool)
|
|
156
|
+
* interrupted `toolResult` (never re-running the tool) — the ones an append can close
|
|
157
|
+
* ({@link closableByAppend}), so a second reconcile of the same branch appends nothing. Returns what
|
|
158
|
+
* was recovered.
|
|
157
159
|
*
|
|
158
160
|
* @param toolEffects optional name→effect map (from the task's tools). Unknown names are treated as
|
|
159
161
|
* `write` — the conservative default, so an unrecognized interrupted call is never said to be safe.
|
|
@@ -124,9 +124,29 @@ export function interruptedResultMessage(result, timestamp) {
|
|
|
124
124
|
timestamp,
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
|
+
function closableByAppend(messages, suspendedBatch) {
|
|
128
|
+
const lastSite = new Map();
|
|
129
|
+
const lastResult = new Map();
|
|
130
|
+
let order = 0;
|
|
131
|
+
messages.forEach((m, i) => {
|
|
132
|
+
if (m.role === "assistant") {
|
|
133
|
+
for (const part of m.content) {
|
|
134
|
+
if (part.type === "toolCall" && !suspendedBatch?.has(part.id) && lastSite.get(part.id)?.at !== i)
|
|
135
|
+
lastSite.set(part.id, { at: i, order: order++, toolName: part.name });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (m.role === "toolResult") {
|
|
139
|
+
lastResult.set(m.toolCallId, i);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return [...lastSite]
|
|
143
|
+
.filter(([id, site]) => (lastResult.get(id) ?? -1) < site.at)
|
|
144
|
+
.sort((a, b) => a[1].order - b[1].order)
|
|
145
|
+
.map(([toolCallId, site]) => ({ toolCallId, toolName: site.toolName }));
|
|
146
|
+
}
|
|
127
147
|
export async function reconcileInterruptedSession(session, toolEffects, suspendedBatch, startedToolCallIds) {
|
|
128
148
|
const { messages } = await session.buildContext();
|
|
129
|
-
const orphans =
|
|
149
|
+
const orphans = closableByAppend(messages, suspendedBatch);
|
|
130
150
|
const recovered = [];
|
|
131
151
|
for (const result of interruptedResults(orphans, toolEffects, startedToolCallIds)) {
|
|
132
152
|
const entryId = await session.appendMessage(interruptedResultMessage(result, Date.now()));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const SHARED_MEMORY_LIST_TOOL_NAME = "memory_list";
|
|
2
2
|
export const SHARED_MEMORY_READ_TOOL_NAME = "memory_read";
|
|
3
|
-
export const SHARED_MEMORY_TOOL_NAMES = [SHARED_MEMORY_LIST_TOOL_NAME, SHARED_MEMORY_READ_TOOL_NAME];
|
|
3
|
+
export const SHARED_MEMORY_TOOL_NAMES = Object.freeze([SHARED_MEMORY_LIST_TOOL_NAME, SHARED_MEMORY_READ_TOOL_NAME]);
|
|
4
4
|
export const SHARED_MEMORY_READ_CAP_BYTES = 102400;
|
|
5
5
|
export const SHARED_MEMORY_LIST_PAGE_SIZE = 50;
|
|
6
6
|
export const SHARED_MEMORY_MAX_STORES = 64;
|
|
@@ -47,7 +47,12 @@ export interface SideQuerySpec {
|
|
|
47
47
|
modelRole?: ModelRole;
|
|
48
48
|
thinking?: ThinkingLevel;
|
|
49
49
|
systemPrompt?: string;
|
|
50
|
-
/** Full multi-turn message list (user/assistant/toolResult), passed to the brain UNFLATTENED
|
|
50
|
+
/** Full multi-turn message list (user/assistant/toolResult), passed to the brain UNFLATTENED — through the same
|
|
51
|
+
* tool-turn law the harness applies to a session transcript (`ensureToolTurnInvariants`): an assistant's tool calls
|
|
52
|
+
* become its trailing blocks, each call is followed by exactly one result, and a call this list leaves unanswered
|
|
53
|
+
* (a trailing tool call, with the query's own prompt appended after it) is closed with a synthesized
|
|
54
|
+
* "interrupted, outcome unknown" result and an operator notice. A caller that does not want that close answers or
|
|
55
|
+
* drops the open call before handing the list over. The caller's array is not modified. */
|
|
51
56
|
messages: SideQueryMessage[];
|
|
52
57
|
/** Tool definitions the model may call in its reply. The engine NEVER executes them — any calls
|
|
53
58
|
* come back in `SideQueryResult.toolCalls` for the caller to interpret. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { thrownText } from "../thrown-value.js";
|
|
3
3
|
import { beginContract } from "./contract-harness.js";
|
|
4
|
-
export const CORRUPT_BODY_FORMS = ["not-an-object", "missing-required", "wrong-type", "torn-json"];
|
|
4
|
+
export const CORRUPT_BODY_FORMS = Object.freeze(["not-an-object", "missing-required", "wrong-type", "torn-json"]);
|
|
5
5
|
async function observe(subject) {
|
|
6
6
|
try {
|
|
7
7
|
const value = await subject.read();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertContainmentFaceValue } from "../tools/fs/containment-face.js";
|
|
2
2
|
import { assertContextSettingsValue } from "./context-settings.js";
|
|
3
|
-
export const SWAPPABLE_DEP_SEATS = ["models", "tiers", "readFace", "writeFace", "contextSettings"];
|
|
3
|
+
export const SWAPPABLE_DEP_SEATS = Object.freeze(["models", "tiers", "readFace", "writeFace", "contextSettings"]);
|
|
4
4
|
const SWAPPABLE_DEP_SEAT_SET = new Set(SWAPPABLE_DEP_SEATS);
|
|
5
5
|
export function isSwappableDepSeat(k) {
|
|
6
6
|
return SWAPPABLE_DEP_SEAT_SET.has(k);
|
|
@@ -2,7 +2,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
2
2
|
import { escapeEnvelopeTag, inlineUntrusted, sanitizeUntrustedText } from "./untrusted-text.js";
|
|
3
3
|
import { PEER_MESSAGE_NOTICE } from "../agents/peer-admission.js";
|
|
4
4
|
import { buildCrossSessionEnvelope, CROSS_SESSION_MESSAGE_NOTICE, neutralizePeerBody } from "../agents/cross-session-envelope.js";
|
|
5
|
-
export const NOTIFICATION_INJECTION_OUTCOMES = ["queued", "parked", "parked_durably", "dropped_duplicate"];
|
|
5
|
+
export const NOTIFICATION_INJECTION_OUTCOMES = Object.freeze(["queued", "parked", "parked_durably", "dropped_duplicate"]);
|
|
6
6
|
export function isNotificationInjectionOutcome(value) {
|
|
7
7
|
return typeof value === "string" && NOTIFICATION_INJECTION_OUTCOMES.includes(value);
|
|
8
8
|
}
|
|
@@ -143,13 +143,13 @@ export const MAX_PENDING_EVENTS_PER_TASK = 50;
|
|
|
143
143
|
export const MAX_PENDING_EVENTS_PER_SESSION = 200;
|
|
144
144
|
export const MAX_PENDING_SESSIONS = 100;
|
|
145
145
|
export const MAX_SESSION_BOX_BINDINGS = 4096;
|
|
146
|
-
export const HANDBACK_STATES = ["send", "flagged", "withheld"];
|
|
146
|
+
export const HANDBACK_STATES = Object.freeze(["send", "flagged", "withheld"]);
|
|
147
147
|
export function handbackStateOf(facts) {
|
|
148
148
|
if (facts.warning !== undefined)
|
|
149
149
|
return "flagged";
|
|
150
150
|
return facts.result || facts.partialText ? "send" : "withheld";
|
|
151
151
|
}
|
|
152
|
-
export const COMPACTION_KINDS = ["manual", "auto", "reactive"];
|
|
152
|
+
export const COMPACTION_KINDS = Object.freeze(["manual", "auto", "reactive"]);
|
|
153
153
|
export function compactionKindOf(trigger) {
|
|
154
154
|
return trigger === "forced" ? "reactive" : trigger;
|
|
155
155
|
}
|
|
@@ -161,7 +161,7 @@ export function terminalFrameFacets(facts) {
|
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
163
|
export const ENGINE_FRAME_CODEC_VERSION = 1;
|
|
164
|
-
export const ENGINE_FRAME_TASK_TYPES = ["background_agent", "monitor", "workflow"];
|
|
164
|
+
export const ENGINE_FRAME_TASK_TYPES = Object.freeze(["background_agent", "monitor", "workflow"]);
|
|
165
165
|
const SHELL_LESS_CARRIERS = ["agentMessage", "crossSessionMessage", "crossSessionNotice", "inboxNotice", "_sema_provenance"];
|
|
166
166
|
export function isEngineFrameParkable(payload) {
|
|
167
167
|
return ENGINE_FRAME_TASK_TYPES.includes(payload.task_type) && SHELL_LESS_CARRIERS.every((k) => payload[k] === undefined);
|
|
@@ -101,7 +101,7 @@ export function spoolDropNote(acct) {
|
|
|
101
101
|
return "[!] an unknown amount of earlier output was permanently dropped before this window (buffer evicted)\n";
|
|
102
102
|
return "";
|
|
103
103
|
}
|
|
104
|
-
export const TASK_OUTPUT_REPLAY_KINDS = ["full", "cursor"];
|
|
104
|
+
export const TASK_OUTPUT_REPLAY_KINDS = Object.freeze(["full", "cursor"]);
|
|
105
105
|
export const REPLAY_FACE = {
|
|
106
106
|
full: { stdout: "stdout (full, re-readable)", stdoutWhenGapped: "stdout (re-readable spool — INCOMPLETE, see note above)", stderr: "stderr" },
|
|
107
107
|
cursor: { stdout: "new stdout", stdoutWhenGapped: undefined, stderr: "new stderr" },
|
|
@@ -66,6 +66,17 @@ export type TerminalCause = {
|
|
|
66
66
|
code?: string;
|
|
67
67
|
/** Human-readable failure text. */
|
|
68
68
|
message?: string;
|
|
69
|
+
/**
|
|
70
|
+
* A machine-readable account of the failure beside `code`, for the one shape a consumer offers its own way out
|
|
71
|
+
* of: `provider_rejected` — the provider REFUSED the request as sent (a 4xx answer) and that refusal named this
|
|
72
|
+
* result. `status` is the refusal's HTTP status (`TaskResult.apiFailure.status`, the same value);
|
|
73
|
+
* `code` the provider's own word for it from its error envelope (`AssistantMessage.apiErrorCode`), when it
|
|
74
|
+
* stated one. PRESENCE: only on that terminal — a run whose higher-ranked cause named the result, a 5xx, a
|
|
75
|
+
* connect / stream failure, an in-band error inside a 200 and every non-provider failure leave it absent.
|
|
76
|
+
* A 401 / 403 (credentials) and a 429 (rate limit) are refusals too: branch on `status` (or on `code` above)
|
|
77
|
+
* to tell a request a consumer may change and resend from one it may not.
|
|
78
|
+
*/
|
|
79
|
+
detail?: TerminalFailureDetail;
|
|
69
80
|
/**
|
|
70
81
|
* The durable pause this failure STANDS IN FOR — set only by the nested hard boundary
|
|
71
82
|
* (`mapNestedSuspend`): an orchestrator's nested leg paused on a durable gate, the orchestrator cannot
|
|
@@ -117,6 +128,14 @@ export type TerminalCause = {
|
|
|
117
128
|
*/
|
|
118
129
|
restoreMode?: "snapshot" | "park_only";
|
|
119
130
|
};
|
|
131
|
+
/** The machine-readable account a `failed` cause may carry ({@link TerminalCause}). `kind` is closed; one member. */
|
|
132
|
+
export interface TerminalFailureDetail {
|
|
133
|
+
kind: "provider_rejected";
|
|
134
|
+
/** The refusal's HTTP status, 400–499. */
|
|
135
|
+
status: number;
|
|
136
|
+
/** The provider's own word for the refusal (its error envelope's `error.code`, else `error.type`), when stated. */
|
|
137
|
+
code?: string;
|
|
138
|
+
}
|
|
120
139
|
/** The paused arm alone — the shape the run's committed-pause holder carries. */
|
|
121
140
|
/**
|
|
122
141
|
* The one disposition table over `TerminalCause["kind"]`: which causes a resume journal may REPLAY
|
package/dist/core/tool-face.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export const TOOL_FAMILIES = ["shell", "file-read", "file-write", "search", "web", "delegate", "plan", "scaffold", "memory", "protocol", "other"];
|
|
2
|
-
export const TOOL_MOUNT_TAGS = ["hands", "backgroundTasks", "workflow", "delegation", "hostInternals", "peerLane", "scheduler", "memory", "scenario", "always", "optional"];
|
|
3
|
-
export const TOOL_APPROVAL_CARDS = ["file-edit", "file-write", "notebook-edit", "shell", "fetch", "plan", "question", "generic"];
|
|
4
|
-
export const TOOL_PATH_ACCESSES = ["read", "create", "edit"];
|
|
5
|
-
export const TOOL_PATH_BASES = ["cwd", "root"];
|
|
6
|
-
export const TOOL_PATH_ABSENCES = ["none", "base"];
|
|
1
|
+
export const TOOL_FAMILIES = Object.freeze(["shell", "file-read", "file-write", "search", "web", "delegate", "plan", "scaffold", "memory", "protocol", "other"]);
|
|
2
|
+
export const TOOL_MOUNT_TAGS = Object.freeze(["hands", "backgroundTasks", "workflow", "delegation", "hostInternals", "peerLane", "scheduler", "memory", "scenario", "always", "optional"]);
|
|
3
|
+
export const TOOL_APPROVAL_CARDS = Object.freeze(["file-edit", "file-write", "notebook-edit", "shell", "fetch", "plan", "question", "generic"]);
|
|
4
|
+
export const TOOL_PATH_ACCESSES = Object.freeze(["read", "create", "edit"]);
|
|
5
|
+
export const TOOL_PATH_BASES = Object.freeze(["cwd", "root"]);
|
|
6
|
+
export const TOOL_PATH_ABSENCES = Object.freeze(["none", "base"]);
|
|
7
7
|
export function pathTargetSlotOnly(pt) {
|
|
8
8
|
return {
|
|
9
9
|
param: pt.param,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type AskOrigin, type AssertAllKeysHandled } from "./ask-origin.js";
|
|
2
2
|
import { type ReadRootGrantScreen } from "./read-root-grant-screen.js";
|
|
3
3
|
import { type Settlement } from "./gate-outcome.js";
|
|
4
|
+
import { type DecisionReason } from "./decision-receipt.js";
|
|
5
|
+
export { isEngineRefusal } from "./decision-receipt.js";
|
|
4
6
|
/** A tool call presented to a policy before it executes. */
|
|
5
7
|
/** design/388 B6 — the per-call face (see {@link ToolCallRequest.face}). */
|
|
6
8
|
export interface ToolCallFace {
|
|
@@ -153,8 +155,9 @@ export declare function advertisedWritableRootsSeat(seat: {
|
|
|
153
155
|
} | undefined): {
|
|
154
156
|
advertisedWritableRoots?: readonly string[];
|
|
155
157
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
/** Declared beside the receipt's list (decision-receipt.ts) — a new member added to either side is a compile error rather
|
|
159
|
+
* than a silent drift between the type and the screen; re-exported here, the module every consumer of the decision reads. */
|
|
160
|
+
export type { DecisionReason };
|
|
158
161
|
/** Carry every engine attestation from a decision onto a reconstruction of it — the
|
|
159
162
|
* seam for the engine's OWN clone sites outside this module (the inherited chain's final-form re-check carries the
|
|
160
163
|
* post-rewrite args on a spread of an ancestor's deny). Not re-exported from `src/index.ts`. */
|
|
@@ -621,8 +624,10 @@ export interface ToolPolicyProjection {
|
|
|
621
624
|
* self-checks with those.
|
|
622
625
|
*/
|
|
623
626
|
export declare function namespacedCoveringEntries(entries: readonly string[] | undefined): readonly string[];
|
|
624
|
-
/** Does any covering entry reach
|
|
625
|
-
|
|
627
|
+
/** Does any covering entry reach the call — under ANY name it answers to, the same reading the exact lists take through
|
|
628
|
+
* `nameListHolds` (a covering entry and an exact entry in one list are one rule: it reaches the call iff its name is on the
|
|
629
|
+
* list)? See {@link namespacedCoveringEntries}. */
|
|
630
|
+
export declare function namespacedCoveringHit(covering: readonly string[], call: Pick<ToolCallRequest, "toolName" | "face">): boolean;
|
|
626
631
|
/**
|
|
627
632
|
* Execute a persisted {@link ToolPolicyProjection} against a call (F-012 L1): returns the first
|
|
628
633
|
* component's deny, or `undefined` when the projection has no opinion (it is deny-only by
|
|
@@ -754,7 +759,7 @@ export declare const DECISION_MEMBERS: readonly DecisionMember[];
|
|
|
754
759
|
* mode arbitration, the approval request, the rewrite that executes) reads the copy; a copy handed to the screen again
|
|
755
760
|
* comes back as it is, unread. In order: (1) the CARRIER's type — a decision that is not an object (a string, a function
|
|
756
761
|
* holding the members) has no `action`; (2) the ONE READ — ONE function ({@link receiveContract}) for the decision and for the
|
|
757
|
-
* records it nests (
|
|
762
|
+
* records it nests (`receiveNestedRecords`, decision-receipt.ts), on an ORDINARY object (a Proxy, as the carrier or anywhere on its prototype
|
|
758
763
|
* chain, is refused before a trap of its could run): each contract member absent or an own enumerable DATA property, its value
|
|
759
764
|
* taken off the carrier's own descriptor (a member outside the contract is neither read nor carried — a policy's self-declared
|
|
760
765
|
* record on another name finds no reader); any other spelling refused by name — an accessor (never invoked), a member the
|
|
@@ -762,7 +767,7 @@ export declare const DECISION_MEMBERS: readonly DecisionMember[];
|
|
|
762
767
|
* descriptor, never read; (3) the retired `reason` field as an OWN property, judged without a read (authorship of the old
|
|
763
768
|
* spelling: a prototype exposing it never wrote it; ignoring it silently dropped the policy's text); (4) the VALUES, off the engine's
|
|
764
769
|
* one-read record, never a second read of the caller's object — the retired window absent, `action` in
|
|
765
|
-
*
|
|
770
|
+
* its closed set (`screenContractValues`, decision-receipt.ts — every closed-set member is held to its set there; a consumer ranks a decision by testing two of its words, so any other value used to rank as an
|
|
766
771
|
* allow; a `canUseTool` result outside its set is a failure in CC) and `decisionReason` in {@link DECISION_REASONS} when
|
|
767
772
|
* present (absence stays legal); (5) the DETACH —
|
|
768
773
|
* the contract members' values structured-cloned ({@link tryCloneArgs}; one that cannot be, a function inside a rewrite,
|
|
@@ -778,10 +783,12 @@ export declare const DECISION_MEMBERS: readonly DecisionMember[];
|
|
|
778
783
|
* there is what is cloned and what runs — one read, consistent) and by a refusal's rendering of a foreign word (`String(v)` on an
|
|
779
784
|
* out-of-set `action`, after the deny is decided). Same-process JavaScript has no trust boundary a function can enforce.
|
|
780
785
|
*
|
|
781
|
-
* `reasonIsNonInput` (the HOOK seam):
|
|
782
|
-
* `
|
|
783
|
-
*
|
|
784
|
-
* opinion
|
|
786
|
+
* `reasonIsNonInput` (the HOOK seam): the same receipt, with the two differences the seam's consumers imply — a hook's
|
|
787
|
+
* `decisionReason` is DISCARDED there (every mint point downstream re-stamps `"hook"`) and so is not held to its set, and a
|
|
788
|
+
* hook result may leave `action` out (no opinion; the policy still adjudicates). `undefined`, `null` and `false` are no
|
|
789
|
+
* opinion (what a hook body yields when it has nothing to say). A refusal the receipt mints at this seam is the ENGINE's
|
|
790
|
+
* decision ({@link isEngineRefusal}) and exits through the engine's fail-closed arm (the deny observer), never as the hook's
|
|
791
|
+
* own deny.
|
|
785
792
|
*/
|
|
786
793
|
export declare function refuseOutOfContractDecision(d: PermissionResult, opts?: {
|
|
787
794
|
reasonIsNonInput?: boolean;
|
|
@@ -1780,4 +1787,3 @@ onLateSettlement?: (late: {
|
|
|
1780
1787
|
kind: "error";
|
|
1781
1788
|
error: unknown;
|
|
1782
1789
|
}) => void): Promise<ResolvedAsk>;
|
|
1783
|
-
export {};
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -13,7 +13,8 @@ import { boundInputHashOf } from "./canonical-json.js";
|
|
|
13
13
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "./untrusted-text.js";
|
|
14
14
|
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
15
15
|
import { thrownText } from "./thrown-value.js";
|
|
16
|
-
import { receiveContract, refusedOutOfContract } from "./decision-receipt.js";
|
|
16
|
+
import { nestedRecordsOf, receiveContract, receiveNestedRecords, refusedOutOfContract, screenContractValues } from "./decision-receipt.js";
|
|
17
|
+
export { isEngineRefusal } from "./decision-receipt.js";
|
|
17
18
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, parsePermissionRule } from "./permission-rules.js";
|
|
18
19
|
import { protocolOf } from "./protocol-table.js";
|
|
19
20
|
import { isAbsoluteForFamily, isAbsolutePathForm, joinForFamily, normalizeAbsPathLexically, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
|
|
@@ -23,8 +24,6 @@ const shellToolsListHolds = (set, req) => heldBy("policy.shellToolsList", set, r
|
|
|
23
24
|
export function advertisedWritableRootsSeat(seat) {
|
|
24
25
|
return seat === undefined ? {} : { advertisedWritableRoots: Object.freeze([...seat.roots]) };
|
|
25
26
|
}
|
|
26
|
-
const DECISION_REASONS = ["rule", "mode", "hook", "safety", "classifier", "persisted_rule", "sandbox", "org_rule", "org_unavailable", "read_only"];
|
|
27
|
-
const DECISION_REASON_SET = new Set(DECISION_REASONS);
|
|
28
27
|
function settledByNobody(kind) {
|
|
29
28
|
return { kind, who: { party: "none" }, when: Date.now() };
|
|
30
29
|
}
|
|
@@ -164,20 +163,20 @@ export function toolPolicyWriteTargetGuard(policies) {
|
|
|
164
163
|
export function namespacedCoveringEntries(entries) {
|
|
165
164
|
return (entries ?? []).filter(isNamespacedCoveringRuleName);
|
|
166
165
|
}
|
|
167
|
-
export function namespacedCoveringHit(covering,
|
|
168
|
-
return covering.length > 0 && covering.some((e) => namespacedRuleNameCovers(e,
|
|
166
|
+
export function namespacedCoveringHit(covering, call) {
|
|
167
|
+
return covering.length > 0 && nameListHolds({ has: (name) => covering.some((e) => namespacedRuleNameCovers(e, name)) }, call);
|
|
169
168
|
}
|
|
170
169
|
export function checkToolPolicyProjection(projection, req) {
|
|
171
170
|
const listed = (list) => heldBy("policy.projectionReplay", { has: (n) => list.includes(n) }, req);
|
|
172
171
|
for (const c of projection.components) {
|
|
173
172
|
if (c.kind === "tool_deny") {
|
|
174
|
-
if (listed(c.names) || namespacedCoveringHit(namespacedCoveringEntries(c.names), req
|
|
173
|
+
if (listed(c.names) || namespacedCoveringHit(namespacedCoveringEntries(c.names), req)) {
|
|
175
174
|
return { action: "deny", message: `tool "${req.toolName}" is denied by a frozen inherited policy projection` };
|
|
176
175
|
}
|
|
177
176
|
continue;
|
|
178
177
|
}
|
|
179
178
|
if (c.kind === "tool_allowlist") {
|
|
180
|
-
if (!listed(c.names) && !namespacedCoveringHit(namespacedCoveringEntries(c.names), req
|
|
179
|
+
if (!listed(c.names) && !namespacedCoveringHit(namespacedCoveringEntries(c.names), req)) {
|
|
181
180
|
return { action: "deny", message: `tool "${req.toolName}" is not in a frozen inherited policy projection's allowlist` };
|
|
182
181
|
}
|
|
183
182
|
continue;
|
|
@@ -247,87 +246,56 @@ export function constraintChainDigest(chain) {
|
|
|
247
246
|
}
|
|
248
247
|
const ALLOW = Object.freeze({ action: "allow" });
|
|
249
248
|
const RETIRED_TEXT_FIELD = "reason";
|
|
250
|
-
const RETIRED_WINDOW_MEMBER = "autoDenyAfterMs";
|
|
251
|
-
const RETIRED_WINDOW_MEMBER_DENY_MESSAGE = `a permission decision's "denialLimitFallback" carries "${RETIRED_WINDOW_MEMBER}" — the decision names the counts only; ` +
|
|
252
|
-
`the auto-deny window is decided by the ask's route (a live approver arms it, every other route runs none) and cannot be ` +
|
|
253
|
-
`declared by a policy; denied fail-closed rather than silently replacing the declared number`;
|
|
254
|
-
const carriesRetiredWindow = (fallback) => typeof fallback === "object" && fallback !== null && fallback[RETIRED_WINDOW_MEMBER] !== undefined;
|
|
255
|
-
const DECISION_ACTIONS = Object.keys({ allow: 0, deny: 0, ask: 0 });
|
|
256
|
-
const DECISION_ACTION_SET = new Set(DECISION_ACTIONS);
|
|
257
249
|
export const DECISION_MEMBERS = Object.keys({
|
|
258
250
|
action: 0, updatedInput: 0, message: 0, decisionReason: 0, requiresRealApproval: 0, denialLimitFallback: 0, persistedRuleShadowed: 0, probeReason: 0,
|
|
259
251
|
probeCause: 0, probeMandated: 0, mandated: 0, mandate: 0, probeReadRootCandidate: 0, matchedAskRule: 0, ruleEvidence: 0, segmentCoverage: 0,
|
|
260
252
|
origin: 0, askClass: 0, classes: 0, guard: 0, ruleUnreadable: 0, authoredBy: 0, foldedAskClasses: 0,
|
|
261
253
|
});
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
["probeCause.", "roots", OPERAND_MEMBERS],
|
|
267
|
-
["probeCause.", "further", OPERAND_MEMBERS],
|
|
268
|
-
];
|
|
269
|
-
const refusedAction = (what) => refusedOutOfContract(`a permission decision's "action" is ${what} — the closed set is ${DECISION_ACTIONS.join("/")}; denied fail-closed rather than reading a word outside it as an allow`);
|
|
270
|
-
function renderForeignValue(v) {
|
|
271
|
-
try {
|
|
272
|
-
return JSON.stringify(String(v).slice(0, 64));
|
|
273
|
-
}
|
|
274
|
-
catch {
|
|
275
|
-
return `[unprintable ${typeof v}]`;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
254
|
+
const HOOK_RESULT_MEMBERS = [...DECISION_MEMBERS, "additionalContext"];
|
|
255
|
+
const HOOK_SEAM_UNREAD = new Set(["decisionReason"]);
|
|
256
|
+
const HOOK_SEAM_OPTIONAL = new Set(["action"]);
|
|
257
|
+
const HOOK_RECEIPTS = new WeakSet();
|
|
278
258
|
const RETIRED_TEXT_FIELD_DENY_MESSAGE = `a permission decision carries the retired "${RETIRED_TEXT_FIELD}" field — rename it to "message" (the one text field ` +
|
|
279
259
|
`a decision carries); denied fail-closed rather than executing a decision whose text this layer cannot read`;
|
|
280
260
|
const ENGINE_RECEIPTS = new WeakSet();
|
|
281
261
|
export function refuseOutOfContractDecision(d, opts) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return carriesRetiredWindow(d.denialLimitFallback) ? refusedOutOfContract(RETIRED_WINDOW_MEMBER_DENY_MESSAGE) : d;
|
|
262
|
+
const hookSeam = opts?.reasonIsNonInput === true;
|
|
263
|
+
if (hookSeam && (d === null || d === undefined || Object.is(d, false)))
|
|
264
|
+
return d;
|
|
265
|
+
if (typeof d !== "object" || d === null) {
|
|
266
|
+
return refusedOutOfContract(`a permission decision's "action" is absent (the decision is ${d === null || d === undefined ? String(d) : `a ${typeof d}`}, not an object) — the closed set is allow/deny/ask; denied fail-closed rather than reading a word outside it as an allow`);
|
|
288
267
|
}
|
|
289
|
-
if (
|
|
290
|
-
return refusedAction(`absent (the decision is ${d === null || d === undefined ? String(d) : `a ${typeof d}`}, not an object)`);
|
|
291
|
-
if (ENGINE_RECEIPTS.has(d))
|
|
268
|
+
if ((hookSeam ? HOOK_RECEIPTS : ENGINE_RECEIPTS).has(d))
|
|
292
269
|
return d;
|
|
293
|
-
const decision = receiveContract(d, DECISION_MEMBERS, "");
|
|
270
|
+
const decision = receiveContract(d, hookSeam ? HOOK_RESULT_MEMBERS : DECISION_MEMBERS, "");
|
|
294
271
|
if (decision.kind === "refused")
|
|
295
272
|
return decision.refused;
|
|
296
273
|
if (Object.prototype.hasOwnProperty.call(d, RETIRED_TEXT_FIELD))
|
|
297
274
|
return refusedOutOfContract(RETIRED_TEXT_FIELD_DENY_MESSAGE);
|
|
298
275
|
const contract = decision.held;
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const nested = receiveContract(held[member], members, `${holder}${member}.`);
|
|
305
|
-
if (nested.kind === "refused")
|
|
306
|
-
return nested.refused;
|
|
307
|
-
held[member] = records[`${holder}${member}.`] = nested.held;
|
|
308
|
-
}
|
|
309
|
-
if (carriesRetiredWindow(contract.denialLimitFallback))
|
|
310
|
-
return refusedOutOfContract(RETIRED_WINDOW_MEMBER_DENY_MESSAGE);
|
|
311
|
-
if (!DECISION_ACTION_SET.has(contract.action))
|
|
312
|
-
return refusedAction(contract.action === undefined ? "absent" : renderForeignValue(contract.action));
|
|
313
|
-
if (contract.decisionReason !== undefined && !DECISION_REASON_SET.has(contract.decisionReason)) {
|
|
314
|
-
return refusedOutOfContract(`a permission decision carries an unrecognized "decisionReason" value (${renderForeignValue(contract.decisionReason)}) — ` +
|
|
315
|
-
`the closed set is ${DECISION_REASONS.join("/")}; denied fail-closed rather than letting consumers branch on a value this layer cannot classify`);
|
|
276
|
+
const refused = receiveNestedRecords(contract) ?? screenContractValues(contract, hookSeam ? { skip: HOOK_SEAM_UNREAD, optional: HOOK_SEAM_OPTIONAL } : undefined);
|
|
277
|
+
if (refused !== undefined)
|
|
278
|
+
return refused;
|
|
279
|
+
if (hookSeam && contract.additionalContext !== undefined && typeof contract.additionalContext !== "string") {
|
|
280
|
+
return refusedOutOfContract(`a PreToolUse hook result's "additionalContext" is not a string — the member is the text appended for the model; denied fail-closed rather than rendering a value this layer cannot classify`);
|
|
316
281
|
}
|
|
317
282
|
const detached = tryCloneArgs(contract);
|
|
318
283
|
if (!detached.ok) {
|
|
319
|
-
return refusedOutOfContract(`a permission decision's members could not be detached from the
|
|
284
|
+
return refusedOutOfContract(`a permission decision's members could not be detached from the layer that returned them (${detached.reason}) — ` +
|
|
320
285
|
`the engine keeps its own copy of what it read; denied fail-closed`);
|
|
321
286
|
}
|
|
322
287
|
const receipt = Object.freeze(detached.value);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
288
|
+
for (const k of ["classes", "foldedAskClasses"])
|
|
289
|
+
if (Object.hasOwn(receipt, k) && Array.isArray(receipt[k]))
|
|
290
|
+
Object.freeze(receipt[k]);
|
|
291
|
+
for (const record of nestedRecordsOf(receipt)) {
|
|
292
|
+
for (const v of Object.values(record))
|
|
293
|
+
if (Array.isArray(v))
|
|
294
|
+
Object.freeze(v);
|
|
295
|
+
Object.freeze(record);
|
|
328
296
|
}
|
|
329
297
|
carryEngineAttestations(d, receipt);
|
|
330
|
-
ENGINE_RECEIPTS.add(receipt);
|
|
298
|
+
(hookSeam ? HOOK_RECEIPTS : ENGINE_RECEIPTS).add(receipt);
|
|
331
299
|
return receipt;
|
|
332
300
|
}
|
|
333
301
|
const DEADLINE_ELAPSED = Symbol("approval.deadline_elapsed");
|
|
@@ -424,11 +392,10 @@ export function createAllowDenyPolicy(opts) {
|
|
|
424
392
|
},
|
|
425
393
|
nameSets: [{ ...(opts.allow ? { allow: [...opts.allow] } : {}), ...(opts.deny ? { deny: [...opts.deny] } : {}) }],
|
|
426
394
|
check(req) {
|
|
427
|
-
|
|
428
|
-
if (nameListHolds(deny, req) || namespacedCoveringHit(denyCovering, toolName)) {
|
|
395
|
+
if (nameListHolds(deny, req) || namespacedCoveringHit(denyCovering, req)) {
|
|
429
396
|
return { action: "deny", message: `tool "${req.toolName}" is denied by policy` };
|
|
430
397
|
}
|
|
431
|
-
if (allow && !nameListHolds(allow, req) && !namespacedCoveringHit(allowCovering,
|
|
398
|
+
if (allow && !nameListHolds(allow, req) && !namespacedCoveringHit(allowCovering, req)) {
|
|
432
399
|
return { action: "deny", message: `tool "${req.toolName}" is not in the allowlist` };
|
|
433
400
|
}
|
|
434
401
|
return ALLOW;
|
|
@@ -464,11 +431,10 @@ export function createApprovalPolicy(opts) {
|
|
|
464
431
|
},
|
|
465
432
|
],
|
|
466
433
|
async check(req, signal) {
|
|
467
|
-
|
|
468
|
-
if (nameListHolds(deny, req) || namespacedCoveringHit(denyCovering, toolName)) {
|
|
434
|
+
if (nameListHolds(deny, req) || namespacedCoveringHit(denyCovering, req)) {
|
|
469
435
|
return { action: "deny", message: `tool "${req.toolName}" is denied by policy` };
|
|
470
436
|
}
|
|
471
|
-
if (nameListHolds(need, req) || namespacedCoveringHit(needCovering,
|
|
437
|
+
if (nameListHolds(need, req) || namespacedCoveringHit(needCovering, req)) {
|
|
472
438
|
const attest = (d, settlement) => attestEngineSettlement(d, { settlement, origin: "policy" }, req);
|
|
473
439
|
if (signal?.aborted) {
|
|
474
440
|
return attest({ action: "deny", message: `approval aborted for "${req.toolName}" (run or turn interrupted)` }, settledByNobody("task_aborted"));
|
|
@@ -518,7 +484,7 @@ export function createApprovalPolicy(opts) {
|
|
|
518
484
|
}
|
|
519
485
|
return attest({ action: "deny", message: `approval denied for "${req.toolName}"` }, settledByPerson("human_refused", undefined));
|
|
520
486
|
}
|
|
521
|
-
if (opts.denyByDefault && !nameListHolds(auto, req) && !namespacedCoveringHit(autoCovering,
|
|
487
|
+
if (opts.denyByDefault && !nameListHolds(auto, req) && !namespacedCoveringHit(autoCovering, req)) {
|
|
522
488
|
return { action: "deny", message: `tool "${req.toolName}" requires explicit allow` };
|
|
523
489
|
}
|
|
524
490
|
return ALLOW;
|
|
@@ -1051,7 +1017,7 @@ export function askApproverIdentity(onAsk) {
|
|
|
1051
1017
|
export function isLiveApproverSeat(onAsk) {
|
|
1052
1018
|
return typeof onAsk === "function";
|
|
1053
1019
|
}
|
|
1054
|
-
export const DURABLE_MANDATE_SOURCES = ["force_durable_gate", "durable_approval"];
|
|
1020
|
+
export const DURABLE_MANDATE_SOURCES = Object.freeze(["force_durable_gate", "durable_approval"]);
|
|
1055
1021
|
Object.freeze(DURABLE_MANDATE_SOURCES);
|
|
1056
1022
|
export function durableMandateSourceOf(args) {
|
|
1057
1023
|
if (args.forceDurableGate)
|
package/dist/core/tool-roster.js
CHANGED
|
@@ -11,7 +11,7 @@ const Name = Type.String({ minLength: 1, maxLength: TOOL_WIRE_NAME_MAX_CHARS });
|
|
|
11
11
|
const Short = Type.String({ maxLength: TOOL_CONTRACT_MAX_CHARS });
|
|
12
12
|
const Key = Type.String({ maxLength: TOOL_KEY_MAX_CHARS });
|
|
13
13
|
const Digest16 = Type.String({ minLength: 16, maxLength: 16 });
|
|
14
|
-
export const ROSTER_SOURCES = ["builtin", "caller", "host", "mcp", "a2a", "external", "synthetic"];
|
|
14
|
+
export const ROSTER_SOURCES = Object.freeze(["builtin", "caller", "host", "mcp", "a2a", "external", "synthetic"]);
|
|
15
15
|
export const ToolRosterRenderHints = Type.Object({
|
|
16
16
|
userFacingName: Type.Optional(Type.String({ maxLength: RENDER_HINT_MAX_CHARS })),
|
|
17
17
|
activity: Type.Optional(Type.String({ maxLength: RENDER_HINT_ACTIVITY_MAX_CHARS })),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const TOOL_SEARCH_DEFAULT_THRESHOLD_PERCENT = 10;
|
|
2
2
|
export const TOOL_SEARCH_INVALID_CODE = "config.tool_search_invalid";
|
|
3
|
-
export const TOOL_SEARCH_POLICIES = ["always", "never", "auto"];
|
|
3
|
+
export const TOOL_SEARCH_POLICIES = Object.freeze(["always", "never", "auto"]);
|
|
4
4
|
export function resolveToolSearch(setting) {
|
|
5
5
|
if (setting === undefined)
|
|
6
6
|
return { policy: "auto", thresholdPercent: TOOL_SEARCH_DEFAULT_THRESHOLD_PERCENT, fraction: TOOL_SEARCH_DEFAULT_THRESHOLD_PERCENT / 100 };
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -860,13 +860,14 @@ export type TraceEvent = {
|
|
|
860
860
|
} | {
|
|
861
861
|
/** A provider echo field was refused by {@link import("../brain/errors.js").PROVIDER_ECHO_SHAPE} (visible ASCII,
|
|
862
862
|
* 1–128 characters) and dropped: the response's `request-id` / `x-request-id` header values (`field:
|
|
863
|
-
* "requestId"`, read at connect — the id a failure would carry as `apiFailure.requestId`),
|
|
864
|
-
* its frames report (`field: "responseModel"`)
|
|
865
|
-
*
|
|
863
|
+
* "requestId"`, read at connect — the id a failure would carry as `apiFailure.requestId`), the model name
|
|
864
|
+
* its frames report (`field: "responseModel"`), or the word a refusal's error envelope states (`field:
|
|
865
|
+
* "apiErrorCode"`). One frame per provider response per field; the value itself is never carried
|
|
866
|
+
* (metadata-only). A count of these frames is this engine's drop count. */
|
|
866
867
|
kind: "brain.echo_dropped";
|
|
867
868
|
version: 1;
|
|
868
869
|
taskId: string;
|
|
869
|
-
field: "requestId" | "responseModel";
|
|
870
|
+
field: "requestId" | "responseModel" | "apiErrorCode";
|
|
870
871
|
ts: number;
|
|
871
872
|
} | {
|
|
872
873
|
/** C10 — a `preemptSignal` fired on a task that is NOT resource-suspend-eligible: the preempt is a
|
package/dist/core/types.d.ts
CHANGED
|
@@ -29,6 +29,6 @@ export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js"
|
|
|
29
29
|
export type { BackgroundChildEvent, DelegationLifecycleEvent } from "./delegation-frames.js";
|
|
30
30
|
export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle } from "./delegation-frames.js";
|
|
31
31
|
export type { ProjectMemoryLoad, ResumePreflightInfo, ResumePreflightVerdict, RunnerDeps } from "./runner-deps.js";
|
|
32
|
-
export type { EngineNotice, OwedUserInputAccount } from "./engine-notice.js";
|
|
33
|
-
export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, settleOwedUserInput, undrainedUserInputNotices } from "./engine-notice.js";
|
|
32
|
+
export type { EngineNotice, OwedUserInputAccount, UserSteerUndrainedCause } from "./engine-notice.js";
|
|
33
|
+
export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, settleOwedUserInput, undrainedUserInputNotices, USER_STEER_UNDRAINED_CAUSES } from "./engine-notice.js";
|
|
34
34
|
export type { AgentTool, Model, StreamFn, ThinkingLevel, ToolInputValidationContext, ToolInputVerdict };
|
package/dist/core/types.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { MODEL_ROLES } from "./model-seat.js";
|
|
2
2
|
export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
|
|
3
3
|
export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle } from "./delegation-frames.js";
|
|
4
|
-
export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, settleOwedUserInput, undrainedUserInputNotices } from "./engine-notice.js";
|
|
4
|
+
export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, settleOwedUserInput, undrainedUserInputNotices, USER_STEER_UNDRAINED_CAUSES } from "./engine-notice.js";
|
|
@@ -5,8 +5,8 @@ import { canonicalize } from "./canonical-json.js";
|
|
|
5
5
|
import { resolveCheckpointStore } from "./checkpoint-store.js";
|
|
6
6
|
import { isLiveQuestionFace } from "./ask-question.js";
|
|
7
7
|
import { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
|
|
8
|
-
export const AUTO_MODE_ARM_REASONS = ["armed", "no_intent", "no_face", "denied", "resolver_fault"];
|
|
9
|
-
export const LSP_LANES = ["tool", "diagnostics", "tool_and_diagnostics"];
|
|
8
|
+
export const AUTO_MODE_ARM_REASONS = Object.freeze(["armed", "no_intent", "no_face", "denied", "resolver_fault"]);
|
|
9
|
+
export const LSP_LANES = Object.freeze(["tool", "diagnostics", "tool_and_diagnostics"]);
|
|
10
10
|
export function deriveHandsSeam(handsMounted) {
|
|
11
11
|
return handsMounted ? { mounted: true } : { mounted: false, reason: "no_execution_env" };
|
|
12
12
|
}
|
|
@@ -183,7 +183,7 @@ function relativeUnderDataRoot(path, roots) {
|
|
|
183
183
|
}
|
|
184
184
|
return null;
|
|
185
185
|
}
|
|
186
|
-
export const WRITE_PROTECTION_TARGET_VIEWS = ["spelling-only", "target"];
|
|
186
|
+
export const WRITE_PROTECTION_TARGET_VIEWS = Object.freeze(["spelling-only", "target"]);
|
|
187
187
|
export function compileWriteProtection(entries, opts) {
|
|
188
188
|
const rows = resolveWriteProtectedTable(entries);
|
|
189
189
|
if (rows.length === 0)
|