@sema-agent/core 7.49.5 → 7.50.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 +157 -0
- package/dist/agents/observer.js +3 -10
- package/dist/agents/subagent-steps.d.ts +6 -14
- package/dist/agents/subagent-steps.js +3 -17
- package/dist/brain/stream-shared.d.ts +1 -1
- package/dist/core/allow-entry.d.ts +1 -1
- package/dist/core/allow-entry.js +1 -1
- package/dist/core/ask-origin.d.ts +15 -11
- package/dist/core/ask-origin.js +6 -14
- package/dist/core/auto-mode-prompt.js +6 -2
- package/dist/core/checkpoint-store.d.ts +6 -4
- package/dist/core/checkpoint-store.js +1 -1
- package/dist/core/context-edit.d.ts +2 -2
- package/dist/core/decision-receipt.d.ts +2 -2
- package/dist/core/decision-receipt.js +30 -11
- package/dist/core/delegation-frames.d.ts +66 -18
- package/dist/core/delegation-frames.js +22 -0
- package/dist/core/engine-notice.d.ts +7 -5
- package/dist/core/frame-settlement.d.ts +6 -1
- package/dist/core/gate-fold.js +34 -19
- package/dist/core/gate-lanes.js +15 -13
- package/dist/core/gate-outcome.d.ts +2 -2
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +28 -20
- package/dist/core/human-input-projection.js +2 -0
- package/dist/core/lsp-diagnostics.d.ts +3 -0
- package/dist/core/lsp-diagnostics.js +6 -0
- package/dist/core/lsp-session.d.ts +39 -17
- package/dist/core/lsp-session.js +118 -58
- package/dist/core/lsp.d.ts +83 -3
- package/dist/core/lsp.js +146 -16
- package/dist/core/mcp.js +2 -1
- package/dist/core/media-byte-cap.d.ts +14 -0
- package/dist/core/media-byte-cap.js +15 -3
- package/dist/core/memory-engine/consolidation-driver.d.ts +23 -1
- package/dist/core/memory-engine/consolidation-driver.js +54 -6
- package/dist/core/memory-engine/consolidation-write-gate.d.ts +46 -0
- package/dist/core/memory-engine/consolidation-write-gate.js +82 -0
- package/dist/core/memory-engine/consolidation.d.ts +17 -0
- package/dist/core/memory-engine/consolidation.js +1 -0
- package/dist/core/memory-engine/distiller.d.ts +8 -2
- package/dist/core/memory-engine/distiller.js +8 -6
- package/dist/core/memory-engine/engine.d.ts +37 -4
- package/dist/core/memory-engine/engine.js +127 -38
- package/dist/core/memory-engine/file-backend.d.ts +8 -3
- package/dist/core/memory-engine/file-backend.js +44 -16
- package/dist/core/memory-engine/index.d.ts +2 -2
- package/dist/core/memory-engine/index.js +1 -1
- package/dist/core/memory-engine/landing-admission.d.ts +65 -0
- package/dist/core/memory-engine/landing-admission.js +68 -0
- package/dist/core/memory-engine/types.d.ts +23 -0
- package/dist/core/mode-policy.d.ts +3 -2
- package/dist/core/mode-policy.js +2 -0
- package/dist/core/permission-rule-model.d.ts +19 -13
- package/dist/core/permission-rule-model.js +7 -4
- package/dist/core/permission-rule-provider.d.ts +11 -0
- package/dist/core/permission-rules.d.ts +1 -1
- package/dist/core/permission-rules.js +5 -2
- package/dist/core/persisted-rule-arms.d.ts +15 -7
- package/dist/core/persisted-rule-arms.js +13 -6
- package/dist/core/reminder-mint.d.ts +5 -0
- package/dist/core/reminder-mint.js +1 -0
- package/dist/core/retired-keys.d.ts +8 -0
- package/dist/core/retired-keys.js +4 -0
- package/dist/core/rule-lane-reads.d.ts +4 -4
- package/dist/core/rule-lane-reads.js +10 -1
- package/dist/core/runner/construction-doors.d.ts +10 -4
- package/dist/core/runner/construction-doors.js +25 -2
- package/dist/core/runner/contracts.d.ts +20 -3
- package/dist/core/runner/env-capability-census.js +1 -1
- package/dist/core/runner/gate-exit.js +2 -1
- package/dist/core/runner/memory-consolidation-driver.d.ts +6 -4
- package/dist/core/runner/memory-consolidation-driver.js +5 -2
- package/dist/core/runner/memory-consolidation-write-judge.d.ts +17 -0
- package/dist/core/runner/memory-consolidation-write-judge.js +14 -0
- package/dist/core/runner/permission-rule-lanes.d.ts +16 -22
- package/dist/core/runner/permission-rule-lanes.js +13 -43
- package/dist/core/runner/person-path-rows.d.ts +244 -0
- package/dist/core/runner/person-path-rows.js +387 -0
- package/dist/core/runner/policy-throw.d.ts +34 -0
- package/dist/core/runner/policy-throw.js +25 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +8 -1
- package/dist/core/runner/prepare-artifact.d.ts +5 -3
- package/dist/core/runner/prepare-artifact.js +2 -8
- package/dist/core/runner/prepare-ask-lane.d.ts +2 -1
- package/dist/core/runner/prepare-ask-lane.js +14 -16
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
- package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
- package/dist/core/runner/prepare-context-lane.js +2 -2
- package/dist/core/runner/prepare-gate-stations.d.ts +3 -3
- package/dist/core/runner/prepare-gate-stations.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +19 -10
- package/dist/core/runner/prepare-hands-readface.js +57 -43
- package/dist/core/runner/prepare-lsp.d.ts +15 -13
- package/dist/core/runner/prepare-lsp.js +11 -11
- package/dist/core/runner/prepare-policy-chain.d.ts +8 -4
- package/dist/core/runner/prepare-policy-chain.js +11 -10
- package/dist/core/runner/prepare-run-refs.js +1 -1
- package/dist/core/runner/prepare-task.js +24 -16
- package/dist/core/runner/prepare-turn-wiring.d.ts +13 -6
- package/dist/core/runner/prepare-turn-wiring.js +19 -8
- package/dist/core/runner/read-root-grant.d.ts +1 -25
- package/dist/core/runner/read-root-grant.js +1 -17
- package/dist/core/runner/retired-runner-deps.d.ts +2 -2
- package/dist/core/runner/run-brain-sinks.d.ts +1 -5
- package/dist/core/runner/run-brain-sinks.js +2 -4
- package/dist/core/runner/run-harness-handlers.js +5 -4
- package/dist/core/runner/run-identity-wiring.d.ts +4 -4
- package/dist/core/runner/run-identity-wiring.js +10 -1
- package/dist/core/runner/run-leg.d.ts +1 -1
- package/dist/core/runner/run-leg.js +22 -9
- package/dist/core/runner/run-settle-and-teardown.d.ts +0 -4
- package/dist/core/runner/run-settle-and-teardown.js +7 -4
- package/dist/core/runner/run-terminal-adoption.d.ts +1 -1
- package/dist/core/runner/run-terminal-adoption.js +1 -1
- package/dist/core/runner/run-turn-boundary.js +2 -2
- package/dist/core/runner/runtask.js +8 -8
- package/dist/core/runner/session-file-state-replay.d.ts +14 -4
- package/dist/core/runner/session-file-state-replay.js +8 -0
- package/dist/core/runner/stream-steer-verb.js +3 -0
- package/dist/core/runner/tool-end-body.d.ts +5 -4
- package/dist/core/runner/tool-end-body.js +4 -4
- package/dist/core/runner/tool-output-projection.d.ts +6 -4
- package/dist/core/runner/tool-output-projection.js +66 -47
- package/dist/core/runner-deps.d.ts +6 -3
- package/dist/core/shared-memory/types.d.ts +2 -2
- package/dist/core/shell-read-silence.d.ts +23 -0
- package/dist/core/shell-read-silence.js +18 -0
- package/dist/core/synthetic-read-verdict.d.ts +80 -21
- package/dist/core/synthetic-read-verdict.js +89 -18
- package/dist/core/task-event.d.ts +113 -25
- package/dist/core/task-spec.d.ts +44 -0
- package/dist/core/task-tool-shape.d.ts +1 -1
- package/dist/core/tool-catalog-entries.js +16 -2
- package/dist/core/tool-policy.d.ts +25 -15
- package/dist/core/tool-policy.js +9 -13
- package/dist/core/trace.d.ts +3 -2
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +2 -2
- package/dist/core/user-attachment.d.ts +164 -0
- package/dist/core/user-attachment.js +437 -0
- package/dist/engine/compaction/compaction.js +10 -3
- package/dist/engine/compaction/utils.js +6 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +19 -1
- package/dist/engine/execution-env/node-execution-env.js +125 -0
- package/dist/engine/harness/agent-harness.d.ts +12 -2
- package/dist/engine/harness/agent-harness.js +15 -3
- package/dist/engine/harness/types.d.ts +21 -0
- package/dist/engine/llm/content-block-render.d.ts +15 -0
- package/dist/engine/llm/content-block-render.js +10 -0
- package/dist/engine/llm/index.d.ts +1 -0
- package/dist/engine/llm/index.js +1 -0
- package/dist/engine/llm/types.d.ts +80 -4
- package/dist/engine/llm/types.js +43 -1
- package/dist/engine/session/import-validate.js +2 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +2 -1
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/workflow.d.ts +4 -5
- package/dist/orchestration/workflow.js +2 -2
- package/dist/prompts/default.d.ts +1 -1
- package/dist/tools/artifact/artifact-tool.js +1 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
- package/dist/tools/fs/dir-entries.d.ts +4 -10
- package/dist/tools/fs/dir-entries.js +4 -11
- package/dist/tools/fs/file-read-check.js +3 -3
- package/dist/tools/fs/fs-pdf.js +2 -4
- package/dist/tools/fs/fs-read.d.ts +47 -4
- package/dist/tools/fs/fs-read.js +63 -19
- package/dist/tools/fs/fs-search-tools.d.ts +3 -3
- package/dist/tools/fs/fs-search-tools.js +7 -2
- package/dist/tools/fs/fs-write.d.ts +1 -1
- package/dist/tools/fs/fs-write.js +7 -6
- package/dist/tools/fs/index.d.ts +7 -1
- package/dist/tools/fs/index.js +56 -34
- package/dist/tools/fs/read-deny-home.d.ts +19 -1
- package/dist/tools/fs/read-deny-home.js +32 -22
- package/dist/tools/fs/read-deny.d.ts +14 -0
- package/dist/tools/fs/read-deny.js +8 -0
- package/dist/tools/fs/repo-map.d.ts +3 -2
- package/dist/tools/fs/repo-map.js +11 -3
- package/dist/tools/fs/safety.d.ts +41 -2
- package/dist/tools/fs/safety.js +12 -3
- package/dist/tools/fs/shell-read-check.d.ts +12 -3
- package/dist/tools/fs/shell-read-check.js +44 -24
- package/dist/tools/fs/shell-read-device.d.ts +1 -1
- package/dist/tools/fs/shell-read-face.d.ts +15 -0
- package/dist/tools/fs/shell-read-face.js +5 -0
- package/dist/tools/fs/shell-read-judge.d.ts +9 -4
- package/dist/tools/fs/shell-read-judge.js +1 -1
- package/dist/tools/loop-tick.d.ts +2 -2
- package/package.json +3 -1
- package/test/export-surface.snapshot.json +39 -1
- package/wire-changes/core@7.50.0.json +1699 -0
- package/wire-changes/core@7.50.1.json +65 -0
- package/dist/core/runner/persisted-path-rows.d.ts +0 -88
- package/dist/core/runner/persisted-path-rows.js +0 -108
|
@@ -52,9 +52,9 @@ import type { ContainmentFace } from "../tools/fs/containment-face.js";
|
|
|
52
52
|
* their standing deny/ask rows (the tightening lexer's `unreadable` word: an expansion where a rule
|
|
53
53
|
* reads a word, an unterminated quote, a syntax error — the answer's `reason`): whether one of those
|
|
54
54
|
* rows forbids this call is unknown, so an allow tightens to an ask a person must clear and no automatic
|
|
55
|
-
* lane may answer. A store that could not be READ is
|
|
56
|
-
*
|
|
57
|
-
* `permission.rule_store_unavailable` notice
|
|
55
|
+
* lane may answer. A store (or a partition of it) that could not be READ is this word too — a row inside it
|
|
56
|
+
* may forbid the call and nobody can tell — with the class `governance_unavailable` instead of the releasable
|
|
57
|
+
* `rule_unreadable`, both bypass-immune; the run also carries one `permission.rule_store_unavailable` notice.
|
|
58
58
|
* - `hook` — a deployment's PreToolUse screening face raised or co-raised the ask; the word is
|
|
59
59
|
* engine-stamped at the fold and is the deployment's own per-call adjudication.
|
|
60
60
|
* - `ask_rule` — an EXPLICIT `ask` permission rule matched: a person's standing "ask me each time",
|
|
@@ -103,9 +103,14 @@ export interface AskOriginFacts {
|
|
|
103
103
|
readonly org: "rule" | "unavailable" | undefined;
|
|
104
104
|
/** The gate's own record of its persisted-rule lane's READ: the store was read, but THIS CALL could not be read
|
|
105
105
|
* against the person's deny/ask rows (the lane's answer carried the lexer's reason) — the origin
|
|
106
|
-
* `rule_store_unavailable
|
|
107
|
-
*
|
|
106
|
+
* `rule_store_unavailable`, the class `rule_unreadable` (bypass-immune, like the store form's). Absent: the lane read the call, or the store
|
|
107
|
+
* could not be read (see {@link ruleStoreUnreadable}). Written only by the gate. */
|
|
108
108
|
readonly ruleCallUnreadable?: boolean;
|
|
109
|
+
/** The gate's own record of the same read when a PARTITION of the store (or all of it) could not be read: a deny / ask
|
|
110
|
+
* row inside it may forbid this call and nobody can tell — the same origin `rule_store_unavailable`, the
|
|
111
|
+
* bypass-immune class `governance_unavailable` (a source of governance that cannot be read: only a person answers).
|
|
112
|
+
* Exclusive with {@link ruleCallUnreadable}. Written only by the gate. */
|
|
113
|
+
readonly ruleStoreUnreadable?: boolean;
|
|
109
114
|
/** Which of the gate's OWN post-fold stations minted the standing ask, if one did — again the gate's
|
|
110
115
|
* record of its own layer (a station replaces an `allow` wholesale, so at most one fires per pass),
|
|
111
116
|
* never a read-back of `decisionReason: "safety"` off a decision a policy could have composed. `unstated` is
|
|
@@ -569,7 +574,7 @@ export declare const MODE_ARBITRATION: {
|
|
|
569
574
|
readonly hookAskFloor: false;
|
|
570
575
|
};
|
|
571
576
|
readonly rule_unreadable: {
|
|
572
|
-
readonly bypassImmune:
|
|
577
|
+
readonly bypassImmune: true;
|
|
573
578
|
readonly classifierEligible: false;
|
|
574
579
|
readonly sandboxAdmission: false;
|
|
575
580
|
readonly ruleMayClear: false;
|
|
@@ -713,14 +718,13 @@ export declare const RRA_EXPLAINERS: ReadonlySet<AskClass>;
|
|
|
713
718
|
export declare function releasableAskClassesFor(face: AskFace): readonly AskClass[];
|
|
714
719
|
/**
|
|
715
720
|
* The closed set of `reason` words a mode release carries (`GateDisposition.released.reason`, the `released` arm of
|
|
716
|
-
* {@link Arbitration}): each is a releasable class the release record names on its own — `
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
* set holds, and none when it holds no member. {@link arbitrateByMode} and {@link isModeReleaseReason} read one copy
|
|
721
|
+
* {@link Arbitration}): each is a releasable class the release record names on its own — `target_unresolved`, a release
|
|
722
|
+
* (the shell face only, {@link RELEASED_ON_SHELL_FACE}) over a write target whose reading could not say where it leads. A
|
|
723
|
+
* release carries the first member (array order) its class set holds, and none when it holds no member. {@link arbitrateByMode} and {@link isModeReleaseReason} read one copy
|
|
720
724
|
* of this list taken at load (a caller that writes into the exported array changes neither), so a new reason is one
|
|
721
725
|
* word here.
|
|
722
726
|
*/
|
|
723
|
-
export declare const MODE_RELEASE_REASONS: readonly ["
|
|
727
|
+
export declare const MODE_RELEASE_REASONS: readonly ["target_unresolved"];
|
|
724
728
|
export type ModeReleaseReason = (typeof MODE_RELEASE_REASONS)[number];
|
|
725
729
|
/** Membership test for {@link ModeReleaseReason} — the word crosses the wire on `tool_end.gate.disposition.released.reason`. */
|
|
726
730
|
export declare function isModeReleaseReason(v: unknown): v is ModeReleaseReason;
|
package/dist/core/ask-origin.js
CHANGED
|
@@ -77,7 +77,7 @@ function originLadder(decision, facts) {
|
|
|
77
77
|
return "org_unavailable";
|
|
78
78
|
if (facts.org === "rule")
|
|
79
79
|
return "org_rule";
|
|
80
|
-
if (facts.ruleCallUnreadable === true)
|
|
80
|
+
if (facts.ruleCallUnreadable === true || facts.ruleStoreUnreadable === true)
|
|
81
81
|
return "rule_store_unavailable";
|
|
82
82
|
if (decision.decisionReason === "hook")
|
|
83
83
|
return "hook";
|
|
@@ -210,7 +210,7 @@ export const MODE_ARBITRATION = {
|
|
|
210
210
|
transcript_write: { bypassImmune: false, classifierEligible: true, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true },
|
|
211
211
|
org_rule: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
212
212
|
governance_unavailable: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
213
|
-
rule_unreadable: { bypassImmune:
|
|
213
|
+
rule_unreadable: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
214
214
|
ask_rule: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true },
|
|
215
215
|
denial_limit_fallback: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
216
216
|
classifier_no_ruling: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
@@ -247,7 +247,7 @@ export function releasableAskClassesFor(face) {
|
|
|
247
247
|
throw new TypeError(`releasableAskClassesFor: "${String(face)}" is not an ask face (${ASK_FACES.join(", ")})`);
|
|
248
248
|
return RELEASABLE_BY_FACE[face];
|
|
249
249
|
}
|
|
250
|
-
export const MODE_RELEASE_REASONS = Object.freeze(["
|
|
250
|
+
export const MODE_RELEASE_REASONS = Object.freeze(["target_unresolved"]);
|
|
251
251
|
const MODE_RELEASE_REASON_SET = new Set(MODE_RELEASE_REASONS);
|
|
252
252
|
export function isModeReleaseReason(v) {
|
|
253
253
|
return MODE_RELEASE_REASON_SET.has(v);
|
|
@@ -266,16 +266,8 @@ export function screenAskClassMembers(askClass, classes) {
|
|
|
266
266
|
}
|
|
267
267
|
export function decisionAskClassesOf(d) {
|
|
268
268
|
const out = new Set();
|
|
269
|
-
|
|
270
|
-
out.add(
|
|
271
|
-
if (d.guard === "unverifiable_delete")
|
|
272
|
-
out.add("unverifiable_delete");
|
|
273
|
-
if (d.guard === "unverifiable_shell_text")
|
|
274
|
-
out.add("unverifiable_shell_text");
|
|
275
|
-
if (d.guard === "transcript_write")
|
|
276
|
-
out.add("transcript_write");
|
|
277
|
-
if (d.guard === "artifact_shared_read")
|
|
278
|
-
out.add("artifact_shared_read");
|
|
269
|
+
for (const guard of d.guards ?? [])
|
|
270
|
+
out.add(guard);
|
|
279
271
|
if (d.ruleUnreadable !== undefined)
|
|
280
272
|
out.add("rule_unreadable");
|
|
281
273
|
if (d.matchedAskRule !== undefined)
|
|
@@ -324,7 +316,7 @@ export function classifyAsk(d, facts) {
|
|
|
324
316
|
classes.add("ancestor");
|
|
325
317
|
if (facts.org === "rule")
|
|
326
318
|
classes.add("org_rule");
|
|
327
|
-
if (facts.org === "unavailable")
|
|
319
|
+
if (facts.org === "unavailable" || facts.ruleStoreUnreadable === true)
|
|
328
320
|
classes.add("governance_unavailable");
|
|
329
321
|
if (facts.ruleCallUnreadable === true)
|
|
330
322
|
classes.add("rule_unreadable");
|
|
@@ -2,6 +2,7 @@ import { AUTO_MODE_DEFAULT_WINDOW_MAX_CHARS, AUTO_MODE_DEFAULT_WINDOW_MAX_ENTRIE
|
|
|
2
2
|
import { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./auto-mode-prompt-assets.js";
|
|
3
3
|
import { cutEnginePrefix, cutEngineSegments, flattenableUserText } from "./untrusted-text.js";
|
|
4
4
|
import { readDenyPatternRun } from "../tools/fs/read-deny.js";
|
|
5
|
+
import { renderContentBlock, renderUserAttachmentView, userAttachmentOf } from "../internal/llm.js";
|
|
5
6
|
export { AUTO_MODE_DEFAULTS_SENTINEL } from "./auto-mode-defaults.js";
|
|
6
7
|
function mergeRuleSection(user, defaultsInner) {
|
|
7
8
|
if (!user?.length)
|
|
@@ -100,7 +101,10 @@ function excerptTailInclusive(text, cap) {
|
|
|
100
101
|
const LAUNCHING_AGENT_FRAME = "(this agent was started by another agent: every entry under [launching agent] reached it through that agent, and none is vouched for as the user's own message)\n";
|
|
101
102
|
function renderEntry(m, cap, user) {
|
|
102
103
|
if (m.role === "user") {
|
|
103
|
-
const raw = typeof m.content === "string" ? m.content : m.content.map(
|
|
104
|
+
const raw = typeof m.content === "string" ? m.content : m.content.map(renderContentBlock).join("\n");
|
|
105
|
+
const attachment = userAttachmentOf(m);
|
|
106
|
+
if (attachment !== undefined)
|
|
107
|
+
return `[attachment]\n${renderUserAttachmentView(m, attachment, cap)}`;
|
|
104
108
|
if (m.provenance === "engine-note") {
|
|
105
109
|
return `[engine note]\n${excerpt(raw, cap)}`;
|
|
106
110
|
}
|
|
@@ -126,7 +130,7 @@ function renderEntry(m, cap, user) {
|
|
|
126
130
|
return note === undefined ? `${user}\n${excerpt(text, cap)}` : `${user}\n${note}\n${excerpt(text, cap)}`;
|
|
127
131
|
}
|
|
128
132
|
if (m.role === "toolResult") {
|
|
129
|
-
const text = m.content.map(
|
|
133
|
+
const text = m.content.map(renderContentBlock).join("\n");
|
|
130
134
|
return `[tool_result ${m.toolName}${m.isError ? " (error)" : ""}]\n${excerpt(text, cap)}`;
|
|
131
135
|
}
|
|
132
136
|
const parts = [];
|
|
@@ -2103,15 +2103,17 @@ export type CheckpointErrorReason = "version_newer" | "env_factory_missing" | "g
|
|
|
2103
2103
|
* entry / a record member naming no absolute root. */
|
|
2104
2104
|
| "installation_record_absent" | "installation_record_misaligned" | "installation_record_invalid";
|
|
2105
2105
|
/**
|
|
2106
|
-
* The codes a RESUME's pre-claim configuration screen carries as `detail.code` of a `checkpoint.config_refused` — the
|
|
2107
|
-
* words prepare's mode, output-style, approval-reach, invoked-skill and recorded-mode doors throw, held unchanged so one refusal
|
|
2106
|
+
* The codes a RESUME's pre-claim configuration screen carries as `detail.code` of a `checkpoint.config_refused` — the eight
|
|
2107
|
+
* words prepare's mode, output-style, approval-reach, invoked-skill, attachment and recorded-mode doors throw, held unchanged so one refusal
|
|
2108
2108
|
* reads the same on a fresh run (`TaskResult.errorCode`) and on a resume. `config.spec_retired_key`: a retired spec key;
|
|
2109
2109
|
* `config.output_style_invalid`: a malformed output-style seat; `config.permission_mode_invalid`: a mode word outside the closed
|
|
2110
2110
|
* set (the config's `permissionMode`, or one the row recorded); `config.shell_gate_value_retired`: a retired shell-floor word;
|
|
2111
2111
|
* `config.approval_reach_invalid`: an `approvalReach` outside its set; `config.user_invoked_skill_unknown` / `skill_deny_unreadable`:
|
|
2112
|
-
* `userInvokedSkills` names a skill the config does not carry / one whose `disallowed-tools` entry the grammar reads as nothing
|
|
2112
|
+
* `userInvokedSkills` names a skill the config does not carry / one whose `disallowed-tools` entry the grammar reads as nothing;
|
|
2113
|
+
* `config.user_attachment_invalid`: the config declares `userAttachments` (a resume sends no new human message) or a malformed
|
|
2114
|
+
* value there.
|
|
2113
2115
|
*/
|
|
2114
|
-
export declare const CHECKPOINT_CONFIG_REFUSAL_CODES: readonly ["config.spec_retired_key", "config.output_style_invalid", "config.permission_mode_invalid", "config.shell_gate_value_retired", "config.approval_reach_invalid", "config.user_invoked_skill_unknown", "config.skill_deny_unreadable"];
|
|
2116
|
+
export declare const CHECKPOINT_CONFIG_REFUSAL_CODES: readonly ["config.spec_retired_key", "config.output_style_invalid", "config.permission_mode_invalid", "config.shell_gate_value_retired", "config.approval_reach_invalid", "config.user_invoked_skill_unknown", "config.skill_deny_unreadable", "config.user_attachment_invalid"];
|
|
2115
2117
|
export type CheckpointConfigRefusalCode = (typeof CHECKPOINT_CONFIG_REFUSAL_CODES)[number];
|
|
2116
2118
|
export declare const isCheckpointConfigRefusalCode: (word: unknown) => word is CheckpointConfigRefusalCode;
|
|
2117
2119
|
/** A typed checkpoint-store error so callers branch on `code` (mirrors `SessionError`). */
|
|
@@ -304,7 +304,7 @@ export function summarizeCheckpoint(cp) {
|
|
|
304
304
|
...(hasBidi ? { hasBidiControls: true } : {}),
|
|
305
305
|
};
|
|
306
306
|
}
|
|
307
|
-
export const CHECKPOINT_CONFIG_REFUSAL_CODES = Object.freeze(["config.spec_retired_key", "config.output_style_invalid", "config.permission_mode_invalid", "config.shell_gate_value_retired", "config.approval_reach_invalid", "config.user_invoked_skill_unknown", "config.skill_deny_unreadable"]);
|
|
307
|
+
export const CHECKPOINT_CONFIG_REFUSAL_CODES = Object.freeze(["config.spec_retired_key", "config.output_style_invalid", "config.permission_mode_invalid", "config.shell_gate_value_retired", "config.approval_reach_invalid", "config.user_invoked_skill_unknown", "config.skill_deny_unreadable", "config.user_attachment_invalid"]);
|
|
308
308
|
export const isCheckpointConfigRefusalCode = (word) => CHECKPOINT_CONFIG_REFUSAL_CODES.some((w) => w === word);
|
|
309
309
|
export class CheckpointError extends Error {
|
|
310
310
|
code;
|
|
@@ -77,7 +77,7 @@ export declare function contextEditFrontier(window: number): number;
|
|
|
77
77
|
* runner wires it on BOTH arms by default — the frontier pass and the MC-R rejection arm share
|
|
78
78
|
* one persist closure — so the shipped default clears to a pointer, never to a dead end.
|
|
79
79
|
* ④ the TRIGGER — CC's client machine fires ONLY on the server's `context_hint` 422/424 refusal
|
|
80
|
-
* (cli250.js:58375
|
|
80
|
+
* (cli250.js:58375 `context_hint`; the `tengu_time_based_microcompact` event name is historical, its `trigger`
|
|
81
81
|
* is the constant `"context_hint"`). Under BYOM there is no such server, so the ask half is
|
|
82
82
|
* unportable and the refusal half is aligned on wire truth (the MC-R arm classifies the
|
|
83
83
|
* provider's own context-overflow refusal) — registered divergence D-3. sema additionally
|
|
@@ -92,7 +92,7 @@ export declare const MICRO_COMPACT_MACHINES: readonly ["off", "cc"];
|
|
|
92
92
|
export type MicroCompactMachine = (typeof MICRO_COMPACT_MACHINES)[number];
|
|
93
93
|
/**
|
|
94
94
|
* How many most-recent CANDIDATE tool results keep their content when the stale-result pass runs:
|
|
95
|
-
* 5 (CC 2.1.223 `uAp`; 2.1.250 `var k
|
|
95
|
+
* 5 (CC 2.1.223 `uAp`; 2.1.250 `var k=5` beside the call site, cli250.js:58373, and 2.1.245 carries
|
|
96
96
|
* the same value — zero drift across 245→250; same value again on CC 88's `timeBasedMCConfig`, i.e.
|
|
97
97
|
* stable across all three corpus generations, and it is a hard-coded constant upstream, not a
|
|
98
98
|
* setting). The #384 adjudication adopted 5 CONDITIONAL on the whole machine coming with it (20k
|
|
@@ -40,7 +40,7 @@ export declare function nestedRecordsOf(receipt: object): object[];
|
|
|
40
40
|
* carrier, the member named by `path`: an accessor (never invoked), and a member the prototype or a non-enumerable property
|
|
41
41
|
* supplies, unless the nearest descriptor holding it spells absence (an `undefined` data property). Descriptors are looked up
|
|
42
42
|
* as OWN entries of the descriptor map, so a polluted `Object.prototype` names no member. Members outside the contract are
|
|
43
|
-
* not looked at. */
|
|
43
|
+
* not looked at — but a retired one (`retired`) is refused by name when the carrier carries it by that same law. */
|
|
44
44
|
/** The receipt's own answers carry a `kind` discriminant as an own property: narrowing never asks the prototype chain (`in`) for a
|
|
45
45
|
* word, so a polluted `Object.prototype` cannot answer for the receipt either. */
|
|
46
46
|
export type Received = {
|
|
@@ -50,5 +50,5 @@ export type Received = {
|
|
|
50
50
|
kind: "refused";
|
|
51
51
|
refused: PermissionResult;
|
|
52
52
|
};
|
|
53
|
-
export declare function receiveContract(carrier: object, members: readonly string[], path: string): Received;
|
|
53
|
+
export declare function receiveContract(carrier: object, members: readonly string[], path: string, retired?: Readonly<Record<string, string>>): Received;
|
|
54
54
|
export {};
|
|
@@ -12,7 +12,7 @@ export const isEngineRefusal = (d) => typeof d === "object" && d !== null && ENG
|
|
|
12
12
|
const DECISION_REASONS = Object.freeze(["rule", "mode", "hook", "safety", "classifier", "persisted_rule", "sandbox", "org_rule", "org_unavailable", "read_only", "skill", "deployment_rule", "request_rule"]);
|
|
13
13
|
const DECISION_ACTIONS = Object.keys({ allow: 0, deny: 0, ask: 0 });
|
|
14
14
|
const GUARDS = Object.keys({ dangerous_delete: 0, unverifiable_delete: 0, unverifiable_shell_text: 0, transcript_write: 0, artifact_shared_read: 0 });
|
|
15
|
-
const RULE_UNREADABLE_WORDS = Object.keys({ content_deny: 0, content_ask: 0, persisted_call: 0 });
|
|
15
|
+
const RULE_UNREADABLE_WORDS = Object.keys({ content_deny: 0, content_ask: 0, persisted_call: 0, persisted_rows: 0 });
|
|
16
16
|
const AUTHORED_BY_WORDS = Object.keys({ deployment: 0 });
|
|
17
17
|
const memberOf = (words) => {
|
|
18
18
|
const set = new Set(words);
|
|
@@ -26,7 +26,7 @@ const CLOSED_MEMBERS = [
|
|
|
26
26
|
{ member: "classes", words: ASK_CLASSES, has: isAskClass, list: true },
|
|
27
27
|
{ member: "foldedAskClasses", words: ASK_CLASSES, has: isAskClass, list: true },
|
|
28
28
|
{ member: "mandate", words: PERSISTED_RULE_MANDATES, has: isPersistedRuleMandate },
|
|
29
|
-
{ member: "
|
|
29
|
+
{ member: "guards", words: GUARDS, has: memberOf(GUARDS), list: true },
|
|
30
30
|
{ member: "ruleUnreadable", words: RULE_UNREADABLE_WORDS, has: memberOf(RULE_UNREADABLE_WORDS) },
|
|
31
31
|
{ member: "authoredBy", words: AUTHORED_BY_WORDS, has: memberOf(AUTHORED_BY_WORDS) },
|
|
32
32
|
{ member: "classifierNoRuling", words: AUTO_MODE_FALLBACK_CAUSES, has: isAutoModeFallbackCause },
|
|
@@ -197,7 +197,7 @@ function ordinaryChain(carrier) {
|
|
|
197
197
|
}
|
|
198
198
|
return { kind: "chain", chain };
|
|
199
199
|
}
|
|
200
|
-
export function receiveContract(carrier, members, path) {
|
|
200
|
+
export function receiveContract(carrier, members, path, retired) {
|
|
201
201
|
const what = path === "" ? "a permission decision" : `a permission decision's "${path.slice(0, -1)}" record`;
|
|
202
202
|
const ordinary = ordinaryChain(carrier);
|
|
203
203
|
if (ordinary.kind === "not") {
|
|
@@ -210,6 +210,21 @@ export function receiveContract(carrier, members, path) {
|
|
|
210
210
|
catch (e) {
|
|
211
211
|
return { kind: "refused", refused: refusedOutOfContract(`${what} threw while its property descriptors were looked at (${thrownText(e)}) — not an ordinary object; denied fail-closed`) };
|
|
212
212
|
}
|
|
213
|
+
const nearest = (m) => {
|
|
214
|
+
try {
|
|
215
|
+
if (Object.hasOwn(own, m))
|
|
216
|
+
return { kind: "found", d: own[m] };
|
|
217
|
+
for (let i = 1; i < ordinary.chain.length; i++) {
|
|
218
|
+
const d = Object.getOwnPropertyDescriptor(ordinary.chain[i], m);
|
|
219
|
+
if (d !== undefined)
|
|
220
|
+
return { kind: "found", d };
|
|
221
|
+
}
|
|
222
|
+
return { kind: "found", d: undefined };
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
return { kind: "refused", refused: refusedOutOfContract(`${what} threw while "${path}${m}" was looked for on its prototype chain (${thrownText(e)}) — not an ordinary object; denied fail-closed`) };
|
|
226
|
+
}
|
|
227
|
+
};
|
|
213
228
|
const held = Object.create(null);
|
|
214
229
|
for (const m of members) {
|
|
215
230
|
const d = Object.hasOwn(own, m) ? own[m] : undefined;
|
|
@@ -220,14 +235,10 @@ export function receiveContract(carrier, members, path) {
|
|
|
220
235
|
held[m] = d.value;
|
|
221
236
|
continue;
|
|
222
237
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
catch (e) {
|
|
229
|
-
return { kind: "refused", refused: refusedOutOfContract(`${what} threw while "${path}${m}" was looked for on its prototype chain (${thrownText(e)}) — not an ordinary object; denied fail-closed`) };
|
|
230
|
-
}
|
|
238
|
+
const found = nearest(m);
|
|
239
|
+
if (found.kind === "refused")
|
|
240
|
+
return found;
|
|
241
|
+
const reached = found.d;
|
|
231
242
|
if (reached !== undefined && !spellsAbsence(reached)) {
|
|
232
243
|
return {
|
|
233
244
|
kind: "refused",
|
|
@@ -236,5 +247,13 @@ export function receiveContract(carrier, members, path) {
|
|
|
236
247
|
};
|
|
237
248
|
}
|
|
238
249
|
}
|
|
250
|
+
for (const [m, replacement] of Object.entries(retired ?? {})) {
|
|
251
|
+
const found = nearest(m);
|
|
252
|
+
if (found.kind === "refused")
|
|
253
|
+
return found;
|
|
254
|
+
if (found.d !== undefined && !spellsAbsence(found.d)) {
|
|
255
|
+
return { kind: "refused", refused: refusedOutOfContract(`a permission decision carries the retired "${m}" member — ${replacement}; denied fail-closed rather than executing a decision with a member this layer does not read`) };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
239
258
|
return { kind: "held", held };
|
|
240
259
|
}
|
|
@@ -7,11 +7,18 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { TaskStatus } from "./task-result.js";
|
|
9
9
|
import type { RunnerDeps } from "./runner-deps.js";
|
|
10
|
+
import type { TaskEvent } from "./task-event.js";
|
|
10
11
|
/**
|
|
11
12
|
* design/129-B — one lifecycle event of a BACKGROUND delegation child, delivered to the
|
|
12
13
|
* process-level {@link RunnerDeps.onBackgroundChildEvent} observer. Keys: `taskId` is the registry handle
|
|
13
14
|
* (`a*` domain — the TaskOutput/TaskStop key); `progressTaskId`/`sessionId` are the child RUN's id (uuid
|
|
14
15
|
* domain — the same key `task_progress` frames carry).
|
|
16
|
+
*
|
|
17
|
+
* `kind: "spawn"` announces a fleet ROW's birth, which is a different fact from a child RUN's start
|
|
18
|
+
* (`TaskEvent` `task_started`): an `a*` row is born when its lane registers it, before the child run
|
|
19
|
+
* prepares (and a child whose prepare throws has a row and no run start); a `wa*` row is born with the
|
|
20
|
+
* workflow's own agent record, including rows that never run a child at all (a replayed or re-parked
|
|
21
|
+
* agent). Each fact has one mint — the lane for the row, the child's spawn station for the run.
|
|
15
22
|
*/
|
|
16
23
|
export interface BackgroundChildEvent {
|
|
17
24
|
kind: "spawn" | "tick" | "terminal";
|
|
@@ -223,15 +230,19 @@ export interface BackgroundChildEvent {
|
|
|
223
230
|
* now sees every delegation lane through one seat.
|
|
224
231
|
*
|
|
225
232
|
* EMISSION CHOKEPOINT (the reason this seat covers every lane at once): frames are minted by the
|
|
226
|
-
* RUNNER at the child LEG itself —
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* this chokepoint, so none of them needs its own emission and none can drift.
|
|
233
|
+
* RUNNER at the child LEG itself — not by the individual spawn lanes. Every delegation lane
|
|
234
|
+
* (sync/steer/background/fork/revive, workflow-spawned agents) runs its child through this
|
|
235
|
+
* chokepoint, so none of them needs its own emission and none can drift.
|
|
230
236
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
237
|
+
* SPAWN IS A PROJECTION, TERMINAL IS PER LEG. The `spawn` phase is not a fact of its own: it is the
|
|
238
|
+
* projection of the child's `task_started` stream frame (`TaskEvent`), delivered from that frame's one
|
|
239
|
+
* mint at the leg's spawn station, at the same instant and under the same gate — so it fires exactly
|
|
240
|
+
* when a CYCLE starts (a first spawn, or a revival of a completed child) and never for a durable
|
|
241
|
+
* RESUME leg, which continues a cycle that already started. The `terminal` phase fires for EVERY
|
|
242
|
+
* delegated leg, resume legs included (a park ends a leg with `"suspended"`; the resume leg that later
|
|
243
|
+
* redeems it ends with its own terminal). Consumers correlate legs of one delegation by
|
|
244
|
+
* `identity.taskId` / `identity.parentToolCallId` (stable across legs), and `identity.legKind`
|
|
245
|
+
* (`"resume"`) names a resume leg's terminal.
|
|
235
246
|
*
|
|
236
247
|
* HONEST ABSENCES (recorded, not gaps to fix silently):
|
|
237
248
|
* · a child whose PREPARE throws emits neither frame (there is no leg identity to report);
|
|
@@ -242,14 +253,15 @@ export interface BackgroundChildEvent {
|
|
|
242
253
|
* ({@link import("./checkpoint-store.js").CheckpointState.isDelegatedChild}) emits neither frame:
|
|
243
254
|
* the row carries no evidence the parked leg was a delegated child, and fabricating the axis
|
|
244
255
|
* would stamp delegation frames onto host-resumed root tasks. Rows minted WITH the axis resume
|
|
245
|
-
* with their
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* the ordinary path emits it where the `TaskResult` assembles, and a
|
|
249
|
-
* stream layer converts into a synthesized failed result (e.g.
|
|
250
|
-
* past the run tail) emits the failed terminal from that
|
|
251
|
-
*
|
|
252
|
-
* both fire for one leg). A leg whose PROCESS dies mid-flight is the only
|
|
256
|
+
* with their terminal even deps-only; a resume that re-supplies trusted internals (every
|
|
257
|
+
* in-engine lane does) was never affected.
|
|
258
|
+
* TERMINAL OWED (closed the one hole): every delegated leg that reached its wiring is closed by
|
|
259
|
+
* exactly one terminal frame — the ordinary path emits it where the `TaskResult` assembles, and a
|
|
260
|
+
* POST-WIRING throw that the stream layer converts into a synthesized failed result (e.g.
|
|
261
|
+
* `resume.tool_unavailable` re-thrown past the run tail) emits the failed terminal from that
|
|
262
|
+
* backstop, gated on a carrier set at the spawn station and cleared by the ordinary terminal (so the
|
|
263
|
+
* two sites can never both fire for one leg). A leg whose PROCESS dies mid-flight is the only one
|
|
264
|
+
* left without its terminal.
|
|
253
265
|
*
|
|
254
266
|
* OBSERVATION ONLY (matrix §5.2 Q3: frame-rate lifecycle facts ride an observer seat, never
|
|
255
267
|
* onNotice): no return capability, and delivery can never alter the child run —
|
|
@@ -265,23 +277,59 @@ export interface BackgroundChildEvent {
|
|
|
265
277
|
* `frame.hasOwnProperty(...)` (throws) or implicit string coercion (`` `${frame}` `` throws).
|
|
266
278
|
*/
|
|
267
279
|
export type DelegationLifecycleEvent = {
|
|
280
|
+
/** A cycle started — the projection of the leg's `task_started` frame (one per first spawn or
|
|
281
|
+
* revival; never for a resume leg, so `identity.legKind` is `"child"` on every spawn frame). */
|
|
268
282
|
phase: "spawn";
|
|
269
283
|
/** The child LEG's identity envelope (#281 件A — the same frozen object that leg's own hook
|
|
270
284
|
* invocations carry). `isDelegatedChild` is `true` by construction on every frame here. */
|
|
271
285
|
identity: import("./hooks.js").HookInvocationIdentity;
|
|
272
286
|
} | {
|
|
273
287
|
phase: "terminal";
|
|
274
|
-
/**
|
|
288
|
+
/** The leg's identity envelope (one mint per leg — the same object as the cycle's spawn frame on
|
|
289
|
+
* the leg that started it). */
|
|
275
290
|
identity: import("./hooks.js").HookInvocationIdentity;
|
|
276
291
|
/** The leg's settled status word — the projection of the `TaskResult.terminal` the spawning lane
|
|
277
292
|
* receives (the sync lane's previously-void `settled` payload, made public). `"suspended"` means a
|
|
278
|
-
* durable park: expect a later `"resume"`-leg
|
|
293
|
+
* durable park: expect a later `"resume"`-leg terminal if it is redeemed (a resume leg starts no
|
|
294
|
+
* cycle, so no spawn frame precedes it). */
|
|
279
295
|
status: TaskStatus;
|
|
280
296
|
/** Turns the leg completed. */
|
|
281
297
|
turns: number;
|
|
282
298
|
/** The leg's terminal code (the failed cause's `code`), when one was stamped. */
|
|
283
299
|
errorCode?: string;
|
|
284
300
|
};
|
|
301
|
+
/**
|
|
302
|
+
* The ONE eligibility reading for a leg's delegated-child ROW frames — its `task_started`, its
|
|
303
|
+
* `task_progress` ticks and its terminal tick all read this and nothing else: a delegated leg, by the
|
|
304
|
+
* identity envelope's effective delegation fact. A row a consumer can open is a row it can close, so
|
|
305
|
+
* no frame of the family may ask a narrower question (whether a spawning tool call is known is
|
|
306
|
+
* attribution, carried on the frames when it exists — never eligibility).
|
|
307
|
+
*/
|
|
308
|
+
export declare function emitsChildRowFrames(identity: import("./hooks.js").HookInvocationIdentity): boolean;
|
|
309
|
+
/**
|
|
310
|
+
* The ONE constructor of a delegated child's `task_started` frame ({@link TaskEvent}); the leg's spawn
|
|
311
|
+
* station calls it once per started cycle. Every member is copied from the seat its `task_progress`
|
|
312
|
+
* twin (or the fleet row) reads — the run ids, the prepared model, the delegation facts the spawner
|
|
313
|
+
* threaded, and the display label as the identity envelope bounds it, present under the tick's own
|
|
314
|
+
* law (a label that bounds to nothing is no label) — so the frame introduces no second reading of any
|
|
315
|
+
* of them. `stamp` is the event identity the leg stamps on every frame.
|
|
316
|
+
*/
|
|
317
|
+
export declare function taskStartedFrame(run: {
|
|
318
|
+
taskId: string;
|
|
319
|
+
sessionId: string;
|
|
320
|
+
model: string;
|
|
321
|
+
identity: import("./hooks.js").HookInvocationIdentity;
|
|
322
|
+
}, spawner: {
|
|
323
|
+
delegationTaskType?: import("./task-event.js").DelegationTaskType;
|
|
324
|
+
cycleSeq?: number;
|
|
325
|
+
parentTaskId?: string;
|
|
326
|
+
parentSessionId?: string;
|
|
327
|
+
rootSessionId?: string;
|
|
328
|
+
explicitAgentName?: string;
|
|
329
|
+
spawnAgentType?: string;
|
|
330
|
+
} | undefined, stamp: import("./task-event.js").TaskEventIdentity): Extract<TaskEvent, {
|
|
331
|
+
type: "task_started";
|
|
332
|
+
}>;
|
|
285
333
|
/** Test seam (mirrors `__resetMalformedNoticeSeatAnnouncement`): never called by production code. */
|
|
286
334
|
export declare function __resetMalformedDelegationSeatAnnouncement(): void;
|
|
287
335
|
/**
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import { observeThenableRejection } from "./safe-notify.js";
|
|
2
|
+
export function emitsChildRowFrames(identity) {
|
|
3
|
+
return identity.isDelegatedChild;
|
|
4
|
+
}
|
|
5
|
+
export function taskStartedFrame(run, spawner, stamp) {
|
|
6
|
+
const { identity } = run;
|
|
7
|
+
return {
|
|
8
|
+
type: "task_started",
|
|
9
|
+
taskId: run.taskId,
|
|
10
|
+
sessionId: run.sessionId,
|
|
11
|
+
...(spawner?.delegationTaskType !== undefined ? { taskType: spawner.delegationTaskType } : {}),
|
|
12
|
+
...(spawner?.cycleSeq !== undefined ? { seq: spawner.cycleSeq } : {}),
|
|
13
|
+
...(spawner?.parentTaskId !== undefined ? { parentTaskId: spawner.parentTaskId } : {}),
|
|
14
|
+
...(spawner?.parentSessionId !== undefined ? { parentSessionId: spawner.parentSessionId } : {}),
|
|
15
|
+
...(spawner?.rootSessionId !== undefined ? { rootSessionId: spawner.rootSessionId } : {}),
|
|
16
|
+
...(identity.agentName ? { name: identity.agentName } : {}),
|
|
17
|
+
...(spawner?.explicitAgentName !== undefined ? { spawnName: spawner.explicitAgentName } : {}),
|
|
18
|
+
...(spawner?.spawnAgentType !== undefined ? { agentType: spawner.spawnAgentType } : {}),
|
|
19
|
+
model: run.model,
|
|
20
|
+
startedAt: Date.now(),
|
|
21
|
+
...stamp,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
2
24
|
let malformedDelegationSeatAnnounced = false;
|
|
3
25
|
export function __resetMalformedDelegationSeatAnnouncement() {
|
|
4
26
|
malformedDelegationSeatAnnounced = false;
|
|
@@ -394,11 +394,13 @@ export interface EngineNotice {
|
|
|
394
394
|
* rowCount, sessionId, runId }` (`rows` = the first eight non-built-in rows). Audience `user`.
|
|
395
395
|
* - `"permission.rule_store_unavailable"` — the person's persisted permission rules could not be read (a read
|
|
396
396
|
* failure, a corrupt or unreadable store, a read that did not answer in time), so that read's rows were SKIPPED:
|
|
397
|
-
* none of their allow, ask or deny rows took part in the decisions it served
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
397
|
+
* none of their allow, ask or deny rows took part in the decisions it served. At `"call"` the call asks a person
|
|
398
|
+
* meanwhile (a deny row among the rows nobody could read may forbid it; the class is `governance_unavailable`,
|
|
399
|
+
* which `bypassPermissions` keeps and a headless run refuses); at `"prepare"` the run's path tables lack the rows
|
|
400
|
+
* for the whole run. `detail.at` names the read that found it first: `"prepare"` (the path rows the run's file and
|
|
401
|
+
* shell checks take at run start — the next run reads them again) or `"call"` (one call's own rule check — every
|
|
402
|
+
* later call reads again, so the rows apply as soon as a read succeeds). Once per run, whichever read fails
|
|
403
|
+
* first; the trace keeps every read's line (`permission.rule_store_unreadable`).
|
|
402
404
|
* `detail: { at, sessionId, runId }`. Audience `user`.
|
|
403
405
|
* - `"permission.rule_row_dropped"` — the person's store served project-scoped rows whose scope root is not a
|
|
404
406
|
* complete absolute path (only a foreign writer or a hand edit makes one; every engine write face refuses it): they
|
|
@@ -86,7 +86,8 @@ export declare function noteHostRewrite(seam: FrameSettlement, toolCallId: strin
|
|
|
86
86
|
* carrier (the host's decision), withheld otherwise (owed again). */
|
|
87
87
|
export declare function recordFrameReceipt(seam: FrameSettlement, carrier: string, committedContent: unknown, entryId: string): void;
|
|
88
88
|
/** The carrier a committed frame was riding, as the harness reports it at the transcript append — a tool
|
|
89
|
-
* result with the content it was committed with (after every result transformation)
|
|
89
|
+
* result with the content it was committed with (after every result transformation); a user-attachment frame by
|
|
90
|
+
* its delivery record's key (`inputId`, `ordinal`). */
|
|
90
91
|
export type CommittedFrameCarrier = {
|
|
91
92
|
kind: "engine_note";
|
|
92
93
|
payload: unknown;
|
|
@@ -94,4 +95,8 @@ export type CommittedFrameCarrier = {
|
|
|
94
95
|
kind: "tool_result";
|
|
95
96
|
toolCallId: string;
|
|
96
97
|
content: unknown;
|
|
98
|
+
} | {
|
|
99
|
+
kind: "attachment";
|
|
100
|
+
inputId: string;
|
|
101
|
+
ordinal: number;
|
|
97
102
|
};
|
package/dist/core/gate-fold.js
CHANGED
|
@@ -5,17 +5,26 @@ import { normalizeReadRootCandidate } from "./read-root-candidate.js";
|
|
|
5
5
|
import { traceHookCrash } from "./gate-crash-trace.js";
|
|
6
6
|
import { writeProtectionMandated, writeProtectionTightenDecision } from "./write-protect.js";
|
|
7
7
|
import { createGateDeadline, awaitWriteProtectionJudgment, gateWaitFailureOf, withGateTimeout } from "./gate-bounded-await.js";
|
|
8
|
+
import { shellReadFaceWords } from "../tools/fs/shell-read-face.js";
|
|
9
|
+
import { failedJudgmentDeny } from "./runner/policy-throw.js";
|
|
8
10
|
import { thrownText } from "./thrown-value.js";
|
|
9
11
|
import { refuseOutOfContractDecision } from "./tool-policy.js";
|
|
12
|
+
import { applyUnansweredSilence, silencedBy } from "./shell-read-silence.js";
|
|
10
13
|
import { asksUnstated, permissionSeatAt, unstatedAskMessage } from "./mode-policy.js";
|
|
11
14
|
export async function runGateFold(pass) {
|
|
12
15
|
const { input, toolName, toolCallId, callSignal, ledger, notifier, adjudicate } = pass;
|
|
13
16
|
const boundedAwaitDeadlineMs = createGateDeadline(input, notifier);
|
|
14
17
|
pass.req = { toolName, args: pass.currentInput, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) };
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
pass.req
|
|
18
|
-
|
|
18
|
+
let unread;
|
|
19
|
+
try {
|
|
20
|
+
const stamped = input.stampCallTarget?.(pass.req, callSignal ?? input.abortSignal);
|
|
21
|
+
if (stamped !== undefined)
|
|
22
|
+
pass.req = await stamped;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
unread = { error };
|
|
26
|
+
}
|
|
27
|
+
pass.decision = unread !== undefined ? failedJudgmentDeny(unread.error, callSignal ?? input.abortSignal, (e) => traceHookCrash(input, e, notifier), "the call's target reading", { toolName, toolCallId }) : adjudicate ? refuseOutOfContractDecision(await (callSignal !== undefined ? adjudicate(pass.req, callSignal) : adjudicate(pass.req))) : { action: "allow" };
|
|
19
28
|
{
|
|
20
29
|
const read = ledger.fromDecision(pass.decision);
|
|
21
30
|
pass.decision = read.decision;
|
|
@@ -180,9 +189,10 @@ export async function runGateFold(pass) {
|
|
|
180
189
|
return await pass.afterFold(pass);
|
|
181
190
|
}
|
|
182
191
|
function shellReadAskMessage(toolName, verdict) {
|
|
192
|
+
const { verb, participle } = shellReadFaceWords(verdict.face);
|
|
183
193
|
if (verdict.finding !== "outside_roots")
|
|
184
|
-
return `tool "${toolName}"
|
|
185
|
-
return `tool "${toolName}" ${
|
|
194
|
+
return `tool "${toolName}" ${verb} where the session's read boundary could not establish what is ${participle} — explicit approval required`;
|
|
195
|
+
return `tool "${toolName}" ${verb} outside the directories allowed for this session — explicit approval required`;
|
|
186
196
|
}
|
|
187
197
|
function applyShellReadVerdict(pass, verdict, toolName, shellGated) {
|
|
188
198
|
const account = verdict.sentence !== undefined && verdict.sentence.trim().length > 0 ? inlineUntrusted(verdict.sentence, PROBE_REASON_MAX) : undefined;
|
|
@@ -196,25 +206,26 @@ function applyShellReadVerdict(pass, verdict, toolName, shellGated) {
|
|
|
196
206
|
pass.shellUnplacedOptions = verdict.unplacedOptions;
|
|
197
207
|
if (verdict.finding === "unestablishable" || verdict.finding === "declined" || verdict.finding === "unanswered" || verdict.unestablished === true)
|
|
198
208
|
pass.shellReadUnestablished = true;
|
|
209
|
+
if (verdict.disposition === "refuse") {
|
|
210
|
+
pass.decision = { action: "deny", message: verdict.sentence ?? `tool "${toolName}" reads what the session's read boundary refuses` };
|
|
211
|
+
pass.deniedBy = "read_boundary";
|
|
212
|
+
if (verdict.details !== undefined)
|
|
213
|
+
pass.denyDetails = verdict.details;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (verdict.finding === "unanswered") {
|
|
217
|
+
applyUnansweredSilence(pass, verdict, { message: shellReadAskMessage(toolName, verdict), account, tightenedBy: shellGated ? "shell_gate" : "safety" });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
199
220
|
switch (verdict.disposition) {
|
|
200
221
|
case "vouch":
|
|
201
222
|
return;
|
|
202
|
-
case "refuse":
|
|
203
|
-
pass.decision = { action: "deny", message: verdict.sentence ?? `tool "${toolName}" reads what the session's read boundary refuses` };
|
|
204
|
-
pass.deniedBy = "read_boundary";
|
|
205
|
-
if (verdict.details !== undefined)
|
|
206
|
-
pass.denyDetails = verdict.details;
|
|
207
|
-
return;
|
|
208
223
|
case "mandated": {
|
|
209
224
|
if (verdict.details !== undefined)
|
|
210
225
|
pass.shellReadDetails = verdict.details;
|
|
211
|
-
const unreadable = verdict.finding === "unanswered";
|
|
212
226
|
if (pass.decision.action !== "allow") {
|
|
213
|
-
if (pass.decision.action === "ask")
|
|
214
|
-
pass.decision =
|
|
215
|
-
? { ...pass.decision, probeMandated: true }
|
|
216
|
-
: { ...pass.decision, requiresRealApproval: true, ...(pass.decision.guard === undefined ? { guard: "unverifiable_shell_text" } : { foldedAskClasses: [...new Set([...(pass.decision.foldedAskClasses ?? []), "unverifiable_shell_text"])] }) };
|
|
217
|
-
}
|
|
227
|
+
if (pass.decision.action === "ask")
|
|
228
|
+
pass.decision = { ...pass.decision, probeMandated: true, ...silencedBy(verdict, pass.decision) };
|
|
218
229
|
return;
|
|
219
230
|
}
|
|
220
231
|
const candidate = normalizeReadRootCandidate(verdict.readRootCandidate);
|
|
@@ -222,7 +233,8 @@ function applyShellReadVerdict(pass, verdict, toolName, shellGated) {
|
|
|
222
233
|
action: "ask",
|
|
223
234
|
message: shellReadAskMessage(toolName, verdict),
|
|
224
235
|
decisionReason: "safety",
|
|
225
|
-
|
|
236
|
+
probeMandated: true,
|
|
237
|
+
...silencedBy(verdict, {}),
|
|
226
238
|
...(account !== undefined ? { probeReason: account } : {}),
|
|
227
239
|
...(candidate !== undefined ? { probeReadRootCandidate: candidate } : {}),
|
|
228
240
|
};
|
|
@@ -235,12 +247,15 @@ function applyShellReadVerdict(pass, verdict, toolName, shellGated) {
|
|
|
235
247
|
verdict.admit?.();
|
|
236
248
|
if (verdict.details !== undefined)
|
|
237
249
|
pass.shellReadDetails = verdict.details;
|
|
250
|
+
if (pass.decision.action === "ask")
|
|
251
|
+
pass.decision = { ...pass.decision, ...silencedBy(verdict, pass.decision) };
|
|
238
252
|
if (pass.decision.action === "allow") {
|
|
239
253
|
const candidate = normalizeReadRootCandidate(verdict.readRootCandidate);
|
|
240
254
|
pass.decision = {
|
|
241
255
|
action: "ask",
|
|
242
256
|
message: shellReadAskMessage(toolName, verdict),
|
|
243
257
|
decisionReason: "safety",
|
|
258
|
+
...silencedBy(verdict, {}),
|
|
244
259
|
...(account !== undefined ? { probeReason: account } : {}),
|
|
245
260
|
...(candidate !== undefined ? { probeReadRootCandidate: candidate } : {}),
|
|
246
261
|
};
|