@sema-agent/core 7.23.5 → 7.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/dist/agents/list-agents-tool.d.ts +7 -4
- package/dist/agents/list-agents-tool.js +12 -1
- package/dist/agents/observer.js +5 -8
- package/dist/agents/subagent.js +2 -2
- package/dist/agents/team.js +9 -0
- package/dist/brain/degrading.js +8 -9
- package/dist/brain/route-adjudicator.d.ts +25 -12
- package/dist/brain/route-adjudicator.js +89 -23
- package/dist/brain/route-conformance.js +9 -0
- package/dist/brain/stream-engine.js +11 -9
- package/dist/core/arg-summary.js +17 -6
- package/dist/core/auto-compaction.d.ts +42 -1
- package/dist/core/auto-compaction.js +21 -1
- package/dist/core/auto-mode.js +2 -8
- package/dist/core/exec-gate.js +3 -5
- package/dist/core/file-history-store.js +11 -10
- package/dist/core/hooks.d.ts +7 -0
- package/dist/core/mcp.js +7 -5
- package/dist/core/memory-engine/distiller.d.ts +7 -4
- package/dist/core/memory-engine/distiller.js +11 -3
- package/dist/core/memory.js +1 -0
- package/dist/core/model-seat.d.ts +4 -1
- package/dist/core/model-seat.js +1 -1
- package/dist/core/roles.d.ts +1 -0
- package/dist/core/roles.js +1 -0
- package/dist/core/runner/assemble-result.js +12 -19
- package/dist/core/runner/contracts.d.ts +1 -0
- package/dist/core/runner/prepare-file-history.js +19 -0
- package/dist/core/runner/run-recovery-lanes.js +4 -20
- package/dist/core/runner/run-settle-and-teardown.js +2 -16
- package/dist/core/runner/run-turn-boundary.js +4 -20
- package/dist/core/runner/runtask.js +3 -5
- package/dist/core/runner/stream-settle-backstop.js +5 -5
- package/dist/core/runner/tool-end-body.js +5 -4
- package/dist/core/runner/tool-output-projection.js +3 -2
- package/dist/core/runner/turn-attachments.d.ts +0 -20
- package/dist/core/runner/turn-attachments.js +41 -36
- package/dist/core/session-store.js +2 -4
- package/dist/core/session.d.ts +4 -1
- package/dist/core/session.js +2 -1
- package/dist/core/task-event.d.ts +19 -9
- package/dist/core/task-registry-shared.js +1 -0
- package/dist/core/thrown-value.d.ts +24 -13
- package/dist/core/thrown-value.js +13 -1
- package/dist/core/tool-errors.d.ts +6 -0
- package/dist/core/tool-errors.js +6 -14
- package/dist/core/tools.js +8 -9
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.js +1 -0
- package/dist/engine/llm/types.d.ts +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/orchestration/workflow.js +44 -23
- package/dist/stores/file/file-history-store.js +12 -11
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +9 -3
- package/wire-changes/core@7.24.0.json +249 -0
- package/wire-changes/core@7.24.1.json +85 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
|
|
2
2
|
import { planRejectionClears } from "../context-edit.js";
|
|
3
3
|
import { landStaleResultClears } from "./prepare-context-lane.js";
|
|
4
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, maybeCompact } from "../auto-compaction.js";
|
|
4
|
+
import { DEFAULT_COMPACTION_INSTRUCTIONS, maybeCompact, mintCompactedEvent } from "../auto-compaction.js";
|
|
5
5
|
import { emitTrace } from "../trace.js";
|
|
6
6
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
7
7
|
import { rebaseCadenceWindows } from "./turn-attachments.js";
|
|
@@ -48,26 +48,10 @@ export function runRecoveryLanes(input) {
|
|
|
48
48
|
if (comp.compacted) {
|
|
49
49
|
compactionBreaker.failures = 0;
|
|
50
50
|
recordCompactionReuse(runner, prepared, comp);
|
|
51
|
-
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
52
|
-
|
|
53
|
-
rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
51
|
+
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON && comp.postTriggerTokens !== undefined) {
|
|
52
|
+
rs.counters.compactionFloor = Math.ceil(comp.postTriggerTokens * COMPACTION_REGROWTH_FACTOR);
|
|
54
53
|
}
|
|
55
|
-
queue.push(
|
|
56
|
-
type: "compacted",
|
|
57
|
-
trigger: "forced",
|
|
58
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
59
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
60
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
61
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
62
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
63
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
64
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
65
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
66
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
67
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
68
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
69
|
-
...ident(),
|
|
70
|
-
});
|
|
54
|
+
queue.push(mintCompactedEvent(comp, "forced", ident()));
|
|
71
55
|
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
|
|
72
56
|
const pd = comp.phaseDurations;
|
|
73
57
|
const pdDur = comp.durationMs;
|
|
@@ -20,27 +20,13 @@ import { generatePromptSuggestions } from "./prompt-suggestions.js";
|
|
|
20
20
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
21
21
|
import { autoRunConsolidationFromRecommendation, autoRunOnRecommendationEffective, scopeHasUnconsumedRecommendation } from "./memory-consolidation-driver.js";
|
|
22
22
|
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
23
|
+
import { mintCompactedEvent } from "../auto-compaction.js";
|
|
23
24
|
import { asThrownError } from "../thrown-value.js";
|
|
24
25
|
export async function runSettleAndTeardown(input) {
|
|
25
26
|
const { spec, queue, prepared, resume, internals, rs, stats, ident, parentToolCallId, subagentName, loopLatch, timeout, ownCommittedTailRef, emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, userInterruptedLive, answerSettled, strandedHumanAnswers, reasoningResolution, comp, owedUserInput, setResult, onSuggestions, onSuspend, runner, sessions } = input;
|
|
26
27
|
try {
|
|
27
28
|
if (comp?.compacted) {
|
|
28
|
-
queue.push(
|
|
29
|
-
type: "compacted",
|
|
30
|
-
trigger: "auto",
|
|
31
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
32
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
33
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
34
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
35
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
36
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
37
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
38
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
39
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
40
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
41
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
42
|
-
...ident(),
|
|
43
|
-
});
|
|
29
|
+
queue.push(mintCompactedEvent(comp, "auto", ident()));
|
|
44
30
|
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
|
|
45
31
|
const pd = comp.phaseDurations;
|
|
46
32
|
const pdDur = comp.durationMs;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mintSystemReminder } from "../reminder-mint.js";
|
|
2
2
|
import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
|
|
3
3
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
4
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
4
|
+
import { 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";
|
|
@@ -643,22 +643,7 @@ export function createTurnBoundary(input) {
|
|
|
643
643
|
}
|
|
644
644
|
recordCompactionReuse(runner, prepared, comp);
|
|
645
645
|
if (comp.compacted) {
|
|
646
|
-
queue.push(
|
|
647
|
-
type: "compacted",
|
|
648
|
-
trigger: passTrigger,
|
|
649
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
650
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
651
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
652
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
653
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
654
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
655
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
656
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
657
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
658
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
659
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
660
|
-
...ident(),
|
|
661
|
-
});
|
|
646
|
+
queue.push(mintCompactedEvent(comp, passTrigger, ident()));
|
|
662
647
|
if (!forceManual && recordCompactionAndCheckRapidRefill(rapidRefill, stats.turns)) {
|
|
663
648
|
compactionBreaker.failures = MAX_CONSECUTIVE_COMPACTION_FAILURES;
|
|
664
649
|
queue.push({ type: "compaction_outcome", outcome: "disabled", trigger: passTrigger, reason: "rapid_refill: compaction disabled for the rest of this task", ...ident() });
|
|
@@ -674,9 +659,8 @@ export function createTurnBoundary(input) {
|
|
|
674
659
|
rs.attach.attachState.postCompactPending = true;
|
|
675
660
|
rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
|
|
676
661
|
}
|
|
677
|
-
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
678
|
-
|
|
679
|
-
rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
662
|
+
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON && comp.postTriggerTokens !== undefined) {
|
|
663
|
+
rs.counters.compactionFloor = Math.ceil(comp.postTriggerTokens * COMPACTION_REGROWTH_FACTOR);
|
|
680
664
|
}
|
|
681
665
|
}
|
|
682
666
|
}
|
|
@@ -6,7 +6,7 @@ import { createRapidRefillState } from "../auto-compaction.js";
|
|
|
6
6
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
7
7
|
import { emitTrace } from "../trace.js";
|
|
8
8
|
import { emitTaskOutcome } from "../task-outcome.js";
|
|
9
|
-
import { authCarrierFingerprint,
|
|
9
|
+
import { authCarrierFingerprint, displayUrlOf, routeKeyOf } from "../../brain/route-adjudicator.js";
|
|
10
10
|
import { runWithReasoningWireFacts } from "../../brain/status-sink.js";
|
|
11
11
|
import { expandTiers } from "../roles.js";
|
|
12
12
|
import { screenSwappableDeps } from "../swappable-deps.js";
|
|
@@ -208,10 +208,8 @@ export class Runner {
|
|
|
208
208
|
const prior = this.deps.models?.[name];
|
|
209
209
|
if (!prior)
|
|
210
210
|
continue;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (from !== to && authCarrierFingerprint(prior.headers) === authCarrierFingerprint(nextModel.headers)) {
|
|
214
|
-
movedEntries.push({ modelId: name, from, to });
|
|
211
|
+
if (routeKeyOf(prior.baseUrl) !== routeKeyOf(nextModel.baseUrl) && authCarrierFingerprint(prior.headers) === authCarrierFingerprint(nextModel.headers)) {
|
|
212
|
+
movedEntries.push({ modelId: name, from: displayUrlOf(prior.baseUrl), to: displayUrlOf(nextModel.baseUrl) });
|
|
215
213
|
}
|
|
216
214
|
}
|
|
217
215
|
}
|
|
@@ -6,7 +6,7 @@ import { createSafeNotifier } from "../safe-notify.js";
|
|
|
6
6
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
7
7
|
import { emitTrace } from "../trace.js";
|
|
8
8
|
import { deliverDelegationLifecycle } from "../types.js";
|
|
9
|
-
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
9
|
+
import { asThrownError, readMember, thrownText } from "../thrown-value.js";
|
|
10
10
|
export function streamSettleBackstop(input) {
|
|
11
11
|
const { spec, resume, queue, taskIdRef, entryTracer, runStartedAt, live, runner, sessions, locallyClaimedTokens, parentConstraintRegistry, suspendedEnvReaps } = input;
|
|
12
12
|
const onRunRejected = async (err) => {
|
|
@@ -57,11 +57,11 @@ export function streamSettleBackstop(input) {
|
|
|
57
57
|
}
|
|
58
58
|
const remoteEnvFailure = (() => {
|
|
59
59
|
let cur = err;
|
|
60
|
-
for (let depth = 0; cur && typeof cur === "object" && depth < 8; depth++) {
|
|
61
|
-
const note = cur
|
|
60
|
+
for (let depth = 0; cur !== null && cur !== undefined && typeof cur === "object" && depth < 8; depth++) {
|
|
61
|
+
const note = readMember(cur, "remoteEnvFailure");
|
|
62
62
|
if (note !== undefined)
|
|
63
63
|
return [note];
|
|
64
|
-
cur = cur
|
|
64
|
+
cur = readMember(cur, "cause");
|
|
65
65
|
}
|
|
66
66
|
return undefined;
|
|
67
67
|
})();
|
|
@@ -79,7 +79,7 @@ export function streamSettleBackstop(input) {
|
|
|
79
79
|
return observed !== undefined && observed.length > 0 ? { editedFiles: observed } : {};
|
|
80
80
|
})(),
|
|
81
81
|
...(() => {
|
|
82
|
-
const hinted = err
|
|
82
|
+
const hinted = readMember(err, "retryAfterMs");
|
|
83
83
|
return code === "memory.admission_required" && typeof hinted === "number" && Number.isFinite(hinted) && hinted > 0
|
|
84
84
|
? { retryAfterMs: hinted }
|
|
85
85
|
: {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {} from "../../internal/harness.js";
|
|
2
|
+
import { readMember } from "../thrown-value.js";
|
|
2
3
|
import { MCP_DELIVERY_VERDICTS } from "../mcp-failure.js";
|
|
3
4
|
import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
|
|
4
5
|
import { toolOutputFrom } from "./tool-output-projection.js";
|
|
@@ -35,11 +36,11 @@ export function batchResponseDigest(result) {
|
|
|
35
36
|
export function toolEndBodyFrom(result, isError, structured, gate, gatedCallIdOfRun) {
|
|
36
37
|
const o = toolOutputFrom(result);
|
|
37
38
|
const st = structured(result);
|
|
38
|
-
const det = isError ? result
|
|
39
|
-
const codeRaw = det
|
|
40
|
-
const kindRaw = det
|
|
39
|
+
const det = isError ? readMember(result, "details") : undefined;
|
|
40
|
+
const codeRaw = readMember(det, "code");
|
|
41
|
+
const kindRaw = readMember(det, "errorKind");
|
|
41
42
|
const code = typeof codeRaw === "string" ? codeRaw : typeof kindRaw === "string" ? kindRaw : undefined;
|
|
42
|
-
const deliveredRaw = det
|
|
43
|
+
const deliveredRaw = readMember(det, "delivered");
|
|
43
44
|
const delivered = typeof deliveredRaw === "string" && MCP_DELIVERY_VERDICTS.includes(deliveredRaw) ? deliveredRaw : undefined;
|
|
44
45
|
const gatedCallId = code === "gate.parked" ? gatedCallIdOfRun : undefined;
|
|
45
46
|
return {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { engineCardTypes } from "../tool-registry.js";
|
|
2
|
+
import { readMember } from "../thrown-value.js";
|
|
2
3
|
const MAX_TOOL_END_OUTPUT_CHARS = 16384;
|
|
3
4
|
const safeJsonLength = (v) => {
|
|
4
5
|
try {
|
|
@@ -62,10 +63,10 @@ export function createStructuredProjector(opts = {}) {
|
|
|
62
63
|
const allowed = new Set([...CC_DETAIL_TYPES, ...(opts.declaredCards ?? [])]);
|
|
63
64
|
const announced = new Set();
|
|
64
65
|
return (result) => {
|
|
65
|
-
const details = result
|
|
66
|
+
const details = readMember(result, "details");
|
|
66
67
|
if (details === null || typeof details !== "object")
|
|
67
68
|
return undefined;
|
|
68
|
-
const t = details
|
|
69
|
+
const t = readMember(details, "type");
|
|
69
70
|
if (typeof t !== "string")
|
|
70
71
|
return undefined;
|
|
71
72
|
if (!allowed.has(t)) {
|
|
@@ -346,26 +346,6 @@ export declare const INSTRUCTIONS_CHANGE_BYTE_CAP = 512;
|
|
|
346
346
|
* `steering_injected` echo are all keyed on that wordlist.
|
|
347
347
|
*/
|
|
348
348
|
export declare function createAttachmentState(): AttachmentState;
|
|
349
|
-
/**
|
|
350
|
-
* H4 — thin-projection reducer over a tool's RAW `result.details` (NOT the wire card projector
|
|
351
|
-
* projection and its 4×16K caps: a huge todo list must shrink into a bounded snapshot here, never
|
|
352
|
-
* disarm the reminder by being dropped wholesale). Only the first-party discriminants `todo` /
|
|
353
|
-
* `task` / `task-list` are recognized — every other card type (subagent `agent`/`fork`/`task-output`,
|
|
354
|
-
* bash, fs, mcp, …) is structurally ignored, which is also the child-isolation contract: a subagent's
|
|
355
|
-
* own todo state never reaches the parent's onToolEnd, and its delegation-card types don't parse here.
|
|
356
|
-
*
|
|
357
|
-
* task #51 F3 (窗口复位/投影摄入拆分): this reducer is PROJECTION INGEST ONLY — it
|
|
358
|
-
* never touches the write windows. CC's window anchor is the tool_use block's PRESENCE in the
|
|
359
|
-
* transcript (qFm/BFm match the block by name, :480217-480219/:480253-480254, with no execution-
|
|
360
|
-
* outcome check), so the run loop stamps the windows on INITIATION via {@link stampWriteAnchor}
|
|
361
|
-
* (inside {@link advanceCadenceClock}'s message walk); ingest stays success-only — a failed write
|
|
362
|
-
* has no trustworthy `details`, exactly like CC's reminder body rendering from the authoritative
|
|
363
|
-
* store rather than from tool results.
|
|
364
|
-
*
|
|
365
|
-
* Idempotent (same event twice ⇒ same state) and commutative across DISTINCT task ids (F10); two
|
|
366
|
-
* whole-list writes (TodoWrite / TaskList) racing in one batch are last-write-wins by nature — the
|
|
367
|
-
* later list IS the truer snapshot, documented rather than fought.
|
|
368
|
-
*/
|
|
369
349
|
export declare function reduceToolEnd(state: AttachmentState, details: unknown): void;
|
|
370
350
|
/** task #51 F2/F3 — which reminder window a tool_use block resets. Resolved by the run loop (alias-aware:
|
|
371
351
|
* ToolSpec.aliases over the mounted roster; retired-name normalization is gone); the producers
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
|
|
2
|
+
import { readMember } from "../thrown-value.js";
|
|
2
3
|
import { delimitUntrustedWithClip, sanitizeUntrustedText, SHELLED_BODY_ENVELOPE_TAGS } from "../untrusted-text.js";
|
|
3
4
|
import { protocolOf } from "../protocol-table.js";
|
|
4
5
|
import { renderSkillLinesWithin, renderSkillsBlock } from "./synthetic-tools.js";
|
|
@@ -24,6 +25,7 @@ export const CHANGED_FILES_MAX = 20;
|
|
|
24
25
|
export const CHANGED_FILES_MTIME_EPS_MS = 2000;
|
|
25
26
|
export const ATTACHMENT_BYTE_CAP = 8 * 1024;
|
|
26
27
|
const PROJECTION_ITEMS_MAX = 50;
|
|
28
|
+
const PROJECTION_SCAN_MAX = 10_000;
|
|
27
29
|
const PROJECTION_CONTENT_MAX = 80;
|
|
28
30
|
const LISTING_FRAME_FLOOR_BYTES = 768;
|
|
29
31
|
const DATE_CHANGE_MAX_BYTES = 160;
|
|
@@ -72,52 +74,55 @@ export function createAttachmentState() {
|
|
|
72
74
|
postCompactPending: false,
|
|
73
75
|
};
|
|
74
76
|
}
|
|
77
|
+
function readListLength(value) {
|
|
78
|
+
const n = value !== null && typeof value === "object" ? readMember(value, "length") : undefined;
|
|
79
|
+
return typeof n === "number" && Number.isInteger(n) && n >= 0 ? Math.min(n, PROJECTION_SCAN_MAX) : undefined;
|
|
80
|
+
}
|
|
81
|
+
function readTaskEntry(raw) {
|
|
82
|
+
const id = readMember(raw, "id");
|
|
83
|
+
if (typeof id !== "string")
|
|
84
|
+
return undefined;
|
|
85
|
+
const subject = readMember(raw, "subject");
|
|
86
|
+
const status = readMember(raw, "status");
|
|
87
|
+
return { id, item: { content: typeof subject === "string" ? subject.slice(0, PROJECTION_CONTENT_MAX) : "", status: typeof status === "string" ? status : "pending" } };
|
|
88
|
+
}
|
|
75
89
|
export function reduceToolEnd(state, details) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!Array.isArray(newTodos))
|
|
90
|
+
const type = readMember(details, "type");
|
|
91
|
+
if (type === "todo") {
|
|
92
|
+
const newTodos = readMember(details, "newTodos");
|
|
93
|
+
const count = readListLength(newTodos);
|
|
94
|
+
if (count === undefined)
|
|
82
95
|
return;
|
|
83
|
-
const shaped =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
const shaped = [];
|
|
97
|
+
for (let i = 0; i < count; i++) {
|
|
98
|
+
const item = readMember(newTodos, String(i));
|
|
99
|
+
const content = readMember(item, "content");
|
|
100
|
+
const status = readMember(item, "status");
|
|
101
|
+
if (typeof content === "string" && typeof status === "string")
|
|
102
|
+
shaped.push({ content: content.slice(0, PROJECTION_CONTENT_MAX), status });
|
|
103
|
+
}
|
|
89
104
|
const allDone = shaped.length > 0 && shaped.every((t) => t.status === "completed");
|
|
90
105
|
state.todo = allDone ? { items: [] } : { items: shaped.slice(0, PROJECTION_ITEMS_MAX) };
|
|
91
106
|
}
|
|
92
|
-
else if (
|
|
107
|
+
else if (type === "task") {
|
|
93
108
|
const map = state.taskItems ?? (state.taskItems = new Map());
|
|
94
|
-
const deleted =
|
|
109
|
+
const deleted = readMember(details, "deleted");
|
|
95
110
|
if (typeof deleted === "string")
|
|
96
111
|
map.delete(deleted);
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else if (d.type === "task-list") {
|
|
109
|
-
const tasks = d.tasks;
|
|
110
|
-
if (!Array.isArray(tasks))
|
|
112
|
+
const entry = readTaskEntry(readMember(details, "task"));
|
|
113
|
+
if (entry !== undefined)
|
|
114
|
+
map.set(entry.id, entry.item);
|
|
115
|
+
}
|
|
116
|
+
else if (type === "task-list") {
|
|
117
|
+
const tasks = readMember(details, "tasks");
|
|
118
|
+
const count = readListLength(tasks);
|
|
119
|
+
if (count === undefined)
|
|
111
120
|
return;
|
|
112
121
|
const map = new Map();
|
|
113
|
-
for (
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
map.set(
|
|
117
|
-
content: typeof t.subject === "string" ? t.subject.slice(0, PROJECTION_CONTENT_MAX) : "",
|
|
118
|
-
status: typeof t.status === "string" ? t.status : "pending",
|
|
119
|
-
});
|
|
120
|
-
}
|
|
122
|
+
for (let i = 0; i < count; i++) {
|
|
123
|
+
const entry = readTaskEntry(readMember(tasks, String(i)));
|
|
124
|
+
if (entry !== undefined)
|
|
125
|
+
map.set(entry.id, entry.item);
|
|
121
126
|
}
|
|
122
127
|
state.taskItems = map;
|
|
123
128
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InMemorySessionRepo, SessionError, uuidv7, } from "../internal/harness.js";
|
|
2
2
|
import { isSessionConflict } from "./session.js";
|
|
3
|
+
import { readMember } from "./thrown-value.js";
|
|
3
4
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
4
5
|
import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
|
|
5
6
|
export { SESSION_DEFAULT_TTL_DAYS };
|
|
@@ -285,8 +286,5 @@ async function settledForkOptions(source) {
|
|
|
285
286
|
return {};
|
|
286
287
|
}
|
|
287
288
|
function isNotFound(err) {
|
|
288
|
-
return (
|
|
289
|
-
err !== null &&
|
|
290
|
-
"code" in err &&
|
|
291
|
-
err.code === "not_found");
|
|
289
|
+
return readMember(err, "code") === "not_found";
|
|
292
290
|
}
|
package/dist/core/session.d.ts
CHANGED
|
@@ -8,7 +8,10 @@ export type { SessionWriteOptions } from "../internal/harness.js";
|
|
|
8
8
|
import type { Session } from "../internal/harness.js";
|
|
9
9
|
import type { SessionTreeEntry } from "../internal/harness.js";
|
|
10
10
|
export type { SessionPlacementRecord } from "../internal/harness.js";
|
|
11
|
-
/** True when `err` is a session optimistic-lock conflict (a concurrent writer won the branch leaf).
|
|
11
|
+
/** True when `err` is a session optimistic-lock conflict (a concurrent writer won the branch leaf). Both
|
|
12
|
+
* halves read what a HOST `SessionStorage` threw, so both are guarded — unreadable is not a conflict,
|
|
13
|
+
* never a raise inside the classify-and-rethrow guard. The class test is KEPT: widening the verdict to
|
|
14
|
+
* any value carrying the word would arm the CAS retry loop on a foreign error (KL-942b). */
|
|
12
15
|
export declare function isSessionConflict(err: unknown): boolean;
|
|
13
16
|
/**
|
|
14
17
|
* Subagent transcript persistence — the ACQUIRE-form placement declaration a trusted spawner passes
|
package/dist/core/session.js
CHANGED
|
@@ -3,8 +3,9 @@ export { InMemorySessionRepo, InMemorySessionStorage, uuidv7, validateEntriesFor
|
|
|
3
3
|
export { BaseSessionStorage, leafIdAfterEntry, } from "../internal/harness.js";
|
|
4
4
|
export { SessionError } from "../internal/harness.js";
|
|
5
5
|
import { SessionError } from "../internal/harness.js";
|
|
6
|
+
import { readMember, thrownIs } from "./thrown-value.js";
|
|
6
7
|
export function isSessionConflict(err) {
|
|
7
|
-
return err
|
|
8
|
+
return thrownIs(err, SessionError) && readMember(err, "code") === "conflict";
|
|
8
9
|
}
|
|
9
10
|
export function hasSessionFork(store) {
|
|
10
11
|
return typeof store.fork === "function";
|
|
@@ -592,16 +592,23 @@ export type TaskEvent = ({
|
|
|
592
592
|
type: "compacted";
|
|
593
593
|
trigger: "auto" | "manual" | "forced";
|
|
594
594
|
tokensBefore: number;
|
|
595
|
-
/**
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
|
|
595
|
+
/** How much context this compaction FREED, as the engine measured it: one structural sum over
|
|
596
|
+
* the pre-messages minus the same sum over the post-messages, inside the pass. **The only
|
|
597
|
+
* subtractable number on this event**, and what a context meter should render. Clamped ≥ 0 at
|
|
598
|
+
* the mint, so `0` means "freed nothing", never "could not tell". Absent ⇒ the post
|
|
599
|
+
* measurement failed and no delta exists — never a stand-in. */
|
|
600
|
+
freedTokens?: number;
|
|
601
|
+
/** DIAGNOSTIC, NOT subtractable. Post-compaction size in the TRIGGER-GATE coordinate
|
|
602
|
+
* (structural estimate + the fixed prompt overhead). {@link triggerTokensBefore} is not its
|
|
603
|
+
* matching pre-value: the gate reads a usage-ANCHORED figure whenever the provider's last
|
|
604
|
+
* usage is fresh, while this is always structural + overhead, so the difference mixes
|
|
605
|
+
* coordinates and goes NEGATIVE on ordinary auto passes (3037 − 4591 = −1554, measured on a
|
|
606
|
+
* 4k window). Use {@link freedTokens}. Absent when the post measurement failed. */
|
|
601
607
|
tokensAfter?: number;
|
|
602
|
-
/**
|
|
603
|
-
*
|
|
604
|
-
*
|
|
608
|
+
/** DIAGNOSTIC, NOT subtractable. The quantity the gate compared against its threshold —
|
|
609
|
+
* usage-anchored when the anchor is fresh, structural + overhead when it is not. It answers
|
|
610
|
+
* WHAT tripped the trigger, a different question from how much was freed; left un-clamped and
|
|
611
|
+
* un-paired rather than made to look subtractable. Absent on the legacy emitter. */
|
|
605
612
|
triggerTokensBefore?: number;
|
|
606
613
|
/** TB telemetry B4: wall time of the whole compaction pass (summary call included) — what the
|
|
607
614
|
* boundary pause actually cost. Absent on legacy emitters. */
|
|
@@ -823,6 +830,9 @@ export type TaskEvent = ({
|
|
|
823
830
|
* Live/ephemeral — NOT persisted (a resumable trace re-derives usage from the result), like `status`.
|
|
824
831
|
* Maps to CC `SDKTaskProgress` (camelCase here → snake_case `task_id`/`total_tokens`/`tool_uses`/`duration_ms`
|
|
825
832
|
* at the SDK/service projection, the same convention as `toolCallId`→`tool_use_id`).
|
|
833
|
+
* `taskId` is the child RUN's id (`spec.taskId ?? sessionId`), NOT the registry handle: the
|
|
834
|
+
* background lanes launch with no explicit `spec.taskId`, so it equals the child's session id and
|
|
835
|
+
* hence its `transcriptId`. Both domains are tabled in `docs/INTEGRATION-CORE.md` §1.6a.
|
|
826
836
|
*/
|
|
827
837
|
type: "task_progress";
|
|
828
838
|
taskId: string;
|
|
@@ -227,6 +227,7 @@ export function formatWorkflowRun(run) {
|
|
|
227
227
|
: {}),
|
|
228
228
|
...(run.journalSkips !== undefined && run.journalSkips > 0 ? { journal_skipped: run.journalSkips } : {}),
|
|
229
229
|
...(run.status === "failed" && run.error ? { error: delimitUntrusted("workflow error", boundedRedactedSummary(run.error, 500)) } : {}),
|
|
230
|
+
...(run.errorCode !== undefined ? { errorCode: run.errorCode } : {}),
|
|
230
231
|
...(run.status === "completed" && run.result !== undefined
|
|
231
232
|
? { result: delimitUntrusted("workflow result", run.resultFull ?? run.result) }
|
|
232
233
|
: {}),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Is this an Error, including one minted in ANOTHER REALM?
|
|
3
3
|
*
|
|
4
|
-
* `instanceof` compares against THIS realm's `Error.prototype`, so a genuine `Error`
|
|
5
|
-
* engine ran
|
|
4
|
+
* `instanceof` compares against THIS realm's `Error.prototype`, so a genuine `Error` minted outside it — by
|
|
5
|
+
* a script the engine ran through a deployment-supplied runner, or a duplicated module instance — fails and would be
|
|
6
6
|
* described as an anonymous object, losing the message that is the entire point of reporting it. The brand
|
|
7
7
|
* check is the realm-independent one: an object with the `[[ErrorData]]` internal slot answers `[object Error]`
|
|
8
8
|
* no matter where it was made, and a proxy forwards it. A plain object can forge the tag through
|
|
@@ -13,21 +13,28 @@
|
|
|
13
13
|
* proxy traps, and the tag is a getter that can throw.
|
|
14
14
|
*/
|
|
15
15
|
export declare function isErrorLike(value: unknown): boolean;
|
|
16
|
+
/** `value instanceof ctor`, TOTAL — the class half of {@link readMember}'s rule. The prototype-chain walk
|
|
17
|
+
* runs two proxy traps and a revoked proxy throws on both, while every caller tests a value someone else
|
|
18
|
+
* threw inside the handler that decides how a failure settles. Unrecognizable is not that class, and
|
|
19
|
+
* {@link isErrorLike} is this same rule for `Error`. */
|
|
20
|
+
export declare function thrownIs<T>(value: unknown, ctor: abstract new (...args: never[]) => T): value is T;
|
|
21
|
+
/** The NAME a thrown value carries (`Error.prototype.name` and its subclasses' overrides), read
|
|
22
|
+
* realm-independently and guarded — `undefined` when the value is not error-like or its `name` is not a
|
|
23
|
+
* string. Callers that compare against a name CONSTANT need exactly this: the comparison is on the WORD
|
|
24
|
+
* precisely because the value crossed a seam, so binding the test to this realm's `Error.prototype`
|
|
25
|
+
* defeats it. Whether an EMPTY name folds to `undefined` is policy and stays at the call site. */
|
|
26
|
+
export declare function thrownName(err: unknown): string | undefined;
|
|
16
27
|
/**
|
|
17
28
|
* Turning a value that was THROWN at us into text — the ONE rule, for the whole engine.
|
|
18
29
|
*
|
|
19
|
-
* The idiom
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* destroy the report it exists to produce. Where the render sat inside a promise rejection handler, the
|
|
25
|
-
* resulting `TypeError` was an unhandled rejection and took the whole process down: a caller's choice of
|
|
26
|
-
* rejection value decided whether the engine stayed alive.
|
|
30
|
+
* The idiom it replaces — `err instanceof Error ? err.message : String(err)` — is PARTIAL on its non-Error
|
|
31
|
+
* arm: `String` calls the value's own `toString` / `Symbol.toPrimitive`, so it throws on a value that has
|
|
32
|
+
* neither and on one whose hook throws. The thrower is not ours (a deployment hook, a plugin callback, a
|
|
33
|
+
* model-supplied tool body may reject with anything), so the rendering step was the one step that could
|
|
34
|
+
* destroy the report it exists to produce — and inside a promise rejection handler it took the process down.
|
|
27
35
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* dominant shapes keep their EXACT previous bytes and only the shapes that used to be useless or fatal move:
|
|
36
|
+
* TOTALITY is therefore the contract, met the way the structural describer meets it: the value is never
|
|
37
|
+
* asked to say what it is. Three arms, ordered so the dominant shapes keep their EXACT previous bytes:
|
|
31
38
|
* - a thrown STRING is its own message (a bare `throw "…"`), returned verbatim — the describer would quote
|
|
32
39
|
* it, which is right when rejecting a config value and wrong when relaying a message someone threw;
|
|
33
40
|
* - an ERROR hands over `message` verbatim — no cap, no one-lining, no quoting. This is the arm nearly all
|
|
@@ -90,6 +97,10 @@ export declare function asThrownError(err: unknown): Error;
|
|
|
90
97
|
* is `undefined` for BOTH "absent" and "unreadable" — deliberately, because a caller that must tell those
|
|
91
98
|
* apart is asking a question an untrusted value is not obliged to answer; what it does instead is fall back
|
|
92
99
|
* to describing the value, which {@link thrownText} already does totally.
|
|
100
|
+
*
|
|
101
|
+
* The read walks the PROTOTYPE CHAIN deliberately — `name` lives on `Error.prototype`. `"__proto__"` is
|
|
102
|
+
* the one key where that differs from an own read, and its only caller is {@link readOwnMembers}, which
|
|
103
|
+
* passes it only when it IS an own key (a data property shadows the accessor; both readings agree).
|
|
93
104
|
*/
|
|
94
105
|
export declare function readMember(value: unknown, key: string): unknown;
|
|
95
106
|
/** The PLURAL of {@link readMember}: read a value's own enumerable members, once each, into an inert
|
|
@@ -9,6 +9,18 @@ export function isErrorLike(value) {
|
|
|
9
9
|
return false;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export function thrownIs(value, ctor) {
|
|
13
|
+
try {
|
|
14
|
+
return value instanceof ctor;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function thrownName(err) {
|
|
21
|
+
const name = isErrorLike(err) ? readMember(err, "name") : undefined;
|
|
22
|
+
return typeof name === "string" ? name : undefined;
|
|
23
|
+
}
|
|
12
24
|
export function thrownText(err) {
|
|
13
25
|
try {
|
|
14
26
|
if (typeof err === "string")
|
|
@@ -66,7 +78,7 @@ export function readOwnMembers(value) {
|
|
|
66
78
|
}
|
|
67
79
|
const out = {};
|
|
68
80
|
for (const key of keys)
|
|
69
|
-
out
|
|
81
|
+
Object.defineProperty(out, key, { value: readMember(value, key), enumerable: true, writable: true, configurable: true });
|
|
70
82
|
return out;
|
|
71
83
|
}
|
|
72
84
|
export function thrownMarksOf(err) {
|
|
@@ -33,6 +33,12 @@ export declare function truncateError(s: string): string;
|
|
|
33
33
|
* budget of one character. */
|
|
34
34
|
export declare const OUTPUT_BUDGET_MIN_CHARS = 512;
|
|
35
35
|
export declare function clipWithFilePointer(s: string, limit: number, fullOutputPath?: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Format a thrown tool error into model-facing text (design/64 §11.1/§11.2): a shell/exec error becomes
|
|
38
|
+
* `Exit code N` + stderr + stdout; a plain Error becomes its message (+ any captured streams); anything
|
|
39
|
+
* else is stringified. The result is truncated to {@link MAX_ERROR_CHARS}. An AbortError is reported as an
|
|
40
|
+
* interruption.
|
|
41
|
+
*/
|
|
36
42
|
export declare function formatToolError(error: unknown): string;
|
|
37
43
|
/**
|
|
38
44
|
* A typed, read-side fold of a `TaskResult.errorCode` into the failure CLASS it belongs to (design/80
|
package/dist/core/tool-errors.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Value } from "typebox/value";
|
|
|
2
2
|
const MAX_ERROR_CHARS = 10_000;
|
|
3
3
|
const HALF = MAX_ERROR_CHARS / 2;
|
|
4
4
|
import { isHighSurrogate, isLowSurrogate, sliceHeadSafe, sliceTailSafe } from "./surrogate-safe-slice.js";
|
|
5
|
-
import { readMember, thrownText } from "./thrown-value.js";
|
|
5
|
+
import { isErrorLike, readMember, thrownName, thrownText } from "./thrown-value.js";
|
|
6
6
|
export function truncateError(s) {
|
|
7
7
|
if (s.length <= MAX_ERROR_CHARS)
|
|
8
8
|
return s;
|
|
@@ -39,18 +39,10 @@ function isShellLike(e) {
|
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
function isErrorInstance(v) {
|
|
43
|
-
try {
|
|
44
|
-
return v instanceof Error;
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
42
|
export function formatToolError(error) {
|
|
51
43
|
const parts = [];
|
|
52
|
-
const name =
|
|
53
|
-
if (
|
|
44
|
+
const name = thrownName(error);
|
|
45
|
+
if (name === "AbortError")
|
|
54
46
|
return "Interrupted";
|
|
55
47
|
const message = readMember(error, "message");
|
|
56
48
|
const messageText = typeof message === "string" ? message : undefined;
|
|
@@ -60,15 +52,15 @@ export function formatToolError(error) {
|
|
|
60
52
|
const stdout = readMember(error, "stdout");
|
|
61
53
|
if (typeof exitCode === "number")
|
|
62
54
|
parts.push(`Exit code ${exitCode}`);
|
|
63
|
-
if (
|
|
55
|
+
if (isErrorLike(error) && messageText !== undefined && messageText !== "")
|
|
64
56
|
parts.push(messageText);
|
|
65
57
|
if (typeof stderr === "string" && stderr.trim())
|
|
66
58
|
parts.push(stderr);
|
|
67
59
|
if (typeof stdout === "string" && stdout.trim())
|
|
68
60
|
parts.push(stdout);
|
|
69
61
|
}
|
|
70
|
-
else if (
|
|
71
|
-
parts.push((messageText ?? "") || (
|
|
62
|
+
else if (isErrorLike(error)) {
|
|
63
|
+
parts.push((messageText ?? "") || (name ?? "") || "Error");
|
|
72
64
|
}
|
|
73
65
|
else {
|
|
74
66
|
parts.push(thrownText(error));
|