@sema-agent/core 7.52.0 → 7.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +194 -0
- package/README.md +3 -1
- package/dist/agents/cross-session-envelope.d.ts +8 -0
- package/dist/agents/cross-session-envelope.js +6 -0
- package/dist/agents/cross-session-judge.d.ts +21 -1
- package/dist/agents/cross-session-judge.js +8 -0
- package/dist/agents/cross-session-ref.d.ts +5 -0
- package/dist/agents/cross-session-ref.js +1 -0
- package/dist/agents/org-delivery-gate.d.ts +122 -0
- package/dist/agents/org-delivery-gate.js +69 -0
- package/dist/agents/peer-held-queue.d.ts +9 -4
- package/dist/agents/peer-held-queue.js +6 -5
- package/dist/agents/peer-session-drain.d.ts +35 -5
- package/dist/agents/peer-session-drain.js +271 -180
- package/dist/agents/read-inbox-tool.d.ts +7 -0
- package/dist/agents/read-inbox-tool.js +5 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +99 -72
- package/dist/agents/tool-filter.d.ts +18 -0
- package/dist/agents/tool-filter.js +6 -0
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +4 -4
- package/dist/brain/model-presets.js +2 -1
- package/dist/brain/route-adjudicator.d.ts +6 -0
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +30 -13
- package/dist/core/allow-entry.d.ts +2 -1
- package/dist/core/allow-entry.js +2 -2
- package/dist/core/auto-compaction.d.ts +57 -0
- package/dist/core/auto-compaction.js +28 -2
- package/dist/core/checkpoint-store.d.ts +29 -20
- package/dist/core/checkpoint-store.js +3 -3
- package/dist/core/compliance.js +3 -1
- package/dist/core/effective-path-target.d.ts +25 -6
- package/dist/core/effective-path-target.js +24 -2
- package/dist/core/engine-notice.d.ts +13 -2
- package/dist/core/gate-fold.js +3 -2
- package/dist/core/gate-lanes.js +11 -13
- package/dist/core/gate-outcome.d.ts +11 -6
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hands-band-screen.d.ts +7 -0
- package/dist/core/hands-band-screen.js +32 -0
- package/dist/core/hooks.d.ts +7 -5
- package/dist/core/hooks.js +5 -3
- package/dist/core/locked-config.d.ts +84 -32
- package/dist/core/locked-config.js +128 -42
- package/dist/core/mcp-probe.js +1 -1
- package/dist/core/mcp-server-spec.d.ts +88 -10
- package/dist/core/mcp-server-spec.js +183 -33
- package/dist/core/mcp.d.ts +21 -14
- package/dist/core/mcp.js +55 -51
- package/dist/core/memory-engine/engine.d.ts +14 -19
- package/dist/core/memory-engine/engine.js +7 -5
- package/dist/core/mode-policy.d.ts +3 -18
- package/dist/core/mode-policy.js +2 -16
- package/dist/core/mods/kernel/dispatch.js +13 -15
- package/dist/core/mods/kernel/events.d.ts +9 -6
- package/dist/core/mods/kernel/events.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +39 -10
- package/dist/core/mods/kernel/receive.js +119 -54
- package/dist/core/mods/kernel/site-rules.d.ts +80 -15
- package/dist/core/mods/kernel/site-rules.js +232 -40
- package/dist/core/mods/stations.d.ts +0 -6
- package/dist/core/mods/stations.js +0 -7
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +71 -2
- package/dist/core/own-property-receipt.d.ts +13 -0
- package/dist/core/own-property-receipt.js +3 -1
- package/dist/core/parent-spec-seat.d.ts +2 -2
- package/dist/core/permission-rule-model.d.ts +58 -9
- package/dist/core/permission-rule-model.js +81 -7
- package/dist/core/permission-rule-org.d.ts +163 -33
- package/dist/core/permission-rule-org.js +205 -71
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +37 -8
- package/dist/core/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +8 -2
- package/dist/core/retired-keys.js +12 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- package/dist/core/runner/contracts.d.ts +1 -0
- package/dist/core/runner/gate-exit.js +4 -3
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
- package/dist/core/runner/permission-rule-lanes.js +22 -10
- package/dist/core/runner/person-path-rows.d.ts +3 -1
- package/dist/core/runner/person-path-rows.js +2 -2
- package/dist/core/runner/prepare-config-doors.d.ts +48 -2
- package/dist/core/runner/prepare-config-doors.js +161 -145
- package/dist/core/runner/prepare-defer-classify.js +4 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.js +7 -5
- package/dist/core/runner/prepare-hands-readface.d.ts +1 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -1
- package/dist/core/runner/prepare-inherited-gate.js +2 -2
- package/dist/core/runner/prepare-listings.d.ts +1 -1
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
- package/dist/core/runner/prepare-memory-engine-session.js +19 -6
- package/dist/core/runner/prepare-memory.d.ts +3 -2
- package/dist/core/runner/prepare-memory.js +6 -2
- package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
- package/dist/core/runner/prepare-offload-wrappers.js +2 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
- package/dist/core/runner/prepare-policy-chain.js +12 -12
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +6 -4
- package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-assembly.js +20 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
- package/dist/core/runner/prepare-prompt-inputs.js +6 -3
- package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
- package/dist/core/runner/prepare-protocol-tools.js +19 -12
- package/dist/core/runner/prepare-question-face.d.ts +4 -2
- package/dist/core/runner/prepare-question-face.js +4 -3
- package/dist/core/runner/prepare-task.js +10 -10
- package/dist/core/runner/prepare-wiring-manifest.d.ts +8 -3
- package/dist/core/runner/prepare-wiring-manifest.js +8 -5
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +7 -5
- package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
- package/dist/core/runner/resume-internals-and-config.js +3 -8
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +1 -4
- package/dist/core/runner/run-leg.js +42 -12
- package/dist/core/runner/run-recovery-lanes.js +5 -3
- package/dist/core/runner/run-terminal-adoption.js +6 -5
- package/dist/core/runner/run-turn-boundary.js +9 -3
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -0
- package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
- package/dist/core/runner/sub-agent-deny-lists.js +7 -5
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- package/dist/core/runner-deps.d.ts +29 -14
- package/dist/core/session-policy-store.d.ts +19 -0
- package/dist/core/session-policy-store.js +22 -2
- package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/task-event.d.ts +38 -2
- package/dist/core/task-notification.js +2 -2
- package/dist/core/task-spec.d.ts +1 -1
- package/dist/core/tool-policy.d.ts +3 -0
- package/dist/core/tool-policy.js +10 -4
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +16 -0
- package/dist/core/untrusted-text.js +7 -2
- package/dist/core/user-attachment.d.ts +13 -2
- package/dist/core/user-attachment.js +47 -41
- package/dist/core/wiring-manifest.d.ts +4 -2
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +17 -5
- package/dist/engine/loop/types.d.ts +7 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.js +12 -6
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- package/dist/prompt-assembly/artifact-store.d.ts +44 -9
- package/dist/prompt-assembly/artifact-store.js +38 -12
- package/dist/prompt-assembly/artifact.d.ts +12 -11
- package/dist/prompt-assembly/artifact.js +52 -21
- package/dist/prompt-assembly/assemble.d.ts +2 -0
- package/dist/prompt-assembly/assemble.js +2 -1
- package/dist/prompt-assembly/catalog/index.js +2 -1
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +16 -2
- package/dist/prompt-assembly/composer.d.ts +2 -0
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/overlay.d.ts +21 -11
- package/dist/prompt-assembly/overlay.js +55 -17
- package/dist/prompt-assembly/select.d.ts +21 -6
- package/dist/prompt-assembly/select.js +39 -2
- package/dist/prompts/default.d.ts +0 -11
- package/dist/prompts/default.js +0 -3
- package/dist/stores/file/permission-rule-store.d.ts +1 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/tools/artifact/artifact-tool.js +4 -3
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +65 -5
- package/wire-changes/core@7.53.0.json +1946 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact } from "../auto-compaction.js";
|
|
1
|
+
import { compactionPassReport, DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact } from "../auto-compaction.js";
|
|
2
2
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
3
3
|
import { appendInterruptionMarker, reconcileInterruptedSession } from "../session-reconcile.js";
|
|
4
4
|
import { emitTrace } from "../trace.js";
|
|
@@ -120,7 +120,8 @@ export async function runTerminalAdoption(input) {
|
|
|
120
120
|
minTokens: rs.counters.compactionFloor,
|
|
121
121
|
brain: compactionBrain,
|
|
122
122
|
windowSafety: windowSafetyOptions(prepared.harness.getModel()),
|
|
123
|
-
|
|
123
|
+
passReport: compactionPassReport(queue, ident),
|
|
124
|
+
onCompactionOutcome: (outcome, reason, end) => queue.push({ type: "compaction_outcome", outcome, trigger: "auto", reason, ...end, ...ident() }),
|
|
124
125
|
}, runner, sessions);
|
|
125
126
|
return await next({ threw, comp, answerSettled: settled, owedUserInput });
|
|
126
127
|
}
|
|
@@ -153,7 +154,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
153
154
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
154
155
|
...seamCCompactionOptions(runner, prepared),
|
|
155
156
|
...compactionRestateOptions(prepared),
|
|
156
|
-
...compactionHookOptions(runner, prepared, "auto", opts.callerStop.signal),
|
|
157
|
+
...compactionHookOptions(runner, prepared, "auto", opts.callerStop.signal), onStarted: opts.passReport.onStarted,
|
|
157
158
|
});
|
|
158
159
|
recordCompactionReuse(runner, prepared, finishComp);
|
|
159
160
|
if (finishComp.unevaluableWindow) {
|
|
@@ -170,7 +171,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
170
171
|
reason: "cancelled",
|
|
171
172
|
ts: Date.now(),
|
|
172
173
|
}));
|
|
173
|
-
opts?.onCompactionOutcome?.("mooted", "cancelled");
|
|
174
|
+
opts?.onCompactionOutcome?.("mooted", "cancelled", opts.passReport.end);
|
|
174
175
|
}
|
|
175
176
|
else {
|
|
176
177
|
const failMsg = thrownText(err);
|
|
@@ -183,7 +184,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
183
184
|
reason: failReason,
|
|
184
185
|
ts: Date.now(),
|
|
185
186
|
}));
|
|
186
|
-
opts?.onCompactionOutcome?.("failed", failReason);
|
|
187
|
+
opts?.onCompactionOutcome?.("failed", failReason, opts.passReport.end);
|
|
187
188
|
runner.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
|
|
188
189
|
}
|
|
189
190
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { compactionKindOf } from "../task-notification.js";
|
|
2
2
|
import { mintSystemReminder } from "../reminder-mint.js";
|
|
3
3
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
4
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, mintCompactedEvent, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
4
|
+
import { compactionPassReport, DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, mintCompactedEvent, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
5
5
|
import { emitTrace } from "../trace.js";
|
|
6
6
|
import { redactThenCut } from "../../agents/subagent-steps.js";
|
|
7
7
|
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, TOTAL_TOKENS_REMINDER_DEFAULT_MODE, attachmentEnvelopeTags, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, isIntactSource, rebaseCadenceWindows, renderAgentListingFrame, renderMcpDroppedTools, renderMcpInstructionsDelta, selectMcpDroppedBatch, renderSkillsListingFrame, renderToolsDelta } from "./turn-attachments.js";
|
|
@@ -558,6 +558,7 @@ export function createTurnBoundary(input) {
|
|
|
558
558
|
const passTrigger = trimPressure ? "forced" : forceManual ? "manual" : "auto";
|
|
559
559
|
let manualOutcome = "failed";
|
|
560
560
|
const manualInstructions = claimedManual?.instructions;
|
|
561
|
+
const passReport = compactionPassReport(queue, ident);
|
|
561
562
|
try {
|
|
562
563
|
const comp = await maybeCompact({
|
|
563
564
|
session: prepared.session,
|
|
@@ -587,6 +588,7 @@ export function createTurnBoundary(input) {
|
|
|
587
588
|
...compactionRestateOptions(prepared),
|
|
588
589
|
...windowSafetyOptions(event.model),
|
|
589
590
|
...compactionHookOptions(runner, prepared, passTrigger, compactionPassSignal),
|
|
591
|
+
onStarted: passReport.onStarted,
|
|
590
592
|
});
|
|
591
593
|
if (comp.blocked) {
|
|
592
594
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
@@ -670,7 +672,7 @@ export function createTurnBoundary(input) {
|
|
|
670
672
|
catch (err) {
|
|
671
673
|
if (isCompactionManualCancel(err)) {
|
|
672
674
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "cancelled", ts: Date.now() }));
|
|
673
|
-
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: passTrigger, reason: "cancelled", ...ident() });
|
|
675
|
+
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: passTrigger, reason: "cancelled", ...passReport.end, ...ident() });
|
|
674
676
|
manualOutcome = "mooted";
|
|
675
677
|
}
|
|
676
678
|
else if (!prepared.abortController.signal.aborted) {
|
|
@@ -686,11 +688,15 @@ export function createTurnBoundary(input) {
|
|
|
686
688
|
reason: failReason,
|
|
687
689
|
ts: Date.now(),
|
|
688
690
|
}));
|
|
689
|
-
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: passTrigger, reason: failReason, ...ident() });
|
|
691
|
+
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: passTrigger, reason: failReason, ...passReport.end, ...ident() });
|
|
690
692
|
manualOutcome = "failed";
|
|
691
693
|
}
|
|
692
694
|
else {
|
|
693
695
|
manualOutcome = "mooted";
|
|
696
|
+
if (passReport.pass !== undefined) {
|
|
697
|
+
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "task_ending", ts: Date.now() }));
|
|
698
|
+
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: passTrigger, reason: "task_ending", ...passReport.end, ...ident() });
|
|
699
|
+
}
|
|
694
700
|
}
|
|
695
701
|
}
|
|
696
702
|
if (forceManual)
|
|
@@ -481,7 +481,7 @@ export class Runner {
|
|
|
481
481
|
outcome,
|
|
482
482
|
taskConfig,
|
|
483
483
|
internals,
|
|
484
|
-
runner: this.depsSeat,
|
|
484
|
+
runner: this.depsSeat, sessions: this.sessions,
|
|
485
485
|
parentConstraintRegistry: this.parentConstraintRegistry,
|
|
486
486
|
next: (admitted) => this.resumeLadder(token, outcome, admitted),
|
|
487
487
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE reading of `TaskSpec.skills` — the one place the caller's skill objects are read. The config doors call it before any
|
|
3
|
+
* resource (a resume before its claim) and hand its copy on as the leg's `skills`; the disclosure block and the `Skill` tool read
|
|
4
|
+
* that copy through `normalizeSkills` (synthetic-tools.ts), its sorted view.
|
|
5
|
+
*/
|
|
6
|
+
import type { SkillSpec } from "../agent-definition.js";
|
|
7
|
+
/**
|
|
8
|
+
* Total, and once: a value that is not a list, an entry that is not an object, a `name` / `description` / `content` that is not a
|
|
9
|
+
* string, or `files` that is not a list of `{ path, content }` strings refuses by name (`config.invalid_argument`, `detail.field` =
|
|
10
|
+
* the member's path); `baseDir` by its own reader ({@link skillBaseDirOf}, `config.skill_base_dir_invalid`). Every entry is
|
|
11
|
+
* screened, a later duplicate of a name included. Each field is read ONCE, BY NAME (an inherited or non-enumerable list counts;
|
|
12
|
+
* a rest copy would drop it). What it returns is plain data in declaration order, first of a name: every member and attachment
|
|
13
|
+
* copied, the two tables read ({@link readSkillTable}: a non-list is one entry that reads as nothing).
|
|
14
|
+
*/
|
|
15
|
+
export declare function readSkillsSeat(skills: unknown): SkillSpec[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readSkillTable } from "../skill-grant.js";
|
|
2
|
+
import { assertNoRetiredKeys, RETIRED_TASK_SPEC_KEYS } from "../retired-keys.js";
|
|
3
|
+
import { skillBaseDirOf } from "./skill-base-dir.js";
|
|
4
|
+
const SKILLS_SEAT = "TaskSpec.skills";
|
|
5
|
+
const kindOf = (v) => (v === null ? "null" : Array.isArray(v) ? "an array" : typeof v);
|
|
6
|
+
function skillsSeatRefusal(field, problem) {
|
|
7
|
+
const e = new Error(`${field} ${problem} — an entry the engine cannot read is refused rather than dropped.`);
|
|
8
|
+
e.code = "config.invalid_argument";
|
|
9
|
+
e.detail = { field };
|
|
10
|
+
return e;
|
|
11
|
+
}
|
|
12
|
+
function readSkillFiles(files, at) {
|
|
13
|
+
if (files === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
if (!Array.isArray(files))
|
|
16
|
+
throw skillsSeatRefusal(at, `must be a list of { path, content } attachments (got ${kindOf(files)})`);
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const [j, f] of files.entries()) {
|
|
19
|
+
if (typeof f !== "object" || f === null || Array.isArray(f))
|
|
20
|
+
throw skillsSeatRefusal(`${at}[${j}]`, `must be an attachment object { path, content } (got ${kindOf(f)})`);
|
|
21
|
+
const { path, content } = f;
|
|
22
|
+
if (typeof path !== "string")
|
|
23
|
+
throw skillsSeatRefusal(`${at}[${j}].path`, `must be a string (got ${kindOf(path)})`);
|
|
24
|
+
if (typeof content !== "string")
|
|
25
|
+
throw skillsSeatRefusal(`${at}[${j}].content`, `must be a string (got ${kindOf(content)})`);
|
|
26
|
+
out.push({ path, content });
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
export function readSkillsSeat(skills) {
|
|
31
|
+
if (skills === undefined)
|
|
32
|
+
return [];
|
|
33
|
+
if (!Array.isArray(skills))
|
|
34
|
+
throw skillsSeatRefusal(SKILLS_SEAT, `must be a list (got ${kindOf(skills)})`);
|
|
35
|
+
assertNoRetiredKeys({ skills }, RETIRED_TASK_SPEC_KEYS, "TaskSpec");
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const [i, raw] of skills.entries()) {
|
|
39
|
+
const at = `${SKILLS_SEAT}[${i}]`;
|
|
40
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw))
|
|
41
|
+
throw skillsSeatRefusal(at, `must be an object { name, description, content } (got ${kindOf(raw)})`);
|
|
42
|
+
const { name, description, content, allowedTools, disallowedTools, files: rawFiles, baseDir, ...rest } = raw;
|
|
43
|
+
void rest;
|
|
44
|
+
for (const [member, value] of [["name", name], ["description", description], ["content", content]]) {
|
|
45
|
+
if (typeof value !== "string")
|
|
46
|
+
throw skillsSeatRefusal(`${at}.${member}`, `must be a string (got ${kindOf(value)})`);
|
|
47
|
+
}
|
|
48
|
+
const files = readSkillFiles(rawFiles, `${at}.files`);
|
|
49
|
+
skillBaseDirOf(baseDir);
|
|
50
|
+
if (seen.has(name))
|
|
51
|
+
continue;
|
|
52
|
+
seen.add(name);
|
|
53
|
+
const [allowed, denied] = [readSkillTable(allowedTools), readSkillTable(disallowedTools)];
|
|
54
|
+
out.push({ name, description, content, ...(allowed !== undefined ? { allowedTools: allowed } : {}), ...(denied !== undefined ? { disallowedTools: denied } : {}), ...(files !== undefined ? { files } : {}), ...(baseDir !== undefined ? { baseDir } : {}) });
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
* another spelling of a tool of the universe) — synchronous, like the phase that calls it.
|
|
35
35
|
*/
|
|
36
36
|
import type { AgentTool } from "../../internal/harness.js";
|
|
37
|
+
import { valveLeaves } from "../../agents/tool-filter.js";
|
|
37
38
|
import { type ProtocolId } from "../protocol-table.js";
|
|
38
|
-
import type { ToolLegKind,
|
|
39
|
+
import type { ToolLegKind, ToolSelectorLayer } from "../tool-face.js";
|
|
39
40
|
import { type ConstraintChainEntry } from "../tool-policy.js";
|
|
40
41
|
import type { TaskSpec } from "../types.js";
|
|
41
42
|
import type { CheckpointErrorReason } from "../checkpoint-store.js";
|
|
@@ -65,14 +66,15 @@ export declare function renamedDelegationMountSentence(...rosters: unknown[]): s
|
|
|
65
66
|
*/
|
|
66
67
|
export declare function refuseRenamedDelegationMounts(...rosters: unknown[]): void;
|
|
67
68
|
/**
|
|
68
|
-
* THE reading of what the delegation tools a run mounts hand their children, read after the run's own
|
|
69
|
-
* tool the
|
|
70
|
-
* unlistable pool of it belong to any universe. The tools themselves, every pool tool's name and alias (a declared
|
|
71
|
-
* roster included), and whether every one of those pools can be listed: a delegation tool whose factory injects tools
|
|
72
|
-
* declared roster has no listable pool (the static `tools` it was given included), so a name outside the listed ones
|
|
73
|
-
* own. This is the one place the pool side of both universes is defined; the list judgment below and the policy name
|
|
69
|
+
* THE reading of what the delegation tools a run mounts hand their children, read after the run's own tool valve: only a delegation
|
|
70
|
+
* tool the valve leaves (`leaves`, the valve's one reading — `valveLeaves`) counts — one it takes out hands nobody anything, and neither
|
|
71
|
+
* its pool names nor an unlistable pool of it belong to any universe. The tools themselves, every pool tool's name and alias (a declared
|
|
72
|
+
* `extraToolNames` roster included), and whether every one of those pools can be listed: a delegation tool whose factory injects tools
|
|
73
|
+
* per spawn with no declared roster has no listable pool (the static `tools` it was given included), so a name outside the listed ones
|
|
74
|
+
* may be one of its own. This is the one place the pool side of both universes is defined; the list judgment below and the policy name
|
|
75
|
+
* audit ask it.
|
|
74
76
|
*/
|
|
75
|
-
export declare function delegationPoolReading(callerTools: NonNullable<TaskSpec["tools"]>,
|
|
77
|
+
export declare function delegationPoolReading(callerTools: NonNullable<TaskSpec["tools"]>, leaves: ReturnType<typeof valveLeaves>): DelegationPoolReading;
|
|
76
78
|
/** The arms of this module's `config.tool_face_control` refusal that a resume carries before the claim as `detail.reason` of a `checkpoint.config_refused` — the same words
|
|
77
79
|
* ({@link CheckpointErrorReason}): a list the row recorded that is not a list / has a member that is not a string / has a spelling no tool name can have / is not in the
|
|
78
80
|
* spelling a row records, a record that is not one, a renamed delegation mount, an occupied table of declared names. */
|
|
@@ -123,8 +125,8 @@ export declare function boundOwnToolLists(record: unknown, own: {
|
|
|
123
125
|
readonly exclude: readonly string[] | undefined;
|
|
124
126
|
}): RunInternals["ownToolLists"];
|
|
125
127
|
export interface RunToolListsInput {
|
|
126
|
-
/** borrowed-readonly — the REBOUND spec: `agents` (the task's definitions)
|
|
127
|
-
spec: Pick<TaskSpec, "agents" | "tools">;
|
|
128
|
+
/** borrowed-readonly — the REBOUND spec: `agents` (the task's definitions), `tools` (the caller's tools and the mounted delegation tools) and `excludeAllTools` (the valve's whole-face switch, which takes the delegation tools out too). */
|
|
129
|
+
spec: Pick<TaskSpec, "agents" | "tools" | "excludeAllTools">;
|
|
128
130
|
/** borrowed-readonly — the assembled mount array BEFORE the selector's valve (names + aliases = the roster half of the universe). */
|
|
129
131
|
tools: ReadonlyArray<AgentTool>;
|
|
130
132
|
/** borrowed-readonly — the peers the task declares, per protocol of the protocol table (its MCP servers, its A2A peers): a namespaced
|
|
@@ -170,5 +172,5 @@ export declare function judgeRunToolLists(input: RunToolListsInput): void;
|
|
|
170
172
|
* `detail.reason: "near_miss"`) rather than kept as a deny that hides nothing. A name no tool answers to stays kept (a tool of that
|
|
171
173
|
* name may be mounted later), unannounced: the host wrote no spec this leg could name the list of.
|
|
172
174
|
*/
|
|
173
|
-
export declare function judgeHostToolCeiling(layers: readonly ToolSelectorLayer[], agent: Pick<TaskSpec, "tools" | "allowTools" | "excludeTools">, above?: readonly ToolSelectorLayer[]): void;
|
|
175
|
+
export declare function judgeHostToolCeiling(layers: readonly ToolSelectorLayer[], agent: Pick<TaskSpec, "tools" | "allowTools" | "excludeTools" | "excludeAllTools">, above?: readonly ToolSelectorLayer[]): void;
|
|
174
176
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compileToolSelector, deniedProtocolTools, delegationListWhere, isCanonicalToolName, isToolSelectorLayer, listReaches, notAListSentence, toolListsDigest, notAStringSentence, readAllowedToolList, readDeniedEntry, readDeniedToolList } from "../../agents/tool-filter.js";
|
|
1
|
+
import { compileToolSelector, valveLeaves, deniedProtocolTools, delegationListWhere, isCanonicalToolName, isToolSelectorLayer, listReaches, notAListSentence, toolListsDigest, notAStringSentence, readAllowedToolList, readDeniedEntry, readDeniedToolList } from "../../agents/tool-filter.js";
|
|
2
2
|
import { readToolNameList, valueTypeLabel } from "../tool-name-list.js";
|
|
3
3
|
import { compileAllowList, inertEntryPhrase, renderListEntry, rootFacingToolNames } from "../allow-entry.js";
|
|
4
4
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
@@ -37,8 +37,8 @@ export function refuseRenamedDelegationMounts(...rosters) {
|
|
|
37
37
|
function poolOf(tools) {
|
|
38
38
|
return { tools, names: mountedToolNames([...tools.map((t) => ({ name: t.name })), ...tools.flatMap((t) => t.agentToolPool ?? [])]), complete: tools.every((t) => t.agentToolPool !== undefined) };
|
|
39
39
|
}
|
|
40
|
-
export function delegationPoolReading(callerTools,
|
|
41
|
-
return poolOf(callerTools.filter((t) => t.agentToolFaces !== undefined &&
|
|
40
|
+
export function delegationPoolReading(callerTools, leaves) {
|
|
41
|
+
return poolOf(callerTools.filter((t) => t.agentToolFaces !== undefined && leaves(t)));
|
|
42
42
|
}
|
|
43
43
|
function peersOf(known, declaredPeers) {
|
|
44
44
|
const peers = new Set(PROTOCOL_TABLE.flatMap((ns) => (declaredPeers[ns.id] ?? []).map((p) => mintNamespacePrefix(ns, p.name))));
|
|
@@ -275,7 +275,8 @@ export function judgeRunToolLists(input) {
|
|
|
275
275
|
const by = [...deniedBy(true, ownWhere ?? "this run's own excludeTools"), ...deniedBy(false, "an inherited list (the spawning run's excludeTools, or one it inherits)")];
|
|
276
276
|
throw refused(`This kind of leg (${input.legs.join(", ")}) relies on ${names.join(", ")}, but ${by.join(" and ")} — the leg cannot work without ${one ? "it" : "them"}. Remove the entry from the list that names it.`, "config.tool_face_control", "protocol_denied");
|
|
277
277
|
}
|
|
278
|
-
const
|
|
278
|
+
const selector = compileToolSelector(input.layers, input.legs);
|
|
279
|
+
const pool = delegationPoolReading(spec.tools ?? [], valveLeaves(spec, selector));
|
|
279
280
|
const mounted = mountedToolNames([...tools, ...(spec.tools ?? [])]);
|
|
280
281
|
if (!input.judgedUpstream) {
|
|
281
282
|
const root = rootFacingUniverse(input.ownBy !== undefined ? new Set([...mounted, input.ownBy.toolName]) : mounted, pool, input.declaredPeers);
|
|
@@ -310,7 +311,8 @@ export function judgeHostToolCeiling(layers, agent, above = []) {
|
|
|
310
311
|
layers.forEach((layer, i) => readSelectorLayer(layer, where(i), () => where(i), false));
|
|
311
312
|
const callerTools = agent.tools ?? [];
|
|
312
313
|
const own = { ...(agent.allowTools != null ? { allow: agent.allowTools } : {}), deny: agent.excludeTools ?? [] };
|
|
313
|
-
const
|
|
314
|
+
const selector = compileToolSelector([...above, ...layers, own], ["delegated"]);
|
|
315
|
+
const pool = delegationPoolReading(callerTools, valveLeaves(agent, selector));
|
|
314
316
|
const universe = rootFacingUniverse(mountedToolNames(callerTools), pool, { mcp: undefined, a2a: undefined });
|
|
315
317
|
const unannounced = () => undefined;
|
|
316
318
|
layers.forEach((layer, i) => {
|
|
@@ -110,8 +110,11 @@ export interface SkillListingEntry {
|
|
|
110
110
|
* and the tool, so what the prompt promises is exactly what the tool serves. Each field is READ ONCE, BY NAME, and
|
|
111
111
|
* both consumers see the copy: the gate screens this list while the invoke substitutes from it, so a `get baseDir()`
|
|
112
112
|
* answering those two reads differently passed a screen that measured a value the model never received — and a rest
|
|
113
|
-
* copy would silently drop an INHERITED or non-enumerable list. The two tables are read by {@link readSkillTable}.
|
|
114
|
-
|
|
113
|
+
* copy would silently drop an INHERITED or non-enumerable list. The two tables are read by {@link readSkillTable}.
|
|
114
|
+
*
|
|
115
|
+
* The sorted view of {@link readSkillsSeat} (skills-seat.ts), which holds the seat's one reading — total over what a caller can
|
|
116
|
+
* hand it, and once. */
|
|
117
|
+
export declare function normalizeSkills(skills: unknown): SkillLike[];
|
|
115
118
|
/** [c209-C] The skills listing's lead line — single-sourced so the run loop's durable-resume
|
|
116
119
|
* transcript probe (MED-3② skills arm, runtask) matches EXACTLY what the initial frame carries
|
|
117
120
|
* (probing the bare `<skills>` fence would be more collision-prone in model-quoted text). */
|
|
@@ -5,6 +5,7 @@ import { SKILL_LISTING_DEFAULT_BUDGET, SKILL_LISTING_DESC_MAX_CHARS_DEFAULT } fr
|
|
|
5
5
|
import { readSkillTable, skillNamedBy } from "../skill-grant.js";
|
|
6
6
|
import { toolFace } from "../tool-catalog-entries.js";
|
|
7
7
|
import { substituteSkillRoot, substituteSkillRootBounded } from "./skill-base-dir.js";
|
|
8
|
+
import { readSkillsSeat } from "./skills-seat.js";
|
|
8
9
|
import { foldListingWithin } from "./listing-fold.js";
|
|
9
10
|
export const OUTPUT_TOOL_NAME = "StructuredOutput";
|
|
10
11
|
export const REPORT_BLOCKED_TOOL_NAME = "ReportBlocked";
|
|
@@ -109,18 +110,7 @@ function skillFilesSuffix(files) {
|
|
|
109
110
|
return ` [files: ${shown.join(", ")}${more > 0 ? `, +${more} more` : ""}]`;
|
|
110
111
|
}
|
|
111
112
|
export function normalizeSkills(skills) {
|
|
112
|
-
|
|
113
|
-
const out = [];
|
|
114
|
-
for (const s of skills ?? []) {
|
|
115
|
-
if (!seen.has(s.name)) {
|
|
116
|
-
seen.add(s.name);
|
|
117
|
-
const { name, description, content, allowedTools, disallowedTools, files, baseDir, ...rest } = s;
|
|
118
|
-
void rest;
|
|
119
|
-
const [allowed, denied] = [readSkillTable(allowedTools), readSkillTable(disallowedTools)];
|
|
120
|
-
out.push({ name, description, content, ...(allowed !== undefined ? { allowedTools: allowed } : {}), ...(denied !== undefined ? { disallowedTools: denied } : {}), ...(files !== undefined ? { files } : {}), ...(baseDir !== undefined ? { baseDir } : {}) });
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return out.toSorted((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
113
|
+
return readSkillsSeat(skills).toSorted((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
124
114
|
}
|
|
125
115
|
export const SKILLS_LISTING_PROBE_HEADER = "The following skills are available for this task.";
|
|
126
116
|
export function skillListingLine(s, descMaxChars = SKILL_LISTING_DESC_MAX_CHARS_DEFAULT) {
|
|
@@ -159,10 +159,12 @@ export interface RunnerDeps {
|
|
|
159
159
|
*/
|
|
160
160
|
rosterStore?: import("../agents/roster-store.js").RosterStore;
|
|
161
161
|
/**
|
|
162
|
-
* The center prompt-artifact source (artifact
|
|
162
|
+
* The center prompt-artifact source (artifact v3 only — a v2 body is refused `schema`, INTEGRATION-CORE §12.11).
|
|
163
|
+
* `current()` supplies the verified
|
|
163
164
|
* candidate a top-level session adopts at session start and at a compaction; `get(digest)` resolves a PINNED center
|
|
164
165
|
* layer on resume (a miss is fail-loud `prompt.snapshot_unavailable` — core never recomposes a hybrid from newer
|
|
165
|
-
* content
|
|
166
|
+
* content; a copy the engine refuses is told apart from a missing one, in the failure's sentence and on `onError`);
|
|
167
|
+
* `sourceState()` is the tri-state every adoption decision keys on. What a source hands a run is re-checked
|
|
166
168
|
* before it is adopted. Core bundles `CenterPromptSource` (+ memory/file artifact & source-state stores); a server
|
|
167
169
|
* feeds it published artifacts via `applySnapshot`. Omitted = no center layer.
|
|
168
170
|
*/
|
|
@@ -961,6 +963,19 @@ export interface RunnerDeps {
|
|
|
961
963
|
* everywhere (mode parity, the CC default). Inert without {@link peerDirectory}.
|
|
962
964
|
*/
|
|
963
965
|
crossSessionInbound?: import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers | (() => import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers);
|
|
966
|
+
/**
|
|
967
|
+
* WHO consumes this deployment's principal inbox (`principal.inbox`, where the
|
|
968
|
+
* organization's delivery gate appends cross-principal letters; the mailbox contract's fourth chain).
|
|
969
|
+
* Absent = no consumer, no lane (byte for byte as before); a deployment that mounts a gate DECLARES
|
|
970
|
+
* `{ assistant: "<agent handle>" }` (the scope's assistant is the only consumer; no terminal leg touches the
|
|
971
|
+
* box). `"terminals"` (each terminal drains the box as a second lane) is refused until held decisions are
|
|
972
|
+
* shared across terminals (`detail.reason: "terminals_unsupported"`). Letters are judged on the principal's
|
|
973
|
+
* `crossSessionInbound` layers (managed, user), held when unset. ANY other value fails the prepare of a leg
|
|
974
|
+
* that drains a session box with `config.principal_inbox_consumer_invalid` (`detail.reason: "malformed"`).
|
|
975
|
+
*/
|
|
976
|
+
principalInboxConsumer?: "terminals" | {
|
|
977
|
+
assistant: string;
|
|
978
|
+
};
|
|
964
979
|
/**
|
|
965
980
|
* design/385 §4.4 (slice 4) — this deployment's `dialogExpiry` (CC settings key, verbatim vocabulary
|
|
966
981
|
* `"60s" | "5m" | "10m" | "never"`, default `"5m"`): how long a HELD cross-session message whose cause
|
|
@@ -1286,18 +1301,18 @@ export interface RunnerDeps {
|
|
|
1286
1301
|
* summaries) is invisible without this and lets a session grow until it tops the window.
|
|
1287
1302
|
* - `"prompt-cache"` — a prefix-cache hazard on the wire: a detected cache break (design/31), or a
|
|
1288
1303
|
* mis-declared/mismatched `promptCacheFamily`. The prefix cache will miss. Log/alert on both.
|
|
1289
|
-
* - `"prompt-constitution"` — a prompt overlay did not take effect as written: a later source (or a later
|
|
1290
|
-
*
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1297
|
-
* -
|
|
1298
|
-
*
|
|
1299
|
-
* broadest class by call-site count — most
|
|
1300
|
-
* the task.
|
|
1304
|
+
* - `"prompt-constitution"` — a prompt overlay did not take effect as written: a later source (or a later declaration in the same source) superseded
|
|
1305
|
+
* it on the same target, its target was not in this run's prompt, or a mods handler's `prompt.compose` answer was skipped (it rewrote another's
|
|
1306
|
+
* overlay, or wrote what the band table does not grant its seat); each overlay is named once per run. The center prompt too: a pinned session's
|
|
1307
|
+
* stored artifact refused or unreadable (it fails `prompt.snapshot_unavailable`), a fresh session's candidate that does not verify, and a
|
|
1308
|
+
* `CenterPromptSource` boot restore with no usable copy (`sessionId: ""`, `classification: "center-source-restore"`) — that last line only when the
|
|
1309
|
+
* host passes this same sink to `CenterPromptSource` as its own `onError` (the source restores at boot, apart from any Runner; given none, it is silent).
|
|
1310
|
+
* - `"degraded"` — the task kept running with a capability quietly reduced: a question auto-answered with no human
|
|
1311
|
+
* present, a skipped env git snapshot, a lost checkpoint-put confirmation, an unroutable descendant notification, and
|
|
1312
|
+
* similar best-effort arms. The run proceeds; the reduced arm is what is being disclosed (`classification` names it).
|
|
1313
|
+
* - `"config"` — a deployment-wiring problem detected while preparing or tearing down a task: tool mount conflicts,
|
|
1314
|
+
* refused/ignored knob values, gate-off notices, store/env teardown legs. The broadest class by call-site count — most
|
|
1315
|
+
* misconfigurations announce here rather than failing the task.
|
|
1301
1316
|
* - `"memory"` — a post-task memory-consolidation pass failed or skipped a malformed reconcile
|
|
1302
1317
|
* decision (design/41). Best-effort: the notes the model saved are kept; the task still succeeds.
|
|
1303
1318
|
* - `"mcp"` — an MCP server failed to connect / list its tools and was skipped (fail-open, design/29);
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* chain); the real isolation boundary is the executionEnv, never these rules.
|
|
28
28
|
*/
|
|
29
29
|
import { compileAllowList, type AllowEntryInert, type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
|
|
30
|
+
import type { EngineNotice } from "./engine-notice.js";
|
|
30
31
|
/** The per-session rules. All optional; an absent bucket imposes no constraint of that kind. */
|
|
31
32
|
export interface SessionPermissionRules {
|
|
32
33
|
/** If set, ONLY the calls these entries admit are allowed — a narrowing allowlist: a call no entry admits is refused.
|
|
@@ -244,6 +245,24 @@ export declare function compileSessionRuleBucket(rules: SessionRulesInGrammar, f
|
|
|
244
245
|
* to make a parenthesized one the rule it now spells, or (`*`, which is no allow rule in any grammar) to leave it out.
|
|
245
246
|
*/
|
|
246
247
|
export declare function earlierGrammarAdvisory(sessionId: string, entries: readonly string[], field: SessionRuleEntryField, grammar: number): string;
|
|
248
|
+
/** The reader's word for one entry an earlier grammar reads as nothing: `no_rule` — this engine's rule grammar reads it as no
|
|
249
|
+
* rule; `named_no_tool` — a grammar-0 allow entry the bare-name comparison of its day read as naming no tool. */
|
|
250
|
+
export type EarlierGrammarReader = "no_rule" | "named_no_tool";
|
|
251
|
+
/**
|
|
252
|
+
* The notice for the entries of one bucket a stored row's earlier grammar reads as nothing (`config.session_rules_earlier_grammar`,
|
|
253
|
+
* audience `user` — the session's rules are what the person's calls are judged by): the message is {@link earlierGrammarAdvisory}
|
|
254
|
+
* verbatim, and `detail` carries the same facts for a machine — `bucket` (the field), `storedGrammar` (the row's), `entries` (every
|
|
255
|
+
* entry, each as `noticeRuleText` hands rule text to a notice, with its reader's word), `rewrite` (the way out: an operator write of
|
|
256
|
+
* a new value without the stored row's rev, which stamps `stampsGrammar`), `rulesSessionId` (whose rules — an ancestor's on a
|
|
257
|
+
* delegated child's inherited layer) and `sessionId` (the run that read them).
|
|
258
|
+
*/
|
|
259
|
+
export declare function earlierGrammarNotice(input: {
|
|
260
|
+
rulesSessionId: string;
|
|
261
|
+
sessionId: string;
|
|
262
|
+
entries: readonly string[];
|
|
263
|
+
field: SessionRuleEntryField;
|
|
264
|
+
grammar: number;
|
|
265
|
+
}): EngineNotice;
|
|
247
266
|
/** The clause a run-time refusal carries when the allowlist it judged by holds entries its earlier grammar reads as nothing
|
|
248
267
|
* ({@link compileSessionRuleBucket}; `grammar` = the row's): the grammar the rules are stored in, then which entries name no tool
|
|
249
268
|
* there (grammar 0: `*` by its spelling, the parenthesized ones counted) and how many this engine's grammar reads as no rule —
|
|
@@ -4,6 +4,7 @@ import { NAMESPACED_NAME_SHAPES } from "./protocol-table.js";
|
|
|
4
4
|
import { callerToolNameProblem } from "./caller-tool-name.js";
|
|
5
5
|
import { compileAllowList, compiledEntryName, inertEntryPhrase, readRuleEntry, renderListEntry, rootFacingToolNames } from "./allow-entry.js";
|
|
6
6
|
import { valueTypeLabel } from "./tool-name-list.js";
|
|
7
|
+
import { noticeRuleText } from "./permission-rule-model.js";
|
|
7
8
|
const SESSION_RULE_BUCKETS = { toolAllow: "allow", toolDeny: "deny", allowDirs: "dirs" };
|
|
8
9
|
const BUCKETS = Object.keys(SESSION_RULE_BUCKETS);
|
|
9
10
|
const ENTRY_FIELDS = BUCKETS.filter((f) => SESSION_RULE_BUCKETS[f] !== "dirs");
|
|
@@ -162,11 +163,30 @@ function shownEntries(entries, render) {
|
|
|
162
163
|
}
|
|
163
164
|
const REWRITE_THROUGH = `through this engine (an operator write of a new value without the stored row's rev; the write stamps grammar ${SESSION_RULES_GRAMMAR})`;
|
|
164
165
|
export function earlierGrammarAdvisory(sessionId, entries, field, grammar) {
|
|
165
|
-
const
|
|
166
|
-
const
|
|
166
|
+
const read = earlierGrammarReadings(entries, field);
|
|
167
|
+
const noRule = read.filter((r) => r.reader === "no_rule").map((r) => r.entry);
|
|
168
|
+
const namedNoTool = read.filter((r) => r.reader === "named_no_tool").map((r) => r.entry);
|
|
167
169
|
const sentences = [...(namedNoTool.length > 0 ? [namedNoToolInGrammar0(namedNoTool)] : []), ...(noRule.length > 0 ? [noRuleInThisGrammar(noRule, field, grammar)] : [])];
|
|
168
170
|
return `session permission rules of session ${JSON.stringify(sessionId)}: ${sentences.join(" ")}`;
|
|
169
171
|
}
|
|
172
|
+
function earlierGrammarReadings(entries, field) {
|
|
173
|
+
return entries.map((entry) => ({ entry, reader: readsAsNoRule(entry, field) ? "no_rule" : "named_no_tool" }));
|
|
174
|
+
}
|
|
175
|
+
export function earlierGrammarNotice(input) {
|
|
176
|
+
const { rulesSessionId, sessionId, entries, field, grammar } = input;
|
|
177
|
+
return {
|
|
178
|
+
code: "config.session_rules_earlier_grammar",
|
|
179
|
+
message: earlierGrammarAdvisory(rulesSessionId, entries, field, grammar),
|
|
180
|
+
detail: {
|
|
181
|
+
bucket: field,
|
|
182
|
+
storedGrammar: grammar,
|
|
183
|
+
entries: earlierGrammarReadings(entries, field).map((r) => ({ entry: noticeRuleText(r.entry), reader: r.reader })),
|
|
184
|
+
rewrite: { by: "operator_write_without_rev", stampsGrammar: SESSION_RULES_GRAMMAR },
|
|
185
|
+
rulesSessionId,
|
|
186
|
+
sessionId,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
170
190
|
function namedNoToolInGrammar0(entries) {
|
|
171
191
|
const one = entries.length === 1;
|
|
172
192
|
const wildcard = entries.some(readsAsWildcard);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { strict as assert } from "node:assert";
|
|
2
2
|
import { MAILBOX_INVALID_PEER_META_CODE, MAILBOX_TOMBSTONED_RECIPIENT_CODE } from "../mailbox-store.js";
|
|
3
3
|
import { beginContract } from "./contract-harness.js";
|
|
4
|
+
import { PRINCIPAL_INBOX_HANDLE } from "../../agents/cross-session-ref.js";
|
|
5
|
+
import { mintCrossPrincipalRecord } from "../../agents/org-delivery-gate.js";
|
|
4
6
|
export const MAILBOX_CONTRACT_SCOPE = "default";
|
|
5
7
|
const msg = (content, sentAt = 1000) => ({ content, sentAt });
|
|
6
8
|
async function withStores(mk, fn) {
|
|
@@ -259,6 +261,17 @@ export async function mailboxBundledOnlyContract(mk, runAssertion) {
|
|
|
259
261
|
await s.drop("tenant-b", "shared-handle");
|
|
260
262
|
assert.equal(await s.peekCount("tenant-b", "shared-handle"), 0);
|
|
261
263
|
});
|
|
264
|
+
run("第四条消费链(principal 收件箱):投递门铸的信落收件人 scope 的 principal.inbox,两只信任锚逐字节回读,发信方 scope 零残留", async (make) => {
|
|
265
|
+
const s = make();
|
|
266
|
+
const letter = mintCrossPrincipalRecord({ from: { scope: "principal-a", name: "alice" }, body: { content: "letter" }, receiptId: "gr-1", sentAt: 10_000, consumer: { kind: "terminals" } });
|
|
267
|
+
assert.equal(await s.append("principal-b", PRINCIPAL_INBOX_HANDLE, letter), 1);
|
|
268
|
+
assert.equal(await s.peekCount("principal-a", PRINCIPAL_INBOX_HANDLE), 0, "发信方 scope 的同名箱不得出现这封信");
|
|
269
|
+
const lease = await s.claimLease("principal-b", PRINCIPAL_INBOX_HANDLE, "drain:r1", 60_000, 10_000);
|
|
270
|
+
assert.deepEqual(lease.messages.map((m) => m.peerMeta), [{ kind: "peer_message", fromScope: "principal-a", gateReceiptId: "gr-1" }]);
|
|
271
|
+
assert.equal(lease.messages[0].from, "alice");
|
|
272
|
+
assert.deepEqual(await s.ack("principal-b", PRINCIPAL_INBOX_HANDLE, "drain:r1", lease.maxSeq), { acked: true });
|
|
273
|
+
assert.equal(await s.peekCount("principal-b", PRINCIPAL_INBOX_HANDLE), 0);
|
|
274
|
+
});
|
|
262
275
|
await settle();
|
|
263
276
|
}
|
|
264
277
|
export async function mailboxCrossProcessContract(hooks) {
|
|
@@ -677,6 +677,10 @@ export type TaskEvent = ({
|
|
|
677
677
|
* pre-compaction context size. CC parity = `SDKCompactBoundary{trigger, pre_tokens, preserved_segment}`. */
|
|
678
678
|
type: "compacted";
|
|
679
679
|
trigger: "auto" | "manual" | "forced";
|
|
680
|
+
/** The pass this frame CONCLUDES — the value the pass's `compaction_started` frame carried. A landing is
|
|
681
|
+
* always preceded by its start, so the engine sets it on every `compacted` frame; it is optional in the
|
|
682
|
+
* type only so a literal a host builds by hand stays valid. */
|
|
683
|
+
pass?: string;
|
|
680
684
|
tokensBefore: number;
|
|
681
685
|
/** How much context this compaction FREED, as the engine measured it: one structural sum over
|
|
682
686
|
* the pre-messages minus the same sum over the post-messages, inside the pass. **The only
|
|
@@ -763,8 +767,9 @@ export type TaskEvent = ({
|
|
|
763
767
|
* - `"mooted"` — the pass did not run to its end: a manual request no boundary could serve
|
|
764
768
|
* (`trigger:"manual"`, `reason:"task_ending"`), or a request withdrawn / a pass cut in flight by its
|
|
765
769
|
* caller — a `compact({ signal })` withdrawal, `halt()`, `interrupt()`, `destroy()` or the task's own
|
|
766
|
-
* `signal` (`reason:"cancelled"`), on any trigger: `"auto"`, `"manual"` or `"forced"
|
|
767
|
-
*
|
|
770
|
+
* `signal` (`reason:"cancelled"`), on any trigger: `"auto"`, `"manual"` or `"forced"`, or a turn-boundary
|
|
771
|
+
* pass in flight that the task's own end cut — a limit, a timeout (`reason:"task_ending"`, with the
|
|
772
|
+
* `pass` the start announced). Read `"mooted"` with its `trigger` — it is not a manual-only outcome.
|
|
768
773
|
*/
|
|
769
774
|
type: "compaction_outcome";
|
|
770
775
|
outcome: Exclude<CompactOutcome, "compacted"> | "suppressed";
|
|
@@ -772,6 +777,37 @@ export type TaskEvent = ({
|
|
|
772
777
|
trigger: "auto" | "manual" | "forced";
|
|
773
778
|
/** Optional human-readable detail (mirrors the trace frame's reason where one exists). */
|
|
774
779
|
reason?: string;
|
|
780
|
+
/** The pass this frame CONCLUDES — the value its `compaction_started` frame carried. Present exactly when a
|
|
781
|
+
* start was announced for the pass this frame ends, which makes it `failed` or `mooted` (a pass that ran its
|
|
782
|
+
* summary call and did not land, or was cut while it ran). Absent on every outcome of a pass that never
|
|
783
|
+
* started — `blocked`, `suppressed`, `disabled`, `noop`, a `mooted` request no boundary served — and on the
|
|
784
|
+
* rapid-refill `disabled` frame, which reports a state the landing before it caused, not that pass's end. */
|
|
785
|
+
pass?: string;
|
|
786
|
+
} & TaskEventIdentity) | ({
|
|
787
|
+
/**
|
|
788
|
+
* A compaction pass is under way: the engine has committed to it and is about to produce its summary.
|
|
789
|
+
* Until now compaction was announced only when it ended (`compacted`, or `compaction_outcome` for a pass
|
|
790
|
+
* that did not land), so a consumer watching a summary call wait out a provider's retries had nothing to
|
|
791
|
+
* show for the minutes in between.
|
|
792
|
+
*
|
|
793
|
+
* Minted at the one place a pass commits — after the trigger gate, a valid cut point and the
|
|
794
|
+
* `preCompact` hook (a blocking hook ends the pass before it starts), before the summary provider is
|
|
795
|
+
* consulted or the model is called — by every lane that compacts: the turn boundary (`"auto"`, `"manual"`,
|
|
796
|
+
* and `"forced"` under trim pressure), the prompt-too-long recovery and guard-chain arm B (`"forced"`), and
|
|
797
|
+
* the end-of-task summary (`"auto"`). Retries inside the pass's model call never mint a second one.
|
|
798
|
+
*
|
|
799
|
+
* Pairing: exactly one frame concludes each started pass, carrying the same `pass` — `compacted` when it
|
|
800
|
+
* lands, `compaction_outcome{failed | mooted}` when it does not. A pass that never reaches this point
|
|
801
|
+
* (`blocked`, `suppressed`, `disabled`, `noop`) announces nothing here and its outcome carries no `pass`.
|
|
802
|
+
* `pass` is unique across runs, so a consumer multiplexing several tasks on one stream pairs on `pass` alone.
|
|
803
|
+
*/
|
|
804
|
+
type: "compaction_started";
|
|
805
|
+
/** Same three-state vocabulary (and the same value) the pass hands the `preCompact` hook and its concluding frame. */
|
|
806
|
+
trigger: "auto" | "manual" | "forced";
|
|
807
|
+
/** The pass's identity: an opaque engine-minted id (uuidv7), carried by the frame that concludes the pass. */
|
|
808
|
+
pass: string;
|
|
809
|
+
/** The engine clock (epoch ms) at the moment the pass committed. */
|
|
810
|
+
startedAt: number;
|
|
775
811
|
} & TaskEventIdentity) | ({
|
|
776
812
|
/**
|
|
777
813
|
* TB telemetry B3: the engine injected a SYSTEM steering message (a deadline nudge
|
|
@@ -2,7 +2,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
2
2
|
import { escapeEnvelopeTag, inlineUntrusted, sanitizeUntrustedText } from "./untrusted-text.js";
|
|
3
3
|
import { egressText } from "./untrusted-egress.js";
|
|
4
4
|
import { PEER_MESSAGE_NOTICE } from "../agents/peer-admission.js";
|
|
5
|
-
import { buildCrossSessionEnvelope, CROSS_SESSION_MESSAGE_NOTICE, neutralizePeerBody } from "../agents/cross-session-envelope.js";
|
|
5
|
+
import { buildCrossSessionEnvelope, CROSS_PRINCIPAL_MESSAGE_NOTICE, CROSS_SESSION_MESSAGE_NOTICE, neutralizePeerBody } from "../agents/cross-session-envelope.js";
|
|
6
6
|
export const TASK_NOTIFICATION_TERMINAL_STATUSES = Object.freeze(["completed", "failed", "killed", "cancelled"]);
|
|
7
7
|
export const NOTIFICATION_INJECTION_OUTCOMES = Object.freeze(["queued", "parked", "parked_durably", "dropped_duplicate"]);
|
|
8
8
|
export function isNotificationInjectionOutcome(value) {
|
|
@@ -88,7 +88,7 @@ export function renderAgentMessageFrame(frame) {
|
|
|
88
88
|
}
|
|
89
89
|
export function renderCrossSessionMessageFrame(frame) {
|
|
90
90
|
const { body, ...fields } = frame;
|
|
91
|
-
return `${buildCrossSessionEnvelope(fields, body)}\n\n${CROSS_SESSION_MESSAGE_NOTICE}`;
|
|
91
|
+
return `${buildCrossSessionEnvelope(fields, body)}\n\n${fields.fromScope !== undefined ? CROSS_PRINCIPAL_MESSAGE_NOTICE : CROSS_SESSION_MESSAGE_NOTICE}`;
|
|
92
92
|
}
|
|
93
93
|
export function renderCrossSessionNoticeLine(notice) {
|
|
94
94
|
return sanitizeUntrustedText(notice.text);
|
package/dist/core/task-spec.d.ts
CHANGED
|
@@ -307,7 +307,7 @@ export interface TaskSpec {
|
|
|
307
307
|
* `appendSystemPrompt` stay. Absent ⇒ standard. Rides the parent spec seat to every engine-built child (a clamp-table
|
|
308
308
|
* row: no child authors the standard form back). A value outside the closed set is refused at prepare. */
|
|
309
309
|
promptForm?: "minimal";
|
|
310
|
-
/** The deployment's
|
|
310
|
+
/** The deployment's binding (a scenario name): a prompt selector value, read by every overlay's `when.binding` (center, deploy and task alike); rides the parent spec seat; out of grammar (non-blank, ≤ 128 Unicode code points — `PROMPT_BINDING_MAX_CHARS`, counted `[...s].length`, not UTF-16 units) ⇒ `config.prompt_selector_invalid`. */
|
|
311
311
|
promptBinding?: string;
|
|
312
312
|
/**
|
|
313
313
|
* APPEND a caller block to the assembled system prompt, keeping the engine's base + harness/safety blocks
|
|
@@ -1879,6 +1879,9 @@ export declare function ownArgs<T>(v: T, root?: string): {
|
|
|
1879
1879
|
reason: string;
|
|
1880
1880
|
cause?: unknown;
|
|
1881
1881
|
};
|
|
1882
|
+
/** A call's argument reading (the loop's: the tool's preparation, the schema's coercions) whose every value is engine-owned
|
|
1883
|
+
* like a pass's args ({@link ownArgs}) — the gate's face seat and the resumed call's belt seat read through this one. */
|
|
1884
|
+
export declare function ownedReading(read: (args: unknown) => unknown): (args: unknown) => unknown;
|
|
1882
1885
|
/**
|
|
1883
1886
|
* A label for a value that was THROWN at us, safe to put in a fail-closed refusal. Never coerces: `String(x)`
|
|
1884
1887
|
* throws on a null-prototype object or a untrusted `toString`/`Symbol.toPrimitive`, and the thrower here is
|