@sema-agent/core 7.49.5 → 7.50.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 +138 -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 +19 -13
- package/dist/core/gate-lanes.js +12 -11
- 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/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-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 +6 -7
- 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 +28 -15
- package/dist/tools/fs/shell-read-device.d.ts +1 -1
- 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/dist/core/runner/persisted-path-rows.d.ts +0 -88
- package/dist/core/runner/persisted-path-rows.js +0 -108
|
@@ -6,6 +6,8 @@ import { normalizePersistedRuleHit } from "../rule-lane-reads.js";
|
|
|
6
6
|
import { decidersNote, SETTLEMENT_NOBODY_JUDGED } from "../gate-outcome.js";
|
|
7
7
|
import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
8
8
|
import { buildAppliedHumanInputEvent, buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
|
|
9
|
+
import { humanInputAttachmentsOf, readObjectiveAttachments, userAttachmentDeclarations } from "../user-attachment.js";
|
|
10
|
+
import { uuidv7 } from "../../internal/harness.js";
|
|
9
11
|
import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
|
|
10
12
|
import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
|
|
11
13
|
import { parkedSkillApprovalRefusal, skillRowRefusal } from "../skill-grant.js";
|
|
@@ -13,7 +15,8 @@ import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
|
|
|
13
15
|
import { computeCostMicroUsd } from "../pricing.js";
|
|
14
16
|
import { mintSystemReminder } from "../reminder-mint.js";
|
|
15
17
|
import { hasDestroy } from "../remote-env.js";
|
|
16
|
-
import { advertisedWritableRootsSeat, exemptRootsSeat, humanRefusalSentence, policyCannotAnswer, policyRefusalMessage, refuseOutOfContractDecision } from "../tool-policy.js";
|
|
18
|
+
import { advertisedWritableRootsSeat, attestationOf, exemptRootsSeat, humanRefusalSentence, policyCannotAnswer, policyRefusalMessage, refuseOutOfContractDecision } from "../tool-policy.js";
|
|
19
|
+
import { pathDenialCardOf } from "../../tools/fs/safety.js";
|
|
17
20
|
import { callFaceSeat, judgeParkedToolIdentity, parkedRowLiveFace, rosterEntryNamed } from "../tool-roster.js";
|
|
18
21
|
import { deliverEngineNotice } from "../types.js";
|
|
19
22
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
@@ -288,7 +291,7 @@ export async function runLeg(input) {
|
|
|
288
291
|
const userPromptSubmit = (spec.hooks ?? runner.deps.hooks)?.userPromptSubmit;
|
|
289
292
|
if (userPromptSubmit) {
|
|
290
293
|
try {
|
|
291
|
-
const promptSeat = await runHookSeat("userPromptSubmit", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => userPromptSubmit(spec.objective, { identity: prepared.hookIdentity, permissionMode: prepared.permissionMode.observed, signal: sig, source: "objective", ...(objectiveActor !== undefined ? { actor: snapshotActorAssertion(objectiveActor) } : {}) }));
|
|
294
|
+
const promptSeat = await runHookSeat("userPromptSubmit", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => userPromptSubmit(spec.objective, { identity: prepared.hookIdentity, permissionMode: prepared.permissionMode.observed, signal: sig, source: "objective", ...(objectiveActor !== undefined ? { actor: snapshotActorAssertion(objectiveActor) } : {}), ...(prepared.userAttachments !== undefined ? { userAttachments: userAttachmentDeclarations(prepared.userAttachments) } : {}) }));
|
|
292
295
|
if (promptSeat.expired) {
|
|
293
296
|
if (promptSeat.cause === "timeout") {
|
|
294
297
|
try {
|
|
@@ -326,6 +329,8 @@ export async function runLeg(input) {
|
|
|
326
329
|
promptBlocked = true;
|
|
327
330
|
}
|
|
328
331
|
}
|
|
332
|
+
const objectiveInputId = uuidv7();
|
|
333
|
+
const { frames: attachmentFrames, chars: attachmentChars } = await readObjectiveAttachments(prepared, objectiveInputId, promptBlocked);
|
|
329
334
|
let gitLegDelivered;
|
|
330
335
|
if (!promptBlocked) {
|
|
331
336
|
const gitBody = await resolveGitLegDelivery(prepared, undefined, (err) => {
|
|
@@ -401,14 +406,14 @@ export async function runLeg(input) {
|
|
|
401
406
|
const precallMicroUsd = rs.budget.maxCostMicroUsd === undefined
|
|
402
407
|
? 0
|
|
403
408
|
: computeCostMicroUsd({
|
|
404
|
-
totalInputTokens: Math.ceil((effectiveObjective.length + gitQueuedChars) / 4),
|
|
409
|
+
totalInputTokens: Math.ceil((effectiveObjective.length + gitQueuedChars + attachmentChars) / 4),
|
|
405
410
|
cacheReadTokens: 0,
|
|
406
411
|
cacheWriteTokens: 0,
|
|
407
412
|
cacheWriteTokensLong: 0,
|
|
408
413
|
outputTokens: prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS,
|
|
409
414
|
}, rs.telemetry.pricing);
|
|
410
415
|
const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? rs.budget.remainingMicroUsd : rs.budget.maxCostMicroUsd;
|
|
411
|
-
const precallTokens = Math.ceil((effectiveObjective.length + gitQueuedChars) / 4) + (prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS);
|
|
416
|
+
const precallTokens = Math.ceil((effectiveObjective.length + gitQueuedChars + attachmentChars) / 4) + (prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS);
|
|
412
417
|
const precallCeilingTokens = prepared.suspendForResource !== undefined ? rs.budget.remainingTokens : rs.budget.maxTokensWindow;
|
|
413
418
|
const entryUsageRetryAfterMs = prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
|
|
414
419
|
if (promptBlocked) {
|
|
@@ -470,21 +475,25 @@ export async function runLeg(input) {
|
|
|
470
475
|
}
|
|
471
476
|
const enginePrefixChars = effectiveObjective.length - projectedObjective.length;
|
|
472
477
|
if (!prepared.abortController.signal.aborted) {
|
|
478
|
+
prepared.attachmentDelivery.stage(objectiveInputId, attachmentFrames);
|
|
473
479
|
queue.push({
|
|
474
480
|
...buildHumanInputEvent({
|
|
475
481
|
carrier: "objective",
|
|
476
482
|
source: "objective",
|
|
477
483
|
delivery: "applied",
|
|
484
|
+
inputId: objectiveInputId,
|
|
478
485
|
sessionSeq: nextHumanInputSeq(prepared.harness),
|
|
479
486
|
...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
|
|
480
487
|
...(objectiveActor?.issuer !== undefined ? { issuer: objectiveActor.issuer } : {}),
|
|
481
488
|
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
489
|
+
...(attachmentFrames.length > 0 ? { attachments: humanInputAttachmentsOf(attachmentFrames) } : {}),
|
|
482
490
|
}),
|
|
483
491
|
...ident(),
|
|
484
492
|
});
|
|
485
493
|
final = await withBrainSinks(() => prepared.harness.prompt(effectiveObjective, {
|
|
486
494
|
...(images && images.length > 0 ? { images } : {}),
|
|
487
495
|
...(enginePrefixChars > 0 ? { enginePrefixChars } : {}),
|
|
496
|
+
...(attachmentFrames.length > 0 ? { trailing: attachmentFrames.map((f) => f.seed) } : {}),
|
|
488
497
|
...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
|
|
489
498
|
}));
|
|
490
499
|
await flushGitMirror();
|
|
@@ -579,7 +588,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
579
588
|
const settleEnd = async (isError, result, refusedBy) => {
|
|
580
589
|
if (refusedBy !== undefined)
|
|
581
590
|
occurrences.refuse(pendingAction.toolCallId, refusedBy);
|
|
582
|
-
const gate = emitSettled(occurrences, callStart, { result, isError }, { push: emit, structured: prepared.structuredProjector }).gate ?? decidedGate;
|
|
591
|
+
const gate = emitSettled(occurrences, callStart, { result, isError }, { push: emit, structured: prepared.structuredProjector, mediaByteCapBytes: runner.deps.mediaByteCapBytes }).gate ?? decidedGate;
|
|
583
592
|
const word = await resume.recordExecutionOutcome(gate);
|
|
584
593
|
settleExecutionRecord(runner.deps.onNotice, word, { toolName: pendingAction.toolName, gate, sessionId: prepared.sessionId, runId: prepared.runId, scope: resume.cp.scope, ...(resume.cp.checkpointId !== undefined ? { checkpointId: resume.cp.checkpointId } : {}) });
|
|
585
594
|
};
|
|
@@ -627,6 +636,10 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
627
636
|
resourceSpentMicroUsd: prepared.resourceLedger?.spentMicroUsd ?? 0,
|
|
628
637
|
suspendCount: resume.cp.suspendCount ?? 0,
|
|
629
638
|
};
|
|
639
|
+
const denialCard = (d) => {
|
|
640
|
+
const card = pathDenialCardOf(attestationOf(d, { toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName })?.pathDenial);
|
|
641
|
+
return card !== undefined ? { details: card } : {};
|
|
642
|
+
};
|
|
630
643
|
const beltStamped = prepared.stampCallTarget?.(beltReq(), prepared.abortController.signal);
|
|
631
644
|
if (beltStamped !== undefined)
|
|
632
645
|
beltReading = (await beltStamped).targetReading;
|
|
@@ -643,7 +656,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
643
656
|
? " (the call as it would run requires a person's approval, and this decision was the deployment's policy)"
|
|
644
657
|
: " (the policy rewrote these bytes to a value that was not approved)";
|
|
645
658
|
const editedDenial = formatHookFeedback(`${outcome.updatedInput !== undefined ? "The approver EDITED this call's input; the edited call" : "A PreToolUse hook REWROTE this approved call's input; the rewritten call"} is refused by the deployment's tool policy and was not executed${why}.`, prepared.reminderMark);
|
|
646
|
-
await settleEnd(true, { content: editedDenial }, "policy");
|
|
659
|
+
await settleEnd(true, { content: editedDenial, ...denialCard(rechecked) }, "policy");
|
|
647
660
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
|
|
648
661
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
649
662
|
return;
|
|
@@ -697,7 +710,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
697
710
|
const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check(beltReq(), prepared.abortController.signal));
|
|
698
711
|
if (narrowed.action === "deny") {
|
|
699
712
|
const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by ${ruleSourcePhrase("narrowing")} and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`, prepared.reminderMark);
|
|
700
|
-
await settleEnd(true, { content: narrowedDenial }, "policy");
|
|
713
|
+
await settleEnd(true, { content: narrowedDenial, ...denialCard(narrowed) }, "policy");
|
|
701
714
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, narrowedDenial, true));
|
|
702
715
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
703
716
|
return;
|
|
@@ -721,7 +734,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
721
734
|
laneRead = Promise.resolve(unreadable);
|
|
722
735
|
}
|
|
723
736
|
const read = await settleOrgVerdictWithin(laneRead, unreadable, { signal: prepared.abortController.signal, timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS, onFallback: (cause) => void (laneWaitCancelled = cause === "aborted") });
|
|
724
|
-
const tightened = applyPersistedTightening({ action: "allow" }, read);
|
|
737
|
+
const tightened = applyPersistedTightening({ action: "allow" }, read, beltReq());
|
|
725
738
|
if (laneWaitCancelled || tightened.tightened === "deny" || tightened.tightened === "unreadable") {
|
|
726
739
|
const why = laneWaitCancelled
|
|
727
740
|
? "the task was cancelled before your persisted permission rules could be read"
|
|
@@ -729,7 +742,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
729
742
|
? `a standing deny rule of yours (${disclosedRuleSet(read.hit?.rules ?? [])}) now refuses it`
|
|
730
743
|
: `it could not be read against your standing deny/ask rules (${read.reason})`;
|
|
731
744
|
const ruleDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${why}. This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
|
|
732
|
-
await settleEnd(true, { content: ruleDenial }, "persisted_rule");
|
|
745
|
+
await settleEnd(true, { content: ruleDenial, ...denialCard(tightened.decision) }, "persisted_rule");
|
|
733
746
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, ruleDenial, true));
|
|
734
747
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
735
748
|
return;
|
|
@@ -26,10 +26,6 @@ export interface RunSettleAndTeardownInput {
|
|
|
26
26
|
parentToolCallId?: string;
|
|
27
27
|
sourceTaskId?: string;
|
|
28
28
|
};
|
|
29
|
-
/** borrowed-readonly — the spawning tool call's id when this task runs AS A SUB-AGENT (gates the terminal tick). */
|
|
30
|
-
parentToolCallId: string | undefined;
|
|
31
|
-
/** borrowed-readonly — the sub-agent display name for the terminal tick. */
|
|
32
|
-
subagentName: string | undefined;
|
|
33
29
|
/** borrowed-readonly — the loop-liveness latch: `userHalted` = the halt verb was ACCEPTED (the unconsumed-halt notice's input; it does not sign the seat). */
|
|
34
30
|
loopLatch: {
|
|
35
31
|
readonly userHalted: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
2
|
+
import { emitsChildRowFrames } from "../delegation-frames.js";
|
|
2
3
|
import { isSyntheticApiErrorMessage } from "../../internal/harness.js";
|
|
3
4
|
import { adjudicateDerivedRoute, fallbackToPrimaryNotice, sameRouteIdentity } from "../../brain/route-adjudicator.js";
|
|
4
5
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
@@ -21,11 +22,12 @@ import { generatePromptSuggestions } from "./prompt-suggestions.js";
|
|
|
21
22
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
22
23
|
import { owedDateAnchor } from "./announce-once-ledger.js";
|
|
23
24
|
import { autoRunConsolidationFromRecommendation, autoRunOnRecommendationEffective, scopeHasUnconsumedRecommendation } from "./memory-consolidation-driver.js";
|
|
25
|
+
import { consolidationWriteJudgeOfLeg } from "./memory-consolidation-write-judge.js";
|
|
24
26
|
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
25
27
|
import { mintCompactedEvent } from "../auto-compaction.js";
|
|
26
28
|
import { asThrownError } from "../thrown-value.js";
|
|
27
29
|
export async function runSettleAndTeardown(input) {
|
|
28
|
-
const { spec, queue, prepared, resume, internals, rs, stats, ident,
|
|
30
|
+
const { spec, queue, prepared, resume, internals, rs, stats, ident, loopLatch, timeout, ownCommittedTailRef, emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, userInterruptedLive, answerSettled, strandedHumanAnswers, reasoningResolution, comp, compactionBreaker, owedUserInput, setResult, onSuggestions, onSuspend, runner, sessions } = input;
|
|
29
31
|
try {
|
|
30
32
|
if (comp?.compacted) {
|
|
31
33
|
queue.push(mintCompactedEvent(comp, "auto", ident()));
|
|
@@ -237,7 +239,8 @@ export async function runSettleAndTeardown(input) {
|
|
|
237
239
|
await settleTeardownLeg(() => mes.harvest(), "memoryEngineSession.harvest (settle leg)", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
238
240
|
const scope = mes.handle.writeScope;
|
|
239
241
|
if (scope !== null && autoRunOnRecommendationEffective(runner.deps) && scopeHasUnconsumedRecommendation(mes.engine, scope)) {
|
|
240
|
-
|
|
242
|
+
const writeJudge = consolidationWriteJudgeOfLeg(prepared.syntheticWriteVerdict);
|
|
243
|
+
void autoRunConsolidationFromRecommendation({ deps: runner.deps, engine: mes.engine, scope, sessionId: prepared.sessionId, writeJudge, onNotice: runner.deps.onNotice, onError: runner.deps.onError });
|
|
241
244
|
}
|
|
242
245
|
}
|
|
243
246
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
@@ -262,14 +265,14 @@ export async function runSettleAndTeardown(input) {
|
|
|
262
265
|
ts: Date.now(),
|
|
263
266
|
}));
|
|
264
267
|
setResult(result);
|
|
265
|
-
if (
|
|
268
|
+
if (emitsChildRowFrames(prepared.hookIdentity) && result.terminal.kind !== "paused") {
|
|
266
269
|
const terminalTick = {
|
|
267
270
|
type: "task_progress",
|
|
268
271
|
taskId: rs.telemetry.taskId,
|
|
269
272
|
...(internals?.delegationTaskType !== undefined ? { taskType: internals.delegationTaskType } : {}),
|
|
270
273
|
...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
|
|
271
274
|
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
272
|
-
...(
|
|
275
|
+
...(prepared.hookIdentity.agentName ? { name: prepared.hookIdentity.agentName } : {}),
|
|
273
276
|
...(internals?.explicitAgentName !== undefined ? { spawnName: internals.explicitAgentName } : {}),
|
|
274
277
|
model: prepared.model.id,
|
|
275
278
|
usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
|
|
@@ -78,7 +78,7 @@ export interface RunTerminalAdoptionInput<R> {
|
|
|
78
78
|
abortedLive: boolean;
|
|
79
79
|
/** borrowed-readonly — the leg's snapshot of `loopLatch.userInterrupted` (gates the interruption marker). */
|
|
80
80
|
userInterruptedLive: boolean;
|
|
81
|
-
/** borrowed-readonly — the Runner's deployment deps, read LIVE (`onError`, `checkpointStore`, `onNotice`); the one home of why it is
|
|
81
|
+
/** borrowed-readonly — the Runner's deployment deps, read LIVE (`onError`, `checkpointStore`, `onNotice`, `mediaByteCapBytes`); the one home of why it is
|
|
82
82
|
* a seat and not a captured object is {@link RunnerDepsSeat}. */
|
|
83
83
|
runner: RunnerDepsSeat;
|
|
84
84
|
/** borrowed-readonly — the session store: the end-of-task pass touches the leg's session through it (`finish`'s teardown legs). */
|
|
@@ -43,7 +43,7 @@ export async function runTerminalAdoption(input) {
|
|
|
43
43
|
const report = await reconcileInterruptedSession(prepared.session, prepared.toolEffects, undefined, startedToolCallIds);
|
|
44
44
|
orphansClosed = report.recovered.length;
|
|
45
45
|
for (const orphan of report.recovered) {
|
|
46
|
-
emitSettled(prepared.toolOccurrences, { toolCallId: orphan.toolCallId, spelledName: orphan.toolName, ...(toolLabels.has(orphan.toolName) ? { label: toolLabels.get(orphan.toolName) } : {}), args: orphan.args }, reconciledEnd(orphan), { push: (f) => queue.push({ ...f, ...ident() }), structured: prepared.structuredProjector });
|
|
46
|
+
emitSettled(prepared.toolOccurrences, { toolCallId: orphan.toolCallId, spelledName: orphan.toolName, ...(toolLabels.has(orphan.toolName) ? { label: toolLabels.get(orphan.toolName) } : {}), args: orphan.args }, reconciledEnd(orphan), { push: (f) => queue.push({ ...f, ...ident() }), structured: prepared.structuredProjector, mediaByteCapBytes: runner.deps.mediaByteCapBytes });
|
|
47
47
|
emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
|
|
48
48
|
}
|
|
49
49
|
reconcileComplete = true;
|
|
@@ -10,7 +10,7 @@ import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFi
|
|
|
10
10
|
import { hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
11
11
|
import { sanitizeUntrustedText, SHELLED_BODY_ENVELOPE_TAGS } from "../untrusted-text.js";
|
|
12
12
|
import { droppedToolDigest, withAnnounceDigests } from "./announce-once-ledger.js";
|
|
13
|
-
import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
|
|
13
|
+
import { formatDiagnosticsBlock, withholdListedDiagnostics } from "../lsp-diagnostics.js";
|
|
14
14
|
import { workflowSizeGuidelineChangeNotice } from "../../orchestration/workflow-size-guideline.js";
|
|
15
15
|
import { awaitChargeWithSlowDisclosure, discloseUnevaluableWindow, ENV_DUE_GOVERNANCE_READ_BUDGET_MS, GOVERNANCE_READ_STALLED, LIMIT_APPROACH_DEFAULT_THRESHOLDS, limitApproachFrames, platformLimitTerminal, raceUntilDeadline } from "./clock-and-limits.js";
|
|
16
16
|
import { appendBoundaryFrames, compactionRestateOptions } from "./compaction-restate.js";
|
|
@@ -203,7 +203,7 @@ export function createTurnBoundary(input) {
|
|
|
203
203
|
!prepared.lspDiagnostics.registry.isEmpty() &&
|
|
204
204
|
!prepared.abortController.signal.aborted &&
|
|
205
205
|
prepared.batchHaltRef.current === undefined) {
|
|
206
|
-
const files = prepared.lspDiagnostics.registry.drain(prepared.lspDiagnostics.runIdent);
|
|
206
|
+
const files = await withholdListedDiagnostics(prepared.lspDiagnostics.registry.drain(prepared.lspDiagnostics.runIdent), prepared.lspDiagnostics.withheldOf);
|
|
207
207
|
if (files.length > 0) {
|
|
208
208
|
queue.push({ type: "diagnostics", files, isNew: true, ...ident() });
|
|
209
209
|
const block = mintSystemReminder(formatDiagnosticsBlock(files), prepared.reminderMark);
|
|
@@ -45,7 +45,7 @@ import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
|
|
|
45
45
|
import { streamHaltVerbs } from "./stream-halt-verbs.js";
|
|
46
46
|
import { streamModeVerb } from "./stream-mode-verb.js";
|
|
47
47
|
import { runNotificationLane } from "./run-notification-lane.js";
|
|
48
|
-
import {
|
|
48
|
+
import { retainRunnerDeps } from "./construction-doors.js";
|
|
49
49
|
import { runLeg } from "./run-leg.js";
|
|
50
50
|
import { runTerminalAdoption } from "./run-terminal-adoption.js";
|
|
51
51
|
import { runSettleAndTeardown } from "./run-settle-and-teardown.js";
|
|
@@ -77,10 +77,10 @@ export class Runner {
|
|
|
77
77
|
static PARENT_CONSTRAINT_REGISTRY_CAP = 1024;
|
|
78
78
|
depsSeat;
|
|
79
79
|
constructor(deps) {
|
|
80
|
-
|
|
80
|
+
const retained = retainRunnerDeps(deps);
|
|
81
81
|
const runner = this;
|
|
82
82
|
this.depsSeat = { get deps() { return runner.deps; } };
|
|
83
|
-
this.deps = withDeploymentAllowedTools(
|
|
83
|
+
this.deps = withDeploymentAllowedTools(retained);
|
|
84
84
|
if (this.deps.tiers) {
|
|
85
85
|
this.deps = { ...this.deps, models: expandTiers(this.deps.models, this.deps.tiers) };
|
|
86
86
|
}
|
|
@@ -121,7 +121,7 @@ export class Runner {
|
|
|
121
121
|
if (!this.deps.toolResultStore) {
|
|
122
122
|
this.deps = { ...this.deps, toolResultStore: new RunnerSharedToolResultStore({ maxTotalChars: 64_000_000 }) };
|
|
123
123
|
}
|
|
124
|
-
this.sessions = deps.sessionStore ?? new TtlSessionStore();
|
|
124
|
+
this.sessions = this.deps.sessionStore ?? new TtlSessionStore();
|
|
125
125
|
}
|
|
126
126
|
emitTaskOutcome(outcome) {
|
|
127
127
|
emitTaskOutcome(this.deps, outcome);
|
|
@@ -465,10 +465,10 @@ export class Runner {
|
|
|
465
465
|
const { effectiveTimeoutMs, walltimeMonotonicDeadline, timeout, pushContent, ownCommittedTailRef, emitCommitted } = runClockAndContent({ spec, prepared, queue, internals, rs, ident, parentToolCallId, startedToolCallIds });
|
|
466
466
|
const toolProgress = createToolProgressLane(pushContent, ident, prepared.toolOccurrences);
|
|
467
467
|
prepared.toolProgressRef.current = toolProgress;
|
|
468
|
-
const {
|
|
468
|
+
const { withBrainSinks, withExtraBodyDroppedSink } = runBrainSinks({ queue, internals, rs, ident, observeReasoningWireFacts, notice: { onNotice: this.deps.onNotice, sessionId: prepared.sessionId, runId: prepared.runId } });
|
|
469
469
|
const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
|
|
470
470
|
for (const orphan of prepared.wakeRecovered) {
|
|
471
|
-
emitSettled(prepared.toolOccurrences, { toolCallId: orphan.toolCallId, spelledName: orphan.toolName, ...(toolLabels.has(orphan.toolName) ? { label: toolLabels.get(orphan.toolName) } : {}), args: orphan.args }, reconciledEnd(orphan), { push: (f) => queue.push({ ...f, ...ident() }), structured: prepared.structuredProjector });
|
|
471
|
+
emitSettled(prepared.toolOccurrences, { toolCallId: orphan.toolCallId, spelledName: orphan.toolName, ...(toolLabels.has(orphan.toolName) ? { label: toolLabels.get(orphan.toolName) } : {}), args: orphan.args }, reconciledEnd(orphan), { push: (f) => queue.push({ ...f, ...ident() }), structured: prepared.structuredProjector, mediaByteCapBytes: this.deps.mediaByteCapBytes });
|
|
472
472
|
emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
|
|
473
473
|
}
|
|
474
474
|
const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
|
|
@@ -477,7 +477,7 @@ export class Runner {
|
|
|
477
477
|
const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove } = createHarnessHandlers({
|
|
478
478
|
prepared, stats, rs,
|
|
479
479
|
deps: {
|
|
480
|
-
spec, queue, internals, ident,
|
|
480
|
+
spec, queue, internals, ident, mediaByteCapBytes: this.deps.mediaByteCapBytes,
|
|
481
481
|
pushContent, emitCommitted, startedToolCallIds, toolStartAt, toolProgress, writeFamilyOf,
|
|
482
482
|
toolLabels, postToolBatchHook, batchArgs, internalsNotifier,
|
|
483
483
|
runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info), onNotice: this.deps.onNotice },
|
|
@@ -512,7 +512,7 @@ export class Runner {
|
|
|
512
512
|
spec, queue, prepared, rs, stats, ident, emitCommitted, startedToolCallIds, toolLabels, timeout, compactionBrain, compactionBreaker, windowSafetyOptions,
|
|
513
513
|
undrainedUserAtEnd, userInputLostAtEnd, final, threw, abortedLive, userInterruptedLive, runner: this.depsSeat, sessions: this.sessions,
|
|
514
514
|
next: ({ threw, comp, answerSettled, owedUserInput }) => runSettleAndTeardown({
|
|
515
|
-
spec, queue, prepared, resume, internals, rs, stats, ident,
|
|
515
|
+
spec, queue, prepared, resume, internals, rs, stats, ident, loopLatch, timeout, ownCommittedTailRef,
|
|
516
516
|
emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, userInterruptedLive, answerSettled, strandedHumanAnswers, reasoningResolution, comp, compactionBreaker, owedUserInput,
|
|
517
517
|
setResult, onSuggestions, onSuspend, runner: this.depsSeat, sessions: this.sessions,
|
|
518
518
|
}),
|
|
@@ -31,15 +31,22 @@
|
|
|
31
31
|
* (the model re-reads), which is the pre-existing cost, not a new failure.
|
|
32
32
|
*/
|
|
33
33
|
import type { AgentMessage } from "../../internal/harness.js";
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
import { type UserAttachmentObservation } from "../../internal/llm.js";
|
|
35
|
+
/** What the transcript proves about one file: its whole text at that moment (a tool's card), or the read-state entry an
|
|
36
|
+
* attachment frame's delivery committed (its record's observation, verbatim — hash, view and partial-view flag). */
|
|
37
|
+
export type TranscriptFileRecord = {
|
|
36
38
|
/** The path as the tool recorded it — absolute (see {@link wholeFileRecordsFromTranscript}). */
|
|
37
39
|
path: string;
|
|
38
40
|
/** The file's ENTIRE text at that moment, in the decoded coordinate the fs tools hash. */
|
|
39
41
|
content: string;
|
|
40
|
-
/** The recording
|
|
42
|
+
/** The recording message's own timestamp — the most honest "last read at" available. */
|
|
41
43
|
at: number;
|
|
42
|
-
}
|
|
44
|
+
} | {
|
|
45
|
+
path: string;
|
|
46
|
+
/** The entry the delivery committed. */
|
|
47
|
+
observation: UserAttachmentObservation["entry"];
|
|
48
|
+
at: number;
|
|
49
|
+
};
|
|
43
50
|
/**
|
|
44
51
|
* Per-file, the last whole-file content the transcript proves — in transcript order, last record wins.
|
|
45
52
|
*
|
|
@@ -57,6 +64,9 @@ export interface TranscriptFileRecord {
|
|
|
57
64
|
* the narrowed model-facing union: this reads the branch as the session stores it, and every lane that
|
|
58
65
|
* is not a tool result falls out on the first line of the loop.
|
|
59
66
|
*
|
|
67
|
+
* A user-attachment frame (`UserMessage.attachment` with an observation) proves the read state its delivery committed,
|
|
68
|
+
* keyed by the canonical key it carries — the same entry the live commit wrote, so a rebuilt partial view stays one.
|
|
69
|
+
*
|
|
60
70
|
* Paths are reported only when the tool recorded an ABSOLUTE one. A relative spelling was resolved
|
|
61
71
|
* against the tracked working directory of that moment, which this task cannot reconstruct; re-
|
|
62
72
|
* resolving it here could key the entry to a different file.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { userAttachmentOf } from "../../internal/llm.js";
|
|
1
2
|
import { isAbsoluteForFamily, pathFamilyOf } from "../../tools/fs/safety.js";
|
|
2
3
|
import { applyRecordedEdit } from "../../tools/fs/fs-shared.js";
|
|
3
4
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
@@ -64,6 +65,13 @@ function wholeFileFromReadCard(rest) {
|
|
|
64
65
|
export function wholeFileRecordsFromTranscript(messages) {
|
|
65
66
|
const byPath = new Map();
|
|
66
67
|
for (const m of messages) {
|
|
68
|
+
const attachment = userAttachmentOf(m);
|
|
69
|
+
if (attachment?.observation !== undefined) {
|
|
70
|
+
const { key, entry } = attachment.observation;
|
|
71
|
+
if (isAbsoluteForFamily(pathFamilyOf({ root: key }), key))
|
|
72
|
+
byPath.set(key, { path: key, observation: entry, at: m.timestamp });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
67
75
|
if (m.role !== "toolResult" || m.isError)
|
|
68
76
|
continue;
|
|
69
77
|
const card = cardOf(m.details);
|
|
@@ -13,6 +13,9 @@ export function streamSteerVerb(input) {
|
|
|
13
13
|
const { spec, internals, queue, acceptedSteerInputs, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, steeringError, runner } = input;
|
|
14
14
|
let steerChain = Promise.resolve();
|
|
15
15
|
const steer = async (text, options) => {
|
|
16
|
+
if (options?.userAttachments !== undefined) {
|
|
17
|
+
throw steeringError("a steer carries no attachments — declare them in TaskSpec.userAttachments on the next objective", "steering.invalid_content");
|
|
18
|
+
}
|
|
16
19
|
const trusted = options?.trusted ? true : false;
|
|
17
20
|
if (trusted && sanitizeUntrustedText(text) !== text) {
|
|
18
21
|
throw steeringError("trusted steering text must not contain a </system-reminder> tag", "steering.invalid_content");
|
|
@@ -32,9 +32,8 @@ export declare function batchResponseDigest(result: unknown): string | undefined
|
|
|
32
32
|
* showed an empty body for every durable-approved call. Same projection = same source as the transcript.
|
|
33
33
|
* Also the one place the gate outcome reaches a frame — see the parameter. */
|
|
34
34
|
export declare function toolEndBodyFrom(result: unknown, isError: boolean,
|
|
35
|
-
/** The leg's card projector
|
|
36
|
-
|
|
37
|
-
structured: StructuredProjector,
|
|
35
|
+
/** The leg's card projector (never a module default: the engine set alone drops every declared caller card unannounced) and media budget. */
|
|
36
|
+
leg: Pick<OccurrenceFrames, "structured" | "mediaByteCapBytes">,
|
|
38
37
|
/** The gate's record of the pass that admitted or refused this call, supplied by the CALLER of this
|
|
39
38
|
* projection — the end projection ({@link emitSettled}) reads it off the call's occurrence (its latest gate
|
|
40
39
|
* record). Deliberately a parameter and never derived from `result`: a tool's own `details`
|
|
@@ -63,10 +62,12 @@ export type OccurrenceFrame = Omit<Extract<TaskEvent, {
|
|
|
63
62
|
}>, keyof TaskEventIdentity> | Omit<Extract<TaskEvent, {
|
|
64
63
|
type: "tool_end";
|
|
65
64
|
}>, keyof TaskEventIdentity>;
|
|
66
|
-
/** Where an occurrence's frames go: the leg's push (it stamps the identity and picks the queue) and its
|
|
65
|
+
/** Where an occurrence's frames go: the leg's push (it stamps the identity and picks the queue), its card projector and its media budget. */
|
|
67
66
|
export interface OccurrenceFrames {
|
|
68
67
|
push(frame: OccurrenceFrame): void;
|
|
69
68
|
structured: StructuredProjector;
|
|
69
|
+
/** `RunnerDeps.mediaByteCapBytes` as given — the request strip's budget, read through the same `mediaBudgetBytes`; required, so no site reads a second one. */
|
|
70
|
+
mediaByteCapBytes: number | undefined;
|
|
70
71
|
}
|
|
71
72
|
/** The START projection: the call's occurrence opens and its start frame goes out. Every leg that announces a call
|
|
72
73
|
* comes through here (the live loop's `tool_execution_start`, the resumed leg's approved call). */
|
|
@@ -33,9 +33,9 @@ export function batchResponseDigest(result) {
|
|
|
33
33
|
? `${text.slice(0, BATCH_RESPONSE_MAX_CHARS)}…[+${text.length - BATCH_RESPONSE_MAX_CHARS} chars truncated]`
|
|
34
34
|
: text;
|
|
35
35
|
}
|
|
36
|
-
export function toolEndBodyFrom(result, isError,
|
|
37
|
-
const o = toolOutputFrom(result);
|
|
38
|
-
const st = structured(result);
|
|
36
|
+
export function toolEndBodyFrom(result, isError, leg, gate, gatedCallIdOfRun) {
|
|
37
|
+
const o = toolOutputFrom(result, leg.mediaByteCapBytes);
|
|
38
|
+
const st = leg.structured(result);
|
|
39
39
|
const det = isError ? readMember(result, "details") : undefined;
|
|
40
40
|
const codeRaw = readMember(det, "code");
|
|
41
41
|
const kindRaw = readMember(det, "errorKind");
|
|
@@ -71,7 +71,7 @@ export function emitSettled(table, start, end, frames) {
|
|
|
71
71
|
toolName: occ.spelledName,
|
|
72
72
|
...(occ.label !== undefined ? { label: occ.label } : {}),
|
|
73
73
|
isError,
|
|
74
|
-
...toolEndBodyFrom(end.result, isError, frames
|
|
74
|
+
...toolEndBodyFrom(end.result, isError, frames, gate, end.gatedCallIdOfRun),
|
|
75
75
|
...(gate?.disposition.kind === "denied" && occ.wireName !== undefined ? { wireName: occ.wireName } : {}),
|
|
76
76
|
...(end.notExecuted === true ? { notExecuted: true } : {}),
|
|
77
77
|
});
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `tool_end` output/structured projection, verbatim from
|
|
3
3
|
* `runtask.ts`'s runLocked R4 phase. Pure functions over the harness `AgentToolResult`: zero closure
|
|
4
|
-
* state, zero runtask coupling — the
|
|
5
|
-
*
|
|
4
|
+
* state, zero runtask coupling — the module-level inputs are the text cap below and the shared media budget default
|
|
5
|
+
* (`mediaBudgetBytes`); the leg hands in its own `mediaByteCapBytes`.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
type ToolOutput = {
|
|
8
8
|
output: unknown;
|
|
9
9
|
truncated: boolean;
|
|
10
10
|
totalChars?: number;
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
|
+
export declare const toolOutputFrom: (result: unknown, mediaByteCapBytes: number | undefined) => ToolOutput | undefined;
|
|
12
13
|
/** The `tool_end.structured` projection over a harness tool result: the CC card when `details.type` names a card
|
|
13
14
|
* the leg admits, else undefined. Minted per leg by {@link createStructuredProjector}; every body projection takes it
|
|
14
15
|
* as a parameter — there is no module-level card set to fall back to. */
|
|
@@ -23,3 +24,4 @@ export declare function createStructuredProjector(opts?: {
|
|
|
23
24
|
declaredCards?: Iterable<string>;
|
|
24
25
|
onUndeclared?: (type: string) => void;
|
|
25
26
|
}): StructuredProjector;
|
|
27
|
+
export {};
|
|
@@ -1,62 +1,81 @@
|
|
|
1
|
+
import { isTextBlock, renderContentBlock } from "../../internal/llm.js";
|
|
2
|
+
import { imageBytes, mediaBudgetBytes } from "../media-byte-cap.js";
|
|
1
3
|
import { engineCardTypes } from "../tool-registry.js";
|
|
2
4
|
import { readMember } from "../thrown-value.js";
|
|
3
5
|
const MAX_TOOL_END_OUTPUT_CHARS = 16384;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
const marker = (n) => `…[+${n} chars truncated]`;
|
|
7
|
+
const isMediaBlock = (b) => {
|
|
8
|
+
const block = b;
|
|
9
|
+
return (block?.type === "image" || block?.type === "document") && typeof block.data === "string";
|
|
10
|
+
};
|
|
11
|
+
const ownChars = (b) => isTextBlock(b) ? b.text.length + (JSON.stringify({ ...b, type: undefined, text: undefined })?.length ?? 2) - 2 : isMediaBlock(b) ? b.data.length : (JSON.stringify(b)?.length ?? 0);
|
|
12
|
+
const boundTextLane = (blocks, lane) => {
|
|
13
|
+
const sized = lane.map((i) => ({ i, size: ownChars(blocks[i]) }));
|
|
14
|
+
const stream = sized.reduce((n, { size }, k) => n + size + (k > 0 ? 1 : 0), 0);
|
|
15
|
+
const forms = new Map();
|
|
16
|
+
let used = 0;
|
|
17
|
+
for (const [k, { i, size }] of sized.entries()) {
|
|
18
|
+
const sep = k > 0 ? 1 : 0;
|
|
19
|
+
if (used + sep + size <= MAX_TOOL_END_OUTPUT_CHARS) {
|
|
20
|
+
used += sep + size;
|
|
21
|
+
forms.set(i, blocks[i]);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const shown = renderContentBlock(blocks[i]).slice(0, Math.max(0, MAX_TOOL_END_OUTPUT_CHARS - used - sep));
|
|
25
|
+
const notShown = stream - Math.min(MAX_TOOL_END_OUTPUT_CHARS, used + sep) - (isTextBlock(blocks[i]) ? shown.length : 0);
|
|
26
|
+
forms.set(i, { type: "text", text: shown + marker(notShown) });
|
|
27
|
+
return { forms, cut: true };
|
|
7
28
|
}
|
|
8
|
-
|
|
9
|
-
|
|
29
|
+
return { forms, cut: false };
|
|
30
|
+
};
|
|
31
|
+
const wireBytes = (b) => Buffer.byteLength(JSON.stringify(b)) + 1;
|
|
32
|
+
const SUMMARY_RESERVE = wireBytes({ type: "omitted", blocks: Number.MAX_SAFE_INTEGER, bytes: Number.MAX_SAFE_INTEGER, truncated: true }) + 2;
|
|
33
|
+
const fitMedia = (media, budget) => {
|
|
34
|
+
let used = 0;
|
|
35
|
+
let degraded = false;
|
|
36
|
+
const blocks = [];
|
|
37
|
+
for (const [i, m] of media.entries()) {
|
|
38
|
+
const cost = used + m.data.length >= budget ? Infinity : wireBytes(m);
|
|
39
|
+
const kept = used + cost <= budget;
|
|
40
|
+
const out = kept ? m : { type: m.type, ...(typeof m.mimeType === "string" && m.mimeType.length <= 255 ? { mimeType: m.mimeType } : {}), bytes: imageBytes(m), truncated: true };
|
|
41
|
+
const outCost = kept ? cost : wireBytes(out);
|
|
42
|
+
degraded ||= !kept;
|
|
43
|
+
if (used + outCost > budget) {
|
|
44
|
+
const rest = media.slice(i);
|
|
45
|
+
blocks.push({ type: "omitted", blocks: rest.length, bytes: rest.reduce((n, r) => n + imageBytes(r), 0), truncated: true });
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
used += outCost;
|
|
49
|
+
blocks.push(out);
|
|
10
50
|
}
|
|
51
|
+
return { blocks, degraded };
|
|
11
52
|
};
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export const toolOutputFrom = (result) => {
|
|
17
|
-
const content = result !== null && typeof result === "object" ? result.content : result;
|
|
18
|
-
if (content === undefined)
|
|
19
|
-
return undefined;
|
|
20
|
-
if (typeof content === "string") {
|
|
21
|
-
if (content.length <= MAX_TOOL_END_OUTPUT_CHARS)
|
|
53
|
+
const project = (content, mediaByteCapBytes) => {
|
|
54
|
+
if (!Array.isArray(content)) {
|
|
55
|
+
const s = typeof content === "string" ? content : (JSON.stringify(content) ?? "");
|
|
56
|
+
if (s.length <= MAX_TOOL_END_OUTPUT_CHARS)
|
|
22
57
|
return { output: content, truncated: false };
|
|
23
|
-
|
|
24
|
-
return { output: t.text, truncated: true, totalChars: t.totalChars };
|
|
58
|
+
return { output: s.slice(0, MAX_TOOL_END_OUTPUT_CHARS) + marker(s.length - MAX_TOOL_END_OUTPUT_CHARS), truncated: true, totalChars: s.length };
|
|
25
59
|
}
|
|
26
|
-
|
|
60
|
+
const media = content.map(isMediaBlock);
|
|
61
|
+
const text = boundTextLane(content, content.flatMap((_, i) => (media[i] ? [] : [i])));
|
|
62
|
+
const limit = mediaBudgetBytes(mediaByteCapBytes);
|
|
63
|
+
const textBytes = [...text.forms.values()].reduce((n, f) => n + wireBytes(f), 0);
|
|
64
|
+
const fit = fitMedia(content.filter((_, i) => media[i]), limit === Infinity ? Infinity : Math.ceil((limit * 4) / 3) - SUMMARY_RESERVE - textBytes);
|
|
65
|
+
if (!text.cut && !fit.degraded)
|
|
66
|
+
return { output: content, truncated: false };
|
|
67
|
+
let next = 0;
|
|
68
|
+
const output = content.flatMap((_, i) => (media[i] ? (fit.blocks[next++] ?? []) : text.forms.has(i) ? [text.forms.get(i)] : []));
|
|
69
|
+
return { output, truncated: true, totalChars: content.reduce((n, b) => n + ownChars(b), 0) };
|
|
70
|
+
};
|
|
71
|
+
export const toolOutputFrom = (result, mediaByteCapBytes) => {
|
|
27
72
|
try {
|
|
28
|
-
|
|
73
|
+
const content = result !== null && typeof result === "object" ? result.content : result;
|
|
74
|
+
return content === undefined ? undefined : project(content, mediaByteCapBytes);
|
|
29
75
|
}
|
|
30
76
|
catch {
|
|
31
|
-
return { output: "[unserializable tool output]", truncated:
|
|
32
|
-
}
|
|
33
|
-
if (serialized.length <= MAX_TOOL_END_OUTPUT_CHARS)
|
|
34
|
-
return { output: content, truncated: false };
|
|
35
|
-
let anyNonText = false;
|
|
36
|
-
let trueChars = 0;
|
|
37
|
-
const raw = Array.isArray(content)
|
|
38
|
-
? content
|
|
39
|
-
.map((b) => {
|
|
40
|
-
const block = b;
|
|
41
|
-
if (block?.type === "text" && typeof block.text === "string") {
|
|
42
|
-
trueChars += block.text.length;
|
|
43
|
-
return block.text;
|
|
44
|
-
}
|
|
45
|
-
anyNonText = true;
|
|
46
|
-
trueChars += typeof block?.data === "string" ? block.data.length : (safeJsonLength(block) ?? 0);
|
|
47
|
-
return `[${typeof block?.type === "string" ? block.type : "block"}${typeof block?.mimeType === "string" ? `: ${block.mimeType}` : ""}]`;
|
|
48
|
-
})
|
|
49
|
-
.join("\n")
|
|
50
|
-
: serialized;
|
|
51
|
-
const totalChars = Array.isArray(content) ? trueChars : serialized.length;
|
|
52
|
-
if (raw.length > MAX_TOOL_END_OUTPUT_CHARS) {
|
|
53
|
-
const t = trunc(raw);
|
|
54
|
-
return { output: t.text, truncated: true, totalChars };
|
|
55
|
-
}
|
|
56
|
-
if (!anyNonText) {
|
|
57
|
-
return { output: raw, truncated: false };
|
|
77
|
+
return { output: "[unserializable tool output]", truncated: true };
|
|
58
78
|
}
|
|
59
|
-
return { output: raw, truncated: true, totalChars };
|
|
60
79
|
};
|
|
61
80
|
const CC_DETAIL_TYPES = engineCardTypes();
|
|
62
81
|
export function createStructuredProjector(opts = {}) {
|
|
@@ -852,8 +852,9 @@ export interface RunnerDeps {
|
|
|
852
852
|
* family this seat complements, not replaces — BCE stays the fleet-row lane with registry `a*`
|
|
853
853
|
* handles and ticks; this seat is the leg-identity lane), and workflow-spawned agents. Frames are
|
|
854
854
|
* minted at the child leg's own runner chokepoint and carry the #281 件A identity envelope plus a
|
|
855
|
-
* terminal status summary
|
|
856
|
-
* per
|
|
855
|
+
* terminal status summary; the `spawn` phase is the projection of the child's `task_started` frame
|
|
856
|
+
* (once per cycle), the `terminal` phase is per leg — see {@link DelegationLifecycleEvent} for the
|
|
857
|
+
* emission points and the recorded honest absences. Wire it once at Runner construction; a
|
|
857
858
|
* deployment needs NO RunInternals access to observe delegation any more. Observation only — a
|
|
858
859
|
* throwing or rejecting observer is contained ({@link deliverDelegationLifecycle}) and never
|
|
859
860
|
* faults the child run; a PRESENT non-function value here is announced once per process and the
|
|
@@ -1446,7 +1447,9 @@ export interface RunnerDeps {
|
|
|
1446
1447
|
* CC 2.1.196 `tengu_media_byte_cap` parity). Image reads (`Read` → ImageContent) are per-image-capped but
|
|
1447
1448
|
* accumulate across turns; this bounds the SUM in the request by stripping the OLDEST images (replaced with a
|
|
1448
1449
|
* marker) — request-only, non-destructive (the durable session keeps the image; the model can re-Read it).
|
|
1449
|
-
*
|
|
1450
|
+
* The same budget bounds the media of one `tool_end.output` (spent there as base64 on the wire, x 4/3); both readers
|
|
1451
|
+
* read it through `mediaBudgetBytes`. Default {@link AGGREGATE_MEDIA_BUDGET_BYTES}; set `Infinity`/`0` to disable. A
|
|
1452
|
+
* non-number, `NaN` or a negative is refused at `new Runner` (`config.invalid_argument`), never read as "no budget".
|
|
1450
1453
|
*/
|
|
1451
1454
|
mediaByteCapBytes?: number;
|
|
1452
1455
|
/** Optional telemetry fired when {@link mediaByteCapBytes} strips media in a request (CC's
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* design/177 — the SHARED memory-store seam (connected team/deployment memory libraries the model may
|
|
3
3
|
* BROWSE and READ through two tools) and its model-facing constants.
|
|
4
4
|
*
|
|
5
|
-
* Upstream anchor: CC 2.1.223's `memory_list` / `memory_read` pair
|
|
6
|
-
*
|
|
5
|
+
* Upstream anchor: CC 2.1.223's `memory_list` / `memory_read` pair, whose defining property was READ/WRITE
|
|
6
|
+
* ASYMMETRY (no model-side write channel); CC no longer has that shape — 2.1.296 also mounts `yd` (cli296.js:242348) "memory_write" — and sema keeps the 223 one (KL-878): write governance is an
|
|
7
7
|
* environment concern, and the tool face's entire participation in it is truthfully reporting each
|
|
8
8
|
* store's `writable` bit. sema keeps that structure and pushes the whole credential/consent half onto
|
|
9
9
|
* the HOST (BYOM: core holds no identity face), so this module's seam is a single pull-shaped snapshot
|