@sema-agent/core 7.50.2 → 7.51.1
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 +313 -0
- package/dist/agents/child-failure.d.ts +5 -16
- package/dist/agents/child-failure.js +4 -24
- package/dist/agents/subagent-steps.js +2 -2
- package/dist/agents/subagent.d.ts +4 -13
- package/dist/agents/subagent.js +94 -204
- package/dist/agents/tool-filter.d.ts +2 -2
- package/dist/brain/anthropic.d.ts +1 -1
- package/dist/brain/anthropic.js +7 -5
- package/dist/brain/open-responses.d.ts +1 -1
- package/dist/brain/open-responses.js +3 -2
- package/dist/brain/openai.d.ts +1 -1
- package/dist/brain/openai.js +3 -2
- package/dist/brain/request-route.d.ts +2 -2
- package/dist/brain/route-adjudicator.d.ts +16 -7
- package/dist/brain/route-adjudicator.js +23 -7
- package/dist/brain/route-conformance.js +23 -5
- package/dist/brain/stream-engine.js +9 -3
- package/dist/core/a2a.js +6 -8
- package/dist/core/allow-entry.d.ts +45 -26
- package/dist/core/allow-entry.js +48 -24
- package/dist/core/arg-summary.d.ts +8 -85
- package/dist/core/arg-summary.js +4 -386
- package/dist/core/ask-origin.d.ts +35 -33
- package/dist/core/ask-origin.js +9 -11
- package/dist/core/checkpoint-requirements.d.ts +6 -0
- package/dist/core/checkpoint-requirements.js +1 -0
- package/dist/core/checkpoint-store.d.ts +15 -34
- package/dist/core/checkpoint-store.js +5 -4
- package/dist/core/classifier-unavailable-arm.d.ts +12 -8
- package/dist/core/classifier-unavailable-arm.js +7 -2
- package/dist/core/credential-vocabulary.d.ts +3 -2
- package/dist/core/credential-vocabulary.js +27 -18
- package/dist/core/delegation-frames.d.ts +40 -15
- package/dist/core/delegation-frames.js +34 -0
- package/dist/core/engine-capabilities.d.ts +15 -0
- package/dist/core/engine-capabilities.js +1 -0
- package/dist/core/engine-notice.d.ts +41 -14
- package/dist/core/gate-fold.js +9 -7
- package/dist/core/gate-lanes.js +6 -4
- package/dist/core/gate-outcome.d.ts +81 -15
- package/dist/core/gate-outcome.js +168 -67
- package/dist/core/governance-codes.d.ts +7 -1
- package/dist/core/governance-codes.js +9 -0
- package/dist/core/hooks.d.ts +4 -4
- package/dist/core/hooks.js +4 -3
- package/dist/core/mcp-connection-pool.d.ts +2 -0
- package/dist/core/mcp-connection-pool.js +2 -1
- package/dist/core/mcp-failure.js +2 -1
- package/dist/core/mcp-server-spec.d.ts +27 -5
- package/dist/core/mcp.d.ts +13 -38
- package/dist/core/mcp.js +28 -23
- package/dist/core/media-byte-cap.d.ts +25 -0
- package/dist/core/media-byte-cap.js +18 -2
- package/dist/core/memory-engine/consolidation-driver.js +1 -1
- package/dist/core/mode-policy.d.ts +25 -1
- package/dist/core/mode-policy.js +28 -13
- package/dist/core/own-property-receipt.d.ts +18 -0
- package/dist/core/own-property-receipt.js +39 -0
- package/dist/core/permission-rule-model.d.ts +32 -11
- package/dist/core/permission-rule-model.js +12 -7
- package/dist/core/persisted-rule-arms.d.ts +3 -3
- package/dist/core/prompt-section-seat.d.ts +54 -0
- package/dist/core/prompt-section-seat.js +49 -0
- package/dist/core/protocol-http-auth.d.ts +44 -0
- package/dist/core/protocol-http-auth.js +45 -0
- package/dist/core/read-face-seats.d.ts +30 -0
- package/dist/core/read-face-seats.js +38 -0
- package/dist/core/redaction-passes.d.ts +82 -0
- package/dist/core/redaction-passes.js +378 -0
- package/dist/core/remote-env.d.ts +4 -3
- package/dist/core/resume-startup-cause.d.ts +56 -0
- package/dist/core/resume-startup-cause.js +43 -0
- package/dist/core/rule-denial-sentence.d.ts +2 -2
- package/dist/core/runner/announce-once-ledger.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +17 -13
- package/dist/core/runner/classifier-ladder.js +12 -10
- package/dist/core/runner/construction-doors.js +9 -20
- package/dist/core/runner/contracts.d.ts +4 -4
- package/dist/core/runner/gate-exit.js +2 -2
- package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
- package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
- package/dist/core/runner/person-path-rows.d.ts +4 -3
- package/dist/core/runner/policy-throw.js +4 -4
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
- package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
- package/dist/core/runner/prepare-gate-stations.js +4 -4
- package/dist/core/runner/prepare-hands-readface.js +1 -19
- package/dist/core/runner/prepare-inherited-gate.js +10 -9
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
- package/dist/core/runner/prepare-policy-chain.js +10 -13
- package/dist/core/runner/prepare-project-context.d.ts +4 -4
- package/dist/core/runner/prepare-project-context.js +2 -1
- package/dist/core/runner/prepare-prompt-assembly.js +1 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-inputs.js +4 -4
- package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
- package/dist/core/runner/prepare-protocol-tools.js +3 -2
- package/dist/core/runner/prepare-task.js +6 -4
- package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
- package/dist/core/runner/prepare-turn-wiring.js +8 -7
- package/dist/core/runner/run-harness-handlers.js +2 -2
- package/dist/core/runner/run-leg.js +10 -9
- package/dist/core/runner/run-settle-and-teardown.js +5 -5
- package/dist/core/runner/run-turn-boundary.js +3 -2
- package/dist/core/runner/runtask.d.ts +1 -2
- package/dist/core/runner/runtask.js +8 -12
- package/dist/core/runner/session-rule-policy.d.ts +15 -11
- package/dist/core/runner/session-rule-policy.js +43 -33
- package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
- package/dist/core/runner/stream-settle-backstop.js +2 -1
- package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
- package/dist/core/runner/sub-agent-deny-lists.js +23 -12
- package/dist/core/runner/terminal-account.js +3 -3
- package/dist/core/runner/tool-output-projection.d.ts +5 -0
- package/dist/core/runner/tool-output-projection.js +6 -11
- package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
- package/dist/core/runner/tool-result-media-seat.js +13 -0
- package/dist/core/runner/turn-attachments.d.ts +13 -12
- package/dist/core/runner/turn-attachments.js +11 -7
- package/dist/core/runner-deps.d.ts +4 -3
- package/dist/core/safety-merge-corpus.js +21 -21
- package/dist/core/secret-env.d.ts +41 -15
- package/dist/core/secret-env.js +85 -19
- package/dist/core/session-policy-store.d.ts +89 -72
- package/dist/core/session-policy-store.js +100 -93
- package/dist/core/shell-read-silence.d.ts +38 -4
- package/dist/core/shell-read-silence.js +102 -0
- package/dist/core/side-query.d.ts +7 -8
- package/dist/core/skill-grant-binding.d.ts +32 -11
- package/dist/core/skill-grant-binding.js +11 -3
- package/dist/core/skill-grant.d.ts +4 -3
- package/dist/core/skill-grant.js +5 -4
- package/dist/core/skills-directory.d.ts +12 -1
- package/dist/core/skills-directory.js +17 -8
- package/dist/core/spec-contract.d.ts +5 -3
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
- package/dist/core/swappable-deps.d.ts +15 -1
- package/dist/core/swappable-deps.js +25 -1
- package/dist/core/synthetic-read-verdict.d.ts +16 -14
- package/dist/core/synthetic-read-verdict.js +28 -8
- package/dist/core/task-notification.d.ts +13 -3
- package/dist/core/task-notification.js +12 -1
- package/dist/core/task-spec.d.ts +14 -15
- package/dist/core/tool-name-readings.d.ts +0 -4
- package/dist/core/tool-name-readings.js +0 -1
- package/dist/core/tool-policy.d.ts +40 -9
- package/dist/core/tool-policy.js +25 -7
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-egress.d.ts +60 -11
- package/dist/core/untrusted-egress.js +120 -9
- package/dist/core/untrusted-text.js +16 -0
- package/dist/core/wiring-manifest.d.ts +22 -8
- package/dist/core/wiring-manifest.js +12 -4
- package/dist/engine/execution-env/kill-tree.js +4 -4
- package/dist/engine/execution-env/node-execution-env.d.ts +11 -4
- package/dist/engine/execution-env/node-execution-env.js +9 -4
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +4 -1
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/llm/types.d.ts +7 -9
- package/dist/engine/loop/agent-loop.d.ts +8 -3
- package/dist/engine/loop/agent-loop.js +26 -9
- package/dist/engine/loop/types.d.ts +10 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -1
- package/dist/engine/lsp/node-lsp-manager.js +2 -2
- package/dist/index.d.ts +22 -5
- package/dist/index.js +18 -5
- package/dist/orchestration/run-workflow-tool.js +8 -8
- package/dist/orchestration/workflow.js +2 -2
- package/dist/prompt-assembly/assemble.d.ts +3 -1
- package/dist/prompt-assembly/assemble.js +6 -0
- package/dist/prompts/cowork.d.ts +42 -0
- package/dist/prompts/cowork.js +529 -0
- package/dist/prompts/default.d.ts +14 -1
- package/dist/prompts/default.js +3 -0
- package/dist/stores/file/fs-atomic.js +2 -2
- package/dist/stores/file/session-policy-store.d.ts +6 -3
- package/dist/stores/file/session-policy-store.js +5 -16
- package/dist/tools/artifact/artifact-tool.js +2 -0
- package/dist/tools/fs/image-admission.d.ts +57 -4
- package/dist/tools/fs/image-admission.js +85 -0
- package/dist/tools/fs/path-containment.d.ts +2 -0
- package/dist/tools/fs/path-containment.js +5 -0
- package/dist/tools/fs/shell-read-check.d.ts +31 -6
- package/dist/tools/fs/shell-read-check.js +2 -4
- package/dist/tools/fs/shell-read-environment.d.ts +10 -25
- package/dist/tools/fs/shell-read-environment.js +6 -18
- package/dist/tools/fs/shell-read-establish.d.ts +3 -2
- package/dist/tools/fs/shell-read-establish.js +5 -5
- package/dist/tools/fs/shell-read-judge.d.ts +38 -8
- package/dist/tools/fs/shell-read-judge.js +12 -10
- package/dist/tools/web.js +2 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +112 -10
- package/wire-changes/core@7.51.0.json +2421 -0
- package/wire-changes/core@7.51.1.json +259 -0
|
@@ -130,6 +130,18 @@ export interface ToolCallRequest {
|
|
|
130
130
|
* a Runner with no `memoryEngineDir` would stamp. Read-only, frozen.
|
|
131
131
|
*/
|
|
132
132
|
exemptRoots?: readonly string[];
|
|
133
|
+
/**
|
|
134
|
+
* The request's statement that nobody can be asked about THIS call — the word of a run's own declaration
|
|
135
|
+
* (`TaskSpec.approvalReach`, the closed set `APPROVAL_REACH`), stated per request. Present (`"none"`) on every request the
|
|
136
|
+
* engine's synthetic probes hand a policy layer (the "would this call be refused outright?" judgments: the visibility
|
|
137
|
+
* probes of a file the model did not ask to read, the background memory consolidation's write probes); absent on a call
|
|
138
|
+
* the gate adjudicates, and on a bare request. A policy that would put the call to a person — an approval seat it awaits,
|
|
139
|
+
* a hook it invokes, a card or a message it sends — answers at once instead: the engine's own factories read this member
|
|
140
|
+
* before reaching a seat and answer {@link noApproverSeatDeny}; a deployment closure does the same. The signal handed
|
|
141
|
+
* beside such a request is LIVE (the judge's wait for that layer): an aborted one means only that the task ended.
|
|
142
|
+
* Read-only.
|
|
143
|
+
*/
|
|
144
|
+
approvalReach?: import("./wiring-manifest.js").ApprovalReach;
|
|
133
145
|
/**
|
|
134
146
|
* design/80 D-E-core: read-only per-run budget snapshot so a STATELESS policy can self-limit against
|
|
135
147
|
* state that survives suspend/resume (a fresh in-memory counter would reset every leg — the supervisor
|
|
@@ -272,11 +284,25 @@ export declare function attest<T extends object>(d: T, call: {
|
|
|
272
284
|
}, members: AttestationMembers): T;
|
|
273
285
|
/** `d` carrying `record` (a fresh object), or `d` itself when there is no record to put on it. */
|
|
274
286
|
export declare function withAttestation<T extends object>(d: T, record: EngineAttestation | undefined): T;
|
|
287
|
+
/** The `no_approver`-attested deny an engine policy answers on a request that states nobody can be asked about it
|
|
288
|
+
* ({@link ToolCallRequest.approvalReach}) instead of reaching a seat — read by a probe as "would ask an approver". */
|
|
289
|
+
export declare function noApproverSeatDeny(req: {
|
|
290
|
+
toolCallId: string;
|
|
291
|
+
toolName: string;
|
|
292
|
+
}, message: string, origin: AskOrigin): PermissionResult;
|
|
275
293
|
/** The `task_aborted`-attested deny an engine policy answers on an aborted signal instead of reaching a seat. */
|
|
276
294
|
export declare function abortedSeatDeny(req: {
|
|
277
295
|
toolCallId: string;
|
|
278
296
|
toolName: string;
|
|
279
297
|
}, message: string, origin: AskOrigin): PermissionResult;
|
|
298
|
+
/** THE question every engine factory asks before it would reach a person (an approver it awaits, a hook it invokes): may the
|
|
299
|
+
* seat be reached? Not when the task ended (an aborted signal ⇒ {@link abortedSeatDeny}, read first) nor when the request
|
|
300
|
+
* states that nobody can be asked about the call ({@link ToolCallRequest.approvalReach} ⇒ {@link noApproverSeatDeny});
|
|
301
|
+
* `undefined` ⇒ it may. `say` is the factory's own sentence for each. */
|
|
302
|
+
export declare function unaskedSeatAnswer(req: Pick<ToolCallRequest, "toolCallId" | "toolName" | "approvalReach">, signal: AbortSignal | undefined, say: {
|
|
303
|
+
aborted: string;
|
|
304
|
+
unreachable: string;
|
|
305
|
+
}, origin: AskOrigin): PermissionResult | undefined;
|
|
280
306
|
/**
|
|
281
307
|
* design/252 G-7 — how long an approver-attribution identifier may be.
|
|
282
308
|
*
|
|
@@ -575,12 +601,14 @@ export declare function decisionText(d: PermissionResult): string | undefined;
|
|
|
575
601
|
*
|
|
576
602
|
* `check` may be async, which is also how **human-in-the-loop approval** works: a deployment can
|
|
577
603
|
* hold the promise open until an operator approves/denies. `signal` fires when the task aborts
|
|
578
|
-
* (timeout / max turns / cancel) or the asking turn is interrupted (design/384)
|
|
579
|
-
* pending approval (F4; the Runner also races `check` against it). An
|
|
604
|
+
* (timeout / max turns / cancel) or the asking turn is interrupted (design/384), and on a synthetic probe when the judge
|
|
605
|
+
* stops waiting for this layer — honor it to release a pending approval (F4; the Runner also races `check` against it). An
|
|
606
|
+
* already-aborted one: see `check`. A request that states nobody can be asked about it: {@link ToolCallRequest.approvalReach}.
|
|
580
607
|
*/
|
|
581
608
|
export interface ToolPolicy {
|
|
582
609
|
/** @contract policy.check.aborted_signal_reaches_nobody — handed an already-aborted signal, `check` reaches no person and
|
|
583
|
-
* no external approval (it answers at once, or with a `task_aborted` deny)
|
|
610
|
+
* no external approval (it answers at once, or with a `task_aborted` deny). The engine's probes do not rely on it: they hand a
|
|
611
|
+
* live signal and state on the request that nobody can be asked (`approvalReach: "none"`, answered with `noApproverSeatDeny`). */
|
|
584
612
|
check(req: ToolCallRequest, signal?: AbortSignal): PermissionResult | Promise<PermissionResult>;
|
|
585
613
|
/**
|
|
586
614
|
* The policy's CONSTRAINT PROJECTION — its deterministic DENY-verdict half expressed as pure data
|
|
@@ -1295,10 +1323,12 @@ export interface AskRequest {
|
|
|
1295
1323
|
* mint site added later is covered by construction); optional on the type because a deployment may
|
|
1296
1324
|
* invoke its approver function directly. */
|
|
1297
1325
|
readonly boundInputHash?: string;
|
|
1298
|
-
/** A call to the engine's `Skill` tool: the hash of {@link boundGrants} — the digest a parked approval of the same card is judged by, so a live card reconciles against its parked row by `(toolCallId, boundGrantDigest)` and an answer is remembered by "this tool + this binding". Present exactly on that call (whoever asked, however the question was worded)
|
|
1326
|
+
/** A call to the engine's `Skill` tool: the hash of {@link boundGrants} — the digest a parked approval of the same card is judged by, so a live card reconciles against its parked row by `(toolCallId, boundGrantDigest)` and an answer is remembered by "this tool + this binding". Present exactly on that call (whoever asked, however the question was worded); every other card carries {@link boundGrantAbsence} instead; `message` renders the same lists narrowed to what releases in the leg's mode. Reconciliation metadata, never adjudication input. */
|
|
1299
1327
|
readonly boundGrantDigest?: string;
|
|
1300
1328
|
/** The value {@link boundGrantDigest} hashes: `{ skill, allowedTools, disallowedTools }` in canonical spelling (de-duplicated, sorted), `skill: null` when no loaded skill is named; a fresh copy per ask; same presence. Display metadata (deployment text — render with contextual escaping). */
|
|
1301
1329
|
readonly boundGrants?: import("./skill-grant-binding.js").BoundSkillGrants;
|
|
1330
|
+
/** Why this card carries no {@link boundGrantDigest} — every card the engine mints carries exactly one of the two: `not_bindable` (the call is not an activation of the engine's `Skill` tool, so it binds no grant and an answer may be remembered by the tool) or `unreadable` (a Skill card whose binding cannot be read — said by a stored row's reader; a live card always has its digest). The same factory mints both members; the park row and the inbox projection carry the same member. Reconciliation metadata, never adjudication input. */
|
|
1331
|
+
readonly boundGrantAbsence?: import("./skill-grant-binding.js").BoundGrantAbsence;
|
|
1302
1332
|
/**
|
|
1303
1333
|
* design/179 §4, re-formed by design/375 §3.1 (BREAKING — this seat REPLACES the retired
|
|
1304
1334
|
* `ruleSuggestions` key, which is never emitted again; the new name is the safe failure posture for
|
|
@@ -1358,8 +1388,9 @@ export interface AskRequest {
|
|
|
1358
1388
|
* seat's to say: it is answered per ask by {@link readRootCandidate}, which is present exactly when
|
|
1359
1389
|
* widening the session's read directories would clear this one and absent for every other member of
|
|
1360
1390
|
* the family — so a surface reads that seat and never infers an answer from this word;
|
|
1361
|
-
* · `"
|
|
1362
|
-
*
|
|
1391
|
+
* · `"allow_outranked"` — an allow-layer member already matched this call and this question outranks it
|
|
1392
|
+
* ({@link allowRuleShadowed} says which): another allow rule would be outranked the same way;
|
|
1393
|
+
* · `"ask_rule_matched"` — an explicit `ask` rule matched: it keeps asking while it stands;
|
|
1363
1394
|
* · `"lane_cannot_speak"` — the lane is armed and this ask IS clearable, but the offer lane
|
|
1364
1395
|
* produced no rule for this command. Usually because the grammar has no text for it (a
|
|
1365
1396
|
* substitution, a subshell, a backgrounded process, or a simple command carrying a redirection —
|
|
@@ -1381,7 +1412,7 @@ export interface AskRequest {
|
|
|
1381
1412
|
* and a surface that ignores it renders exactly the card it rendered before. The durable park row
|
|
1382
1413
|
* carries the same seat under the same contract, filled by the same one factory.
|
|
1383
1414
|
*/
|
|
1384
|
-
readonly ruleOffersAbsence?: "
|
|
1415
|
+
readonly ruleOffersAbsence?: import("./ask-origin.js").RuleOffersAbsence;
|
|
1385
1416
|
/** The human/model-readable prompt explaining what needs approval. */
|
|
1386
1417
|
message: string;
|
|
1387
1418
|
/** Runner-filled source identity (design S1d §1): the end-user {@link AskRequest} issuing task's
|
|
@@ -1444,7 +1475,7 @@ export interface AskRequest {
|
|
|
1444
1475
|
* remembered from an earlier call, at any scope, a session included — can retire this question; it is
|
|
1445
1476
|
* answered only for THIS call, by an answerer (see the ask-arm member of the same name for who may). ABSENT is not the inverse claim: a person's own explicit `ask` rule and a
|
|
1446
1477
|
* matching allow rule the lane already refused both leave it absent and still block rule clearance
|
|
1447
|
-
* ({@link ruleOffersAbsence} words
|
|
1478
|
+
* ({@link ruleOffersAbsence} words them `ask_rule_matched` / `allow_outranked`). The wire twin of the
|
|
1448
1479
|
* {@link PermissionResult} ask-arm member of the same name, minted once by the engine at the gate's
|
|
1449
1480
|
* origin stamp and copied here by the four mint stations. The DOORS are enumerated once, at
|
|
1450
1481
|
* {@link import("./ask-origin.js").ruleClearanceOf}: the tool seat's own egress / irreversibility
|
|
@@ -1452,7 +1483,7 @@ export interface AskRequest {
|
|
|
1452
1483
|
* that could not answer, a write the protection judge matched or could not resolve, governance
|
|
1453
1484
|
* ({@link requiresRealApproval}), a hook's screening authority, and an ask an ANCESTOR's approver
|
|
1454
1485
|
* decides. Not a door: a person's own explicit `ask` rule, which {@link ruleOffersAbsence} words
|
|
1455
|
-
* `
|
|
1486
|
+
* `ask_rule_matched` — a rule they wrote is speaking, and a posture answering it crosses no mandate.
|
|
1456
1487
|
*
|
|
1457
1488
|
* WHY a surface must read THIS member and not its neighbour. {@link ruleOffersAbsence} answers what
|
|
1458
1489
|
* the RULE-OFFER LANE has to give, and that lane is structurally silent for any tool it does not
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -15,7 +15,7 @@ import { heldBy } from "./tool-name-readings.js";
|
|
|
15
15
|
import { boundInputHashOf } from "./canonical-json.js";
|
|
16
16
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "./untrusted-text.js";
|
|
17
17
|
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
18
|
-
import {
|
|
18
|
+
import { egressText, egressThrownText } from "./untrusted-egress.js";
|
|
19
19
|
import { assertNoRetiredToolPolicyKeys, RETIRED_DECISION_MEMBERS } from "./retired-keys.js";
|
|
20
20
|
import { isEngineRefusal, nestedRecordsOf, receiveContract, receiveNestedRecords, refusedOutOfContract, screenContractValues } from "./decision-receipt.js";
|
|
21
21
|
export { isEngineRefusal } from "./decision-receipt.js";
|
|
@@ -102,8 +102,25 @@ export function attest(d, call, members) {
|
|
|
102
102
|
export function withAttestation(d, record) {
|
|
103
103
|
return record === undefined ? d : { ...d, attestation: record };
|
|
104
104
|
}
|
|
105
|
+
const UNASKED_SEAT_SETTLEMENTS = new Set(["task_aborted", "no_approver"]);
|
|
106
|
+
function seatNeverAsked(settlement) {
|
|
107
|
+
return settlement !== undefined && UNASKED_SEAT_SETTLEMENTS.has(settlement.kind);
|
|
108
|
+
}
|
|
109
|
+
function unaskedSeatDeny(req, kind, message, origin) {
|
|
110
|
+
return attest({ action: "deny", message }, req, { settlement: settledByNobody(kind), origin });
|
|
111
|
+
}
|
|
112
|
+
export function noApproverSeatDeny(req, message, origin) {
|
|
113
|
+
return unaskedSeatDeny(req, "no_approver", message, origin);
|
|
114
|
+
}
|
|
105
115
|
export function abortedSeatDeny(req, message, origin) {
|
|
106
|
-
return
|
|
116
|
+
return unaskedSeatDeny(req, "task_aborted", message, origin);
|
|
117
|
+
}
|
|
118
|
+
export function unaskedSeatAnswer(req, signal, say, origin) {
|
|
119
|
+
if (signal?.aborted === true)
|
|
120
|
+
return abortedSeatDeny(req, say.aborted, origin);
|
|
121
|
+
if (req.approvalReach === "none")
|
|
122
|
+
return noApproverSeatDeny(req, say.unreachable, origin);
|
|
123
|
+
return undefined;
|
|
107
124
|
}
|
|
108
125
|
export const APPROVER_ATTRIBUTION_MAX_CHARS = 256;
|
|
109
126
|
const APPROVER_REFUSED_CHARS_RE = /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u;
|
|
@@ -442,8 +459,9 @@ export function createApprovalPolicy(opts) {
|
|
|
442
459
|
}
|
|
443
460
|
if (nameListHolds(need, req) || namespacedCoveringHit(needCovering, req)) {
|
|
444
461
|
const attestOver = (inputDigest) => (d, settlement) => ({ ...d, attestation: mintAttestation(req, { settlement, origin: "policy", inputDigest }) });
|
|
445
|
-
|
|
446
|
-
|
|
462
|
+
const unasked = unaskedSeatAnswer(req, signal, { aborted: `approval aborted for "${req.toolName}" (run or turn interrupted)`, unreachable: `approval for "${req.toolName}" was not requested: nobody can be asked about this call` }, "policy");
|
|
463
|
+
if (unasked !== undefined)
|
|
464
|
+
return unasked;
|
|
447
465
|
const shown = tryCloneArgs(req.args);
|
|
448
466
|
if (!shown.ok) {
|
|
449
467
|
return attestOver(boundInputHashOf(req.args))({ action: "deny", message: `approval for "${req.toolName}" could not present the args safely (unclonable value: ${shown.reason}) — denied fail-closed` }, settledByNobody("presentation_failed"));
|
|
@@ -565,7 +583,7 @@ export function combinePolicies(...policies) {
|
|
|
565
583
|
for (const p of policies) {
|
|
566
584
|
const d = refuseOutOfContractDecision(await p.check(current, signal));
|
|
567
585
|
if (d.action === "deny") {
|
|
568
|
-
if (attestationOf(d, current)?.settlement
|
|
586
|
+
if (!seatNeverAsked(attestationOf(d, current)?.settlement))
|
|
569
587
|
return settleDeny(d);
|
|
570
588
|
seatDeny ??= settleDeny(d);
|
|
571
589
|
continue;
|
|
@@ -854,10 +872,10 @@ export function tryCloneArgs(v) {
|
|
|
854
872
|
}
|
|
855
873
|
}
|
|
856
874
|
export function describeThrown(err) {
|
|
857
|
-
const contained = containThrownText(
|
|
875
|
+
const contained = containThrownText(egressThrownText("same-principal", err));
|
|
858
876
|
if (contained !== "")
|
|
859
877
|
return contained;
|
|
860
|
-
return containThrownText(describeRejectedValue(err));
|
|
878
|
+
return containThrownText(egressText("same-principal", describeRejectedValue(err)));
|
|
861
879
|
}
|
|
862
880
|
const THROWN_TEXT_MAX = 300;
|
|
863
881
|
function containThrownText(text) {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type { PermissionMode } from "./ask-origin.js";
|
|
|
18
18
|
export type { ToolEffect, ToolContentOrigin, ReversibilityVerdict, ReadRootGrantCandidate, ToolSpec, ToolReturn, NestedUsage, NestedUsageAccum, ToolExecuteContext } from "./tool-spec.js";
|
|
19
19
|
export type { BeforeWriteRequest, BeforeWriteResult, BeforeWriteHook, TrackEditRequest, TrackEditResult, TrackFileEditHook, FileEditedNotice, FileEditedHook, HandsBandOptions } from "./hands-band.js";
|
|
20
20
|
export type { AgentDefinition, SkillSpec } from "./agent-definition.js";
|
|
21
|
-
export type { McpServerSpec, A2aServerSpec, McpElicitRequest, McpElicitResponse, OnElicit } from "./mcp-server-spec.js";
|
|
21
|
+
export type { McpServerSpec, A2aServerSpec, McpElicitRequest, McpElicitResponse, OnElicit, McpRevocationLedger, McpMountIdentity } from "./mcp-server-spec.js";
|
|
22
22
|
export type { TaskLimits } from "./task-limits.js";
|
|
23
23
|
export type { TaskStatus, RemoteEnvFailureNote, EffectiveMemoryScopes, TaskResult, HumanReviewGateRow } from "./task-result.js";
|
|
24
24
|
export type { TerminalCause, PausedCause } from "./terminal-cause.js";
|
|
@@ -26,8 +26,8 @@ export type { TaskEventIdentity, BrainStatusPhase, BrainRetryErrClass, BrainStat
|
|
|
26
26
|
export type { TaskStream } from "./task-stream.js";
|
|
27
27
|
export type { WorkflowGovernanceBaseline, RuntimeCaps, AutoModeDenySource } from "./runtime-caps.js";
|
|
28
28
|
export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
|
|
29
|
-
export type { BackgroundChildEvent, DelegationLifecycleEvent } from "./delegation-frames.js";
|
|
30
|
-
export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
|
|
29
|
+
export type { BackgroundChildEvent, BackgroundChildRow, DelegationLifecycleEvent } from "./delegation-frames.js";
|
|
30
|
+
export { __resetMalformedDelegationSeatAnnouncement, backgroundChildTerminal, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
|
|
31
31
|
export type { ProjectMemoryLoad, ResumePreflightInfo, ResumePreflightVerdict, RunnerDeps } from "./runner-deps.js";
|
|
32
32
|
export type { EngineNotice, OwedUserInputAccount, UserInputUndrainedCause } from "./engine-notice.js";
|
|
33
33
|
export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, parentLegIds, settleOwedUserInput, undrainedUserInputNotices, USER_INPUT_UNDRAINED_CAUSES, USER_INPUT_UNDRAINED_FAMILIES } from "./engine-notice.js";
|
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
|
-
export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
|
|
3
|
+
export { __resetMalformedDelegationSeatAnnouncement, backgroundChildTerminal, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
|
|
4
4
|
export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, parentLegIds, settleOwedUserInput, undrainedUserInputNotices, USER_INPUT_UNDRAINED_CAUSES, USER_INPUT_UNDRAINED_FAMILIES } from "./engine-notice.js";
|
|
@@ -1,18 +1,67 @@
|
|
|
1
|
-
import { type RedactionReport } from "./
|
|
2
|
-
export type { RedactionFinding, RedactionReport, RedactionConfidence, RedactionSource, SummarizableFinding, } from "./
|
|
3
|
-
export { summarizeRedactions } from "./
|
|
4
|
-
export declare function redactSecrets(s: string, report?: RedactionReport): string;
|
|
5
|
-
export declare function redactHostLeaks(s: string, report?: RedactionReport): string;
|
|
6
|
-
/** Secret-redact + size-bound (NO injection fence) — the SAME-PRINCIPAL tier: workflow outputs/results/errors
|
|
7
|
-
* riding back to the launching model/script, run rows behind scope-gated observe. Paths/URLs survive
|
|
8
|
-
* (the assertion pattern depends on them); secrets never do. */
|
|
9
|
-
export declare function boundedRedactedSummary(value: unknown, max: number): string;
|
|
1
|
+
import { type RedactionReport } from "./redaction-passes.js";
|
|
2
|
+
export type { RedactionFinding, RedactionReport, RedactionConfidence, RedactionSource, SummarizableFinding, } from "./redaction-passes.js";
|
|
3
|
+
export { summarizeRedactions } from "./redaction-passes.js";
|
|
10
4
|
/**
|
|
11
|
-
*
|
|
5
|
+
* Every exit that redacts — into a model's context, a host's event or error lane, a log line, a person's screen — does it
|
|
6
|
+
* with ONE pass, its tier's, and nothing else redacts: a text through {@link egressText}, a thrown value's text through
|
|
7
|
+
* {@link egressThrownText}, a value with members (a thrown error, a marks bag) through {@link egressValue}, which runs the
|
|
8
|
+
* same pass over every string member. What comes out is an {@link Egress}: a string the type checker knows came out of
|
|
9
|
+
* that tier. What a tool RETURNS is its content, read by the model that asked for it, and is not redacted (CC redacts
|
|
10
|
+
* nothing there either); what is redacted is what the engine itself words or carries: errors, summaries, run rows.
|
|
11
|
+
*
|
|
12
|
+
* Two tiers, because the faces have two audiences (CC 2.1.212 alignment):
|
|
13
|
+
* - **`same-principal`** — the model, the script and the host that launched the work (tool results, workflow agent
|
|
14
|
+
* outputs / results / errors, run rows behind the scope-gated observe API, host error lanes): the credential
|
|
15
|
+
* vocabulary only. Paths and URLs in a worker's answer are the caller's OWN machine's paths — redacting them killed the
|
|
16
|
+
* cross-agent assertion pattern ("return the file you wrote" → `[redacted-path]`) while protecting no one;
|
|
17
|
+
* cross-tenant isolation on these faces is ACCESS CONTROL (scope gates), not string surgery. CC redacts nothing here;
|
|
18
|
+
* sema keeps the credential pass as a declared superset.
|
|
19
|
+
* - **`human`** — text shown to a person or exported beyond the launching principal: the credential vocabulary plus
|
|
20
|
+
* every URL and absolute path — host internals, and exfil-lure links in untrusted text.
|
|
21
|
+
* {@link untrustedEgressForHuman} additionally FENCES its text (design/97 CORE-2), so untrusted content cannot pose as
|
|
22
|
+
* instructions to its reader.
|
|
23
|
+
*/
|
|
24
|
+
export type EgressTier = "same-principal" | "human";
|
|
25
|
+
declare const EGRESS_TIER: unique symbol;
|
|
26
|
+
/** A string that came out of tier `T`'s pass. Only this module mints one. */
|
|
27
|
+
export type Egress<T extends EgressTier> = string & {
|
|
28
|
+
readonly [EGRESS_TIER]: T;
|
|
29
|
+
};
|
|
30
|
+
/** Tier `tier`'s one pass over a text. */
|
|
31
|
+
export declare function egressText<T extends EgressTier>(tier: T, text: string, report?: RedactionReport): Egress<T>;
|
|
32
|
+
/** The text a thrown value reads as (`thrownText`), through tier `tier`'s pass: the one form a thrown value's words take
|
|
33
|
+
* on their way out of the engine. */
|
|
34
|
+
export declare function egressThrownText<T extends EgressTier>(tier: T, err: unknown): Egress<T>;
|
|
35
|
+
/** The `same-principal` tier's pass, under the name hosts call it by (the engine calls {@link egressText}). */
|
|
36
|
+
export declare function redactSecrets(s: string, report?: RedactionReport): Egress<"same-principal">;
|
|
37
|
+
/** The `human` tier's pass, under the name hosts call it by (the engine calls {@link egressText}). */
|
|
38
|
+
export declare function redactHostLeaks(s: string, report?: RedactionReport): Egress<"human">;
|
|
39
|
+
/**
|
|
40
|
+
* Tier `tier`'s pass over every string member of `value`, returned as a DATA SNAPSHOT — never the value itself, so nothing
|
|
41
|
+
* that leaves can run the value's code again (a getter, a serializer, a proxy trap): a host's serializer, a log's inspector
|
|
42
|
+
* and the loop's own readers (`thrownText`, `thrownMarksOf`) reach what was read, once, and passed. The rules, one each:
|
|
43
|
+
* - a string goes through {@link egressText}, and so does every member NAME; a function is code, not data, and is not
|
|
44
|
+
* carried at any depth (the value handed in included: it comes back `undefined`); any other primitive is itself;
|
|
45
|
+
* - an error (any realm, any class) is re-minted as a plain `Error` — never on the thrown value's prototype, whose
|
|
46
|
+
* serializers and accessors would run again — carrying EVERY own member (`message`, `stack`, `cause`, `errors`,
|
|
47
|
+
* `details`, `code` …) and its `name`, `message`, `code` and `stack` read once through its chain, all as own data;
|
|
48
|
+
* - any other object is what JSON reads: its `toJSON(name)` when it has one (a `URL` is its `href`, a `Date` its ISO
|
|
49
|
+
* text; one that answers itself is read for its members), otherwise its own enumerable members;
|
|
50
|
+
* - a member whose name is a credential name (the vocabulary's record-key view: `password`, `Authorization`,
|
|
51
|
+
* `x-api-key`) and whose reading is a string is the marker whole;
|
|
52
|
+
* - cycles and shared members stay shared on the snapshot.
|
|
53
|
+
*/
|
|
54
|
+
export declare function egressValue<T extends EgressTier, V>(tier: T, value: V): V;
|
|
55
|
+
/** The `same-principal` tier's pass + size-bound (NO injection fence): workflow outputs/results/errors riding back to
|
|
56
|
+
* the launching model/script, run rows behind scope-gated observe. Paths/URLs survive (the assertion pattern depends on
|
|
57
|
+
* them); secrets never do. */
|
|
58
|
+
export declare function boundedRedactedSummary(value: unknown, max: number): Egress<"same-principal">;
|
|
59
|
+
/**
|
|
60
|
+
* The FULL untrusted-egress transform (design/97 CORE-2): the `human` tier's pass + size-bound + FENCE. Use
|
|
12
61
|
* for any worker prompt/output (#5 transcript) or steer content (#6) shown to a HUMAN or exported beyond the
|
|
13
62
|
* launching principal, so untrusted text can neither leak host internals nor pose as instructions.
|
|
14
63
|
*/
|
|
15
64
|
export declare function untrustedEgressForHuman(value: unknown, opts: {
|
|
16
65
|
label: string;
|
|
17
66
|
max: number;
|
|
18
|
-
}):
|
|
67
|
+
}): Egress<"human">;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { delimitUntrusted, neutralizeForFence } from "./untrusted-text.js";
|
|
2
|
-
import { egressPasses, runRedactionPasses } from "./
|
|
3
|
-
import { CREDENTIAL_VOCABULARY, egressPassGroups, URI_SCHEME_SOURCE } from "./credential-vocabulary.js";
|
|
2
|
+
import { egressPasses, runRedactionPasses } from "./redaction-passes.js";
|
|
3
|
+
import { CREDENTIAL_VOCABULARY, egressPassGroups, isSensitiveRecordKey, URI_SCHEME_SOURCE } from "./credential-vocabulary.js";
|
|
4
|
+
import { isErrorLike, readMember, thrownText } from "./thrown-value.js";
|
|
4
5
|
import { sliceHeadSafe } from "./surrogate-safe-slice.js";
|
|
5
|
-
export { summarizeRedactions } from "./
|
|
6
|
+
export { summarizeRedactions } from "./redaction-passes.js";
|
|
6
7
|
const URL_OF_ANY_PROTOCOL = new RegExp(String.raw `${URI_SCHEME_SOURCE}:\/\/[^\s"'\`]+`, "gi");
|
|
7
8
|
const BARE_USERNAME_MAX_LEN = 20;
|
|
8
9
|
function redactUserinfo(scheme, userinfoWithAt) {
|
|
@@ -18,16 +19,126 @@ function redactUserinfo(scheme, userinfoWithAt) {
|
|
|
18
19
|
const SECRET_TIER_PASSES = egressPasses(egressPassGroups(CREDENTIAL_VOCABULARY), {
|
|
19
20
|
"url-credentials": (_m, scheme, userinfo) => redactUserinfo(scheme, userinfo),
|
|
20
21
|
});
|
|
21
|
-
export function redactSecrets(s, report) {
|
|
22
|
-
return runRedactionPasses(s, SECRET_TIER_PASSES, report);
|
|
23
|
-
}
|
|
24
22
|
const HOST_LEAK_PASSES = [
|
|
25
23
|
...SECRET_TIER_PASSES,
|
|
26
24
|
{ kind: "url", confidence: "medium", marker: "[redacted-url]", source: "untrusted-egress", re: URL_OF_ANY_PROTOCOL, replace: "[redacted-url]" },
|
|
27
25
|
{ kind: "fs-path", confidence: "low", marker: "[redacted-path]", source: "untrusted-egress", re: /(?:\/[A-Za-z0-9_.-]+){2,}/g, replace: "[redacted-path]" },
|
|
28
26
|
];
|
|
27
|
+
const TIER_PASSES = { "same-principal": SECRET_TIER_PASSES, human: HOST_LEAK_PASSES };
|
|
28
|
+
export function egressText(tier, text, report) {
|
|
29
|
+
return runRedactionPasses(text, TIER_PASSES[tier], report);
|
|
30
|
+
}
|
|
31
|
+
export function egressThrownText(tier, err) {
|
|
32
|
+
return egressText(tier, thrownText(err));
|
|
33
|
+
}
|
|
34
|
+
export function redactSecrets(s, report) {
|
|
35
|
+
return egressText("same-principal", s, report);
|
|
36
|
+
}
|
|
29
37
|
export function redactHostLeaks(s, report) {
|
|
30
|
-
return
|
|
38
|
+
return egressText("human", s, report);
|
|
39
|
+
}
|
|
40
|
+
const VALUE_DEPTH_MAX = 32;
|
|
41
|
+
const VALUE_MEMBERS_MAX = 2_000;
|
|
42
|
+
const WHOLE = "[redacted]";
|
|
43
|
+
export function egressValue(tier, value) {
|
|
44
|
+
const snapshots = new Map();
|
|
45
|
+
let budget = VALUE_MEMBERS_MAX;
|
|
46
|
+
const ownKeys = (o, all) => {
|
|
47
|
+
try {
|
|
48
|
+
return all ? Reflect.ownKeys(o) : Object.keys(o);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const prototypeOf = (o) => {
|
|
55
|
+
try {
|
|
56
|
+
return Object.getPrototypeOf(o);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return Object.prototype;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const readOwn = (o, key) => {
|
|
63
|
+
try {
|
|
64
|
+
const d = Reflect.getOwnPropertyDescriptor(o, key);
|
|
65
|
+
if (d === undefined)
|
|
66
|
+
return { value: undefined, enumerable: true };
|
|
67
|
+
return { value: "value" in d ? d.value : d.get?.call(o), enumerable: d.enumerable ?? true };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { value: undefined, enumerable: true };
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const member = (read, depth, name) => {
|
|
74
|
+
const out = pass(read, depth + 1, name);
|
|
75
|
+
return typeof out === "string" && isSensitiveRecordKey(name) ? WHOLE : out;
|
|
76
|
+
};
|
|
77
|
+
const fill = (o, depth, keys, snapshot) => {
|
|
78
|
+
snapshots.set(o, snapshot);
|
|
79
|
+
for (const key of keys ?? []) {
|
|
80
|
+
if (budget-- <= 0)
|
|
81
|
+
break;
|
|
82
|
+
const read = readOwn(o, key);
|
|
83
|
+
const name = typeof key === "string" ? egressText(tier, key) : key;
|
|
84
|
+
Object.defineProperty(snapshot, name, { value: member(read.value, depth, typeof key === "string" ? key : ""), enumerable: read.enumerable, writable: true, configurable: true });
|
|
85
|
+
}
|
|
86
|
+
return snapshot;
|
|
87
|
+
};
|
|
88
|
+
const errorSnapshot = (o, depth) => {
|
|
89
|
+
const snapshot = new Error();
|
|
90
|
+
for (const key of Reflect.ownKeys(snapshot))
|
|
91
|
+
Reflect.deleteProperty(snapshot, key);
|
|
92
|
+
fill(o, depth, ownKeys(o, true), snapshot);
|
|
93
|
+
for (const key of ["name", "message", "code", "stack"]) {
|
|
94
|
+
if (Object.prototype.hasOwnProperty.call(snapshot, key))
|
|
95
|
+
continue;
|
|
96
|
+
const read = readMember(o, key);
|
|
97
|
+
if (read !== undefined && typeof read !== "function")
|
|
98
|
+
Object.defineProperty(snapshot, key, { value: member(read, depth, key), enumerable: false, writable: true, configurable: true });
|
|
99
|
+
}
|
|
100
|
+
return snapshot;
|
|
101
|
+
};
|
|
102
|
+
function pass(v, depth, key) {
|
|
103
|
+
if (typeof v === "string")
|
|
104
|
+
return egressText(tier, v);
|
|
105
|
+
if (typeof v === "function")
|
|
106
|
+
return undefined;
|
|
107
|
+
if (typeof v !== "object" || v === null)
|
|
108
|
+
return v;
|
|
109
|
+
const known = snapshots.get(v);
|
|
110
|
+
if (known !== undefined)
|
|
111
|
+
return known;
|
|
112
|
+
if (depth >= VALUE_DEPTH_MAX)
|
|
113
|
+
return WHOLE;
|
|
114
|
+
if (isErrorLike(v))
|
|
115
|
+
return errorSnapshot(v, depth);
|
|
116
|
+
const toJSON = readMember(v, "toJSON");
|
|
117
|
+
if (typeof toJSON === "function") {
|
|
118
|
+
let reading;
|
|
119
|
+
try {
|
|
120
|
+
reading = toJSON.call(v, key);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return WHOLE;
|
|
124
|
+
}
|
|
125
|
+
if (reading !== v) {
|
|
126
|
+
const out = pass(reading, depth + 1, key);
|
|
127
|
+
snapshots.set(v, out);
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const array = (() => {
|
|
132
|
+
try {
|
|
133
|
+
return Array.isArray(v);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
})();
|
|
139
|
+
return fill(v, depth, ownKeys(v, false), array ? [] : prototypeOf(v) === null ? Object.create(null) : {});
|
|
140
|
+
}
|
|
141
|
+
return pass(value, 0, "");
|
|
31
142
|
}
|
|
32
143
|
const REDACTION_MARKER_RE = /\[redacted(?:-[a-z]+)?\]/g;
|
|
33
144
|
function countRedactionMarkers(s) {
|
|
@@ -85,10 +196,10 @@ function boundedString(value, max, redact) {
|
|
|
85
196
|
return out;
|
|
86
197
|
}
|
|
87
198
|
export function boundedRedactedSummary(value, max) {
|
|
88
|
-
return boundedString(value, max,
|
|
199
|
+
return boundedString(value, max, (s) => egressText("same-principal", s));
|
|
89
200
|
}
|
|
90
201
|
export function untrustedEgressForHuman(value, opts) {
|
|
91
|
-
const source = boundedString(value, Number.POSITIVE_INFINITY,
|
|
202
|
+
const source = boundedString(value, Number.POSITIVE_INFINITY, (s) => egressText("human", s));
|
|
92
203
|
const probe = (budget) => {
|
|
93
204
|
const bounded = boundedString(source, budget, (s) => s);
|
|
94
205
|
return { bounded, overflow: [...neutralizeForFence(bounded)].length - opts.max };
|
|
@@ -378,6 +378,22 @@ export const ENGINE_ENVELOPES = Object.freeze([
|
|
|
378
378
|
fenced: false,
|
|
379
379
|
disclosed: false,
|
|
380
380
|
},
|
|
381
|
+
...[
|
|
382
|
+
"additional_skills_reminder", "application_details", "artifacts", "ask_user_question_tool", "citation_requirements",
|
|
383
|
+
"claude_behavior", "computer_use", "env", "evenhandedness", "examples", "file_creation_advice", "file_handling_rules",
|
|
384
|
+
"good_file_sharing_examples", "high_level_computer_use_explanation", "knowledge_cutoff", "legal_and_financial_advice",
|
|
385
|
+
"lists_and_bullets", "notes_on_user_uploaded_files", "package_management", "producing_outputs", "product_information",
|
|
386
|
+
"refusal_handling", "responding_to_mistakes_and_criticism", "sharing_files", "strong", "suggesting_claude_actions",
|
|
387
|
+
"todo_list_tool", "tone_and_formatting", "tool_result_safety", "unnecessary_computer_use_avoidance", "user_wellbeing",
|
|
388
|
+
"verification_step", "web_content_restrictions", "working_with_user_files",
|
|
389
|
+
].map((tag) => ({
|
|
390
|
+
tag,
|
|
391
|
+
kind: "not-an-envelope",
|
|
392
|
+
mint: "prompts/cowork.ts — the Cowork base template's own section markup",
|
|
393
|
+
guard: "no run-supplied bytes reach it: the template is a constant rendered section by section, and its only fills are the two directory placeholders, engine facts",
|
|
394
|
+
fenced: false,
|
|
395
|
+
disclosed: false,
|
|
396
|
+
})),
|
|
381
397
|
]);
|
|
382
398
|
const ENVELOPES_BY_TAG = new Map(ENGINE_ENVELOPES.map((e) => [e.tag, e]));
|
|
383
399
|
export function engineEnvelope(tag) {
|
|
@@ -646,16 +646,30 @@ export declare function deriveAskEffective(form: AskSeamForm, parkEffective: boo
|
|
|
646
646
|
*/
|
|
647
647
|
export declare function askSeatEffective(manifest: Pick<WiringManifest, "ask" | "parkLane">): AskEffective;
|
|
648
648
|
/**
|
|
649
|
-
*
|
|
650
|
-
*
|
|
651
|
-
* `
|
|
652
|
-
*
|
|
653
|
-
*
|
|
654
|
-
* carrying the bit), or refused.
|
|
649
|
+
* WHERE an ask carrying `requiresRealApproval` goes from a station — the closed set of the ONE approval-seat decision:
|
|
650
|
+
* `live_approver` (a live approver callback answers it now), `durable_park` (it parks for a person to answer later),
|
|
651
|
+
* `none` (nobody can: a blanket policy seat, no seat, or a run that declared nobody can be asked — each only when no park
|
|
652
|
+
* lane is armed, since an armed park is the deployment's own statement that an answer can wait for a person — or a word that cannot be
|
|
653
|
+
* resolved — the fail-closed reading).
|
|
655
654
|
*/
|
|
656
|
-
export declare
|
|
655
|
+
export declare const APPROVAL_ROUTES: readonly ["live_approver", "durable_park", "none"];
|
|
656
|
+
export type ApprovalRoute = (typeof APPROVAL_ROUTES)[number];
|
|
657
|
+
/**
|
|
658
|
+
* The APPROVAL SEAT as a route, read off the ONE ask derivation above, never re-derived: `human_reachable` ⇒
|
|
659
|
+
* `live_approver`; `park_only` ⇒ `durable_park` (where the park cannot take the ask, a blanket seat behind it refuses the
|
|
660
|
+
* bit); `auto_allow`, `auto_deny` and `unresolved` ⇒ `none`. Every station that forks on "does a handed-on ask reach a
|
|
661
|
+
* person" reads this word — the leg's gate from the leg's seat word ({@link askSeatEffective}), an ancestor's frozen seat
|
|
662
|
+
* through {@link frozenApproverRoute} — and the fork, its observer and the sentence a person reads all read the same word.
|
|
663
|
+
*/
|
|
664
|
+
export declare function approvalRouteOf(ask: {
|
|
657
665
|
readonly effective?: AskEffective;
|
|
658
|
-
}):
|
|
666
|
+
}): ApprovalRoute;
|
|
667
|
+
/**
|
|
668
|
+
* The route from an ancestor's FROZEN approver (the inherited fold's station): the same table read with that seat's own
|
|
669
|
+
* form and no park — the inherited station resolves at the frozen seat, whose only road to a person is a live callback
|
|
670
|
+
* (the leg's park is reached only through a live approver's `unavailable`), so a blanket or absent frozen seat is `none`.
|
|
671
|
+
*/
|
|
672
|
+
export declare function frozenApproverRoute(onAsk: OnAsk | undefined): ApprovalRoute;
|
|
659
673
|
export declare function deriveWiringManifest(facts: WiringFacts): WiringManifest;
|
|
660
674
|
/** A run's approval seat as its one resolution froze it: the value every reader that needs the approver itself reads
|
|
661
675
|
* (`undefined` when nothing on this run can be asked), the form the manifest reports and every reader of the word derives
|
|
@@ -81,16 +81,18 @@ export function deriveAskEffective(form, parkEffective, mode) {
|
|
|
81
81
|
export function askSeatEffective(manifest) {
|
|
82
82
|
return deriveAskEffective(manifest.ask.form, manifest.parkLane.effective);
|
|
83
83
|
}
|
|
84
|
-
export
|
|
84
|
+
export const APPROVAL_ROUTES = Object.freeze(["live_approver", "durable_park", "none"]);
|
|
85
|
+
export function approvalRouteOf(ask) {
|
|
85
86
|
const effective = ask.effective ?? "unresolved";
|
|
86
87
|
switch (effective) {
|
|
87
88
|
case "human_reachable":
|
|
89
|
+
return "live_approver";
|
|
88
90
|
case "park_only":
|
|
89
|
-
return
|
|
91
|
+
return "durable_park";
|
|
90
92
|
case "auto_allow":
|
|
91
93
|
case "auto_deny":
|
|
92
94
|
case "unresolved":
|
|
93
|
-
return
|
|
95
|
+
return "none";
|
|
94
96
|
default: {
|
|
95
97
|
const _exhaustive = effective;
|
|
96
98
|
void _exhaustive;
|
|
@@ -98,6 +100,12 @@ export function askReachesPerson(ask) {
|
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
}
|
|
103
|
+
export function frozenApproverRoute(onAsk) {
|
|
104
|
+
return approvalRouteOf({ effective: deriveAskEffective(askSeamFormOf(onAsk) ?? "absent", false) });
|
|
105
|
+
}
|
|
106
|
+
function askSeamFormOf(v) {
|
|
107
|
+
return typeof v === "function" ? "callback" : v === "allow" ? "allow" : v === "deny" ? "deny" : v === undefined ? "absent" : undefined;
|
|
108
|
+
}
|
|
101
109
|
function deriveParkLane(facts) {
|
|
102
110
|
if (!facts.parkCapable) {
|
|
103
111
|
return { capable: false, effective: false, reasons: ["no_checkpoint_store"] };
|
|
@@ -235,7 +243,7 @@ export function resolveAskSeat(spec, deps) {
|
|
|
235
243
|
if (seat === undefined)
|
|
236
244
|
return Object.freeze(reach === undefined ? { value: undefined, form: "absent" } : { value: undefined, form: "declared_none", reach });
|
|
237
245
|
const v = seat.value;
|
|
238
|
-
const form =
|
|
246
|
+
const form = askSeamFormOf(v);
|
|
239
247
|
if (form === undefined) {
|
|
240
248
|
const e = new Error(`the resolved onAsk seat (${seat.provenance}) holds ${JSON.stringify(v)} — not an OnAsk ` +
|
|
241
249
|
`("allow" | "deny" | approver function); refusing to classify it.`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { inheritedHostEnv } from "../../core/secret-env.js";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
const DEFAULT_GRACE_MS = 3000;
|
|
@@ -145,7 +145,7 @@ function enumerateDescendantsSync(rootPid) {
|
|
|
145
145
|
let out;
|
|
146
146
|
try {
|
|
147
147
|
const result = spawnSync("ps", ["-A", "-o", "pid=", "-o", "ppid=", "-o", "etime="], {
|
|
148
|
-
env:
|
|
148
|
+
env: inheritedHostEnv("scrub"),
|
|
149
149
|
timeout: PS_ENUMERATION_TIMEOUT_MS,
|
|
150
150
|
encoding: "utf8",
|
|
151
151
|
windowsHide: true,
|
|
@@ -211,7 +211,7 @@ function parseEtimeSeconds(etime) {
|
|
|
211
211
|
function runTaskkill(args) {
|
|
212
212
|
try {
|
|
213
213
|
const child = spawn("taskkill", args, {
|
|
214
|
-
env:
|
|
214
|
+
env: inheritedHostEnv("scrub"),
|
|
215
215
|
stdio: "ignore",
|
|
216
216
|
detached: true,
|
|
217
217
|
windowsHide: true,
|
|
@@ -271,7 +271,7 @@ function tryMsysGroupKill(bashPath, pid) {
|
|
|
271
271
|
`[ -n "$g" ] && [ "$g" != "0" ] && kill -9 -"$g" 2>/dev/null; ` +
|
|
272
272
|
`done; exit 0`;
|
|
273
273
|
try {
|
|
274
|
-
const child = spawn(bashPath, ["-c", script], { env:
|
|
274
|
+
const child = spawn(bashPath, ["-c", script], { env: inheritedHostEnv("scrub"), stdio: "ignore", detached: true, windowsHide: true });
|
|
275
275
|
child.on("error", () => { });
|
|
276
276
|
return true;
|
|
277
277
|
}
|