@sema-agent/core 1.435.0 → 1.437.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/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +24 -6
- package/dist/core/auto-compaction.d.ts +4 -1
- package/dist/core/auto-compaction.js +90 -3
- package/dist/core/background-agent-store.d.ts +1 -0
- package/dist/core/runner/prepare-task.d.ts +3 -1
- package/dist/core/runner/prepare-task.js +17 -3
- package/dist/core/runner/runtask.js +15 -0
- package/dist/core/task-registry.d.ts +3 -0
- package/dist/core/task-registry.js +10 -3
- package/dist/tools/fs/index.d.ts +2 -1
- package/dist/tools/fs/index.js +7 -2
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { type ExecutionEnv } from "../internal/harness.js";
|
|
|
5
5
|
import type { RunInternals } from "../core/runner/prepare-task.js";
|
|
6
6
|
import type { TaskNotificationPayload } from "../core/task-notification.js";
|
|
7
7
|
export type { SubagentStep, SubagentEditedFile } from "./subagent-steps.js";
|
|
8
|
+
export declare function notifyResultField(result: string | undefined): string | undefined;
|
|
8
9
|
export declare function inheritedManifestScopeFor(snapshot: readonly unknown[] | undefined): RunInternals["inheritedManifestScope"];
|
|
9
10
|
export declare const DEFAULT_SUBAGENT_TOOL_NAME = "Agent";
|
|
10
11
|
export declare const LEGACY_SUBAGENT_TOOL_NAME = "Task";
|
package/dist/agents/subagent.js
CHANGED
|
@@ -12,6 +12,7 @@ import { SUBAGENT_PROMPT } from "../prompts/default.js";
|
|
|
12
12
|
import { hasSessionFork } from "../core/session.js";
|
|
13
13
|
import { isDurablePause } from "./suspend-guard.js";
|
|
14
14
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
15
|
+
import { boundedRedactedSummary } from "../core/untrusted-egress.js";
|
|
15
16
|
import { uuidv7 } from "../internal/harness.js";
|
|
16
17
|
import { addWorktree } from "../core/git-worktree-env.js";
|
|
17
18
|
import { BG_AGENT_REAP_STOP_ERROR, DURABLE_AGENT_HANDLE_RE, DURABLE_AGENT_HEARTBEAT_MS, normalizeAgentName } from "../core/task-registry.js";
|
|
@@ -19,6 +20,23 @@ import { canAccessAgentRecord } from "../core/background-agent-store.js";
|
|
|
19
20
|
import { extractErrorCode } from "../brain/errors.js";
|
|
20
21
|
import { ObserverDigestTap, ObserverPairing, createObserverReportToolSpec, markObserverTaskId, unmarkObserverTaskId, isObserverTaskId, observerFramingPrompt, observerSlug, resolveObserverDeclaration, OBSERVER_SENDMESSAGE_SENDER_REFUSAL, OBSERVER_SENDMESSAGE_TARGET_REFUSAL, } from "./observer.js";
|
|
21
22
|
import { SubagentStepRecorder, stepsFromMessages } from "./subagent-steps.js";
|
|
23
|
+
const BG_AGENT_RESULT_MAX = 4_000;
|
|
24
|
+
const BG_AGENT_RESULT_FULL_MAX = 200_000;
|
|
25
|
+
function resultSettleFields(result) {
|
|
26
|
+
if (!result)
|
|
27
|
+
return {};
|
|
28
|
+
const full = boundedRedactedSummary(result, BG_AGENT_RESULT_FULL_MAX);
|
|
29
|
+
const display = full.length > BG_AGENT_RESULT_MAX ? boundedRedactedSummary(result, BG_AGENT_RESULT_MAX) : full;
|
|
30
|
+
return display === full ? { result: display } : { result: display, resultFull: full };
|
|
31
|
+
}
|
|
32
|
+
const BG_AGENT_NOTIFY_RESULT_MAX = 2_000;
|
|
33
|
+
export function notifyResultField(result) {
|
|
34
|
+
if (!result)
|
|
35
|
+
return undefined;
|
|
36
|
+
return result.length > BG_AGENT_NOTIFY_RESULT_MAX
|
|
37
|
+
? `${result.slice(0, BG_AGENT_NOTIFY_RESULT_MAX)}\n[result truncated: ${result.length} chars total — call TaskOutput for the full text]`
|
|
38
|
+
: result;
|
|
39
|
+
}
|
|
22
40
|
export function inheritedManifestScopeFor(snapshot) {
|
|
23
41
|
if (!snapshot || snapshot.length === 0)
|
|
24
42
|
return undefined;
|
|
@@ -610,7 +628,7 @@ function makeSubagentResume(deps) {
|
|
|
610
628
|
try {
|
|
611
629
|
const winner = deps.registry.settleRevivedAgent(deps.taskId, reviveCycle, {
|
|
612
630
|
status,
|
|
613
|
-
...(child.result
|
|
631
|
+
...resultSettleFields(child.result),
|
|
614
632
|
...(status !== "completed" ? { error: (child.errorMessage ?? `resumed run ${status}`).slice(0, 500) } : {}),
|
|
615
633
|
});
|
|
616
634
|
if (winner !== undefined)
|
|
@@ -644,7 +662,7 @@ function makeSubagentResume(deps) {
|
|
|
644
662
|
...(stoppedByRevive !== undefined ? { stoppedBy: stoppedByRevive } : {}),
|
|
645
663
|
seq: entry.cycleSeq,
|
|
646
664
|
summary: `Agent "${reviveName}" (resumed) ${status === "killed" ? "stopped" : child.status === "completed" ? "finished" : String(child.status)}${ccElapsedTag(Date.now() - reviveStartedAt)}`,
|
|
647
|
-
...(child.result ? { result: child.result
|
|
665
|
+
...(child.result ? { result: notifyResultField(child.result) } : {}),
|
|
648
666
|
...(status === "killed" && child.result ? { partial: true } : {}),
|
|
649
667
|
...resumeResidual(),
|
|
650
668
|
resumable: status !== "killed",
|
|
@@ -1771,7 +1789,7 @@ function makeSubagentTool(opts, depth, excluded, extraToolsBudget) {
|
|
|
1771
1789
|
const reapedBg = !okBg && abort.signal.aborted;
|
|
1772
1790
|
const settledBg = bg.registry.settleBackgroundAgent(taskId, {
|
|
1773
1791
|
status: okBg ? "completed" : reapedBg ? "killed" : "failed",
|
|
1774
|
-
...(child.result
|
|
1792
|
+
...resultSettleFields(child.result),
|
|
1775
1793
|
...(!okBg ? { error: reapedBg ? BG_AGENT_REAP_STOP_ERROR : child.errorMessage ?? String(child.status) } : {}),
|
|
1776
1794
|
}) ?? (abort.signal.aborted ? "killed" : okBg ? "completed" : "failed");
|
|
1777
1795
|
const stoppedByBg = settledBg === "killed" ? bg.registry.getStopAttribution(taskId) ?? "system" : undefined;
|
|
@@ -1809,7 +1827,7 @@ function makeSubagentTool(opts, depth, excluded, extraToolsBudget) {
|
|
|
1809
1827
|
...(stoppedByBg !== undefined ? { stoppedBy: stoppedByBg } : {}),
|
|
1810
1828
|
summary: `${ccCompletionText(shortDesc, settledBg, String(child.status), Date.now() - forkBgStartedAt)}`,
|
|
1811
1829
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
1812
|
-
...(child.result ? { result: child.result
|
|
1830
|
+
...(child.result ? { result: notifyResultField(child.result) } : {}),
|
|
1813
1831
|
...(settledBg === "killed" && child.result ? { partial: true } : {}),
|
|
1814
1832
|
...residualFork,
|
|
1815
1833
|
resumable: resumableFork,
|
|
@@ -2347,7 +2365,7 @@ task_id: ${taskId}
|
|
|
2347
2365
|
const settled = bg.registry.settleBackgroundAgent(taskId, {
|
|
2348
2366
|
status: ok ? "completed" : reaped ? "killed" : "failed",
|
|
2349
2367
|
seq: seqAtSettle ?? 1,
|
|
2350
|
-
...(child.result
|
|
2368
|
+
...resultSettleFields(child.result),
|
|
2351
2369
|
...(!ok ? { error: reaped ? BG_AGENT_REAP_STOP_ERROR : child.errorMessage ?? String(child.status) } : {}),
|
|
2352
2370
|
}) ??
|
|
2353
2371
|
(abort.signal.aborted ? "killed" : ok ? "completed" : "failed");
|
|
@@ -2386,7 +2404,7 @@ task_id: ${taskId}
|
|
|
2386
2404
|
...(stoppedBy !== undefined ? { stoppedBy } : {}),
|
|
2387
2405
|
summary: `${ccCompletionText(shortDesc, settled, String(child.status), Date.now() - bgStartedAt)}${observerNote}`,
|
|
2388
2406
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
2389
|
-
...(child.result ? { result: child.result
|
|
2407
|
+
...(child.result ? { result: notifyResultField(child.result) } : {}),
|
|
2390
2408
|
...(settled === "killed" && child.result ? { partial: true } : {}),
|
|
2391
2409
|
...residual,
|
|
2392
2410
|
resumable: resumableBg,
|
|
@@ -66,11 +66,14 @@ export interface MaybeCompactOptions {
|
|
|
66
66
|
maxFiles?: number;
|
|
67
67
|
maxCharsPerFile?: number;
|
|
68
68
|
recentlyReadFiles?: () => string[];
|
|
69
|
+
instructionSourcePaths?: ReadonlyArray<string>;
|
|
70
|
+
normalizePath?: (raw: string) => Promise<string>;
|
|
71
|
+
isDedupStubResult?: (resultText: string) => boolean;
|
|
69
72
|
};
|
|
70
73
|
onApplied?: (attachedComplete: ReadonlyArray<{
|
|
71
74
|
path: string;
|
|
72
75
|
content: string;
|
|
73
|
-
}>) => void;
|
|
76
|
+
}>, preserveReadState?: ReadonlyArray<string>) => void;
|
|
74
77
|
overheadTokens?: number;
|
|
75
78
|
summaryProvider?: (input: {
|
|
76
79
|
messagesToSummarize: AgentMessage[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, CompactionError, compact, computeFileLists, dryRunSummarizationClamp, estimateContextTokens, estimateTokens, prepareCompaction, shouldCompact, } from "../internal/harness.js";
|
|
2
|
+
import { fileArgPath } from "../tools/fs/safety.js";
|
|
2
3
|
import { contextEditFrontier } from "./context-edit.js";
|
|
3
4
|
import { selectCompactionEpoch } from "../prompt-assembly/epoch.js";
|
|
4
5
|
import { SEMA_DEFAULT_PACK } from "../prompt-assembly/packs/sema-default.js";
|
|
@@ -318,9 +319,95 @@ export async function maybeCompact(opts) {
|
|
|
318
319
|
const attachedComplete = [];
|
|
319
320
|
const att = opts.workingFileAttachments;
|
|
320
321
|
const recentlyRead = att?.recentlyReadFiles?.() ?? [];
|
|
321
|
-
const
|
|
322
|
+
const rawCandidateFiles = recentlyRead.length > 0 ? recentlyRead : (details?.modifiedFilesByRecency ?? details?.modifiedFiles);
|
|
323
|
+
const maxFilesForSelection = Math.max(1, att?.maxFiles ?? 3);
|
|
324
|
+
const excludedReadStatePreserveKeys = [];
|
|
325
|
+
const candidateFiles = Array.isArray(rawCandidateFiles) && rawCandidateFiles.length > 0
|
|
326
|
+
? await (async () => {
|
|
327
|
+
const keptTailReadPathsRaw = new Set();
|
|
328
|
+
const firstKeptIdx = branch.findIndex((e) => e.id === firstKeptEntryId);
|
|
329
|
+
if (firstKeptIdx >= 0) {
|
|
330
|
+
const dedupStub = att?.isDedupStubResult;
|
|
331
|
+
for (let i = firstKeptIdx; i < branch.length; i++) {
|
|
332
|
+
const entry = branch[i];
|
|
333
|
+
if (entry.type !== "message" || entry.message.role !== "assistant")
|
|
334
|
+
continue;
|
|
335
|
+
const msg = entry.message;
|
|
336
|
+
if (!("content" in msg) || !Array.isArray(msg.content))
|
|
337
|
+
continue;
|
|
338
|
+
const pendingReads = new Map();
|
|
339
|
+
for (const block of msg.content) {
|
|
340
|
+
if (typeof block !== "object" || block === null)
|
|
341
|
+
continue;
|
|
342
|
+
if (!("type" in block) || block.type !== "toolCall")
|
|
343
|
+
continue;
|
|
344
|
+
const tc = block;
|
|
345
|
+
if (typeof tc.name !== "string" || typeof tc.id !== "string" || !tc.arguments)
|
|
346
|
+
continue;
|
|
347
|
+
if (tc.name !== "read" && tc.name !== "Read" && tc.name !== "read_file")
|
|
348
|
+
continue;
|
|
349
|
+
const path = fileArgPath(tc.arguments);
|
|
350
|
+
if (path)
|
|
351
|
+
pendingReads.set(tc.id, path);
|
|
352
|
+
}
|
|
353
|
+
if (pendingReads.size === 0)
|
|
354
|
+
continue;
|
|
355
|
+
for (let j = i + 1; j < branch.length && pendingReads.size > 0; j++) {
|
|
356
|
+
const next = branch[j];
|
|
357
|
+
if (next.type !== "message")
|
|
358
|
+
continue;
|
|
359
|
+
if (next.message.role === "assistant")
|
|
360
|
+
break;
|
|
361
|
+
if (next.message.role !== "toolResult")
|
|
362
|
+
continue;
|
|
363
|
+
const tr = next.message;
|
|
364
|
+
const path = pendingReads.get(tr.toolCallId);
|
|
365
|
+
if (path === undefined)
|
|
366
|
+
continue;
|
|
367
|
+
pendingReads.delete(tr.toolCallId);
|
|
368
|
+
if (tr.isError)
|
|
369
|
+
continue;
|
|
370
|
+
if (dedupStub) {
|
|
371
|
+
const text = tr.content.map((c) => ("text" in c ? c.text : "")).join("");
|
|
372
|
+
if (dedupStub(text))
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
keptTailReadPathsRaw.add(path);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const instructionPathsRaw = new Set(att?.instructionSourcePaths ?? []);
|
|
380
|
+
if (keptTailReadPathsRaw.size === 0 && instructionPathsRaw.size === 0)
|
|
381
|
+
return rawCandidateFiles;
|
|
382
|
+
const normalize = att?.normalizePath;
|
|
383
|
+
if (!normalize) {
|
|
384
|
+
excludedReadStatePreserveKeys.push(...keptTailReadPathsRaw, ...instructionPathsRaw);
|
|
385
|
+
return rawCandidateFiles.filter((p) => !keptTailReadPathsRaw.has(p) && !instructionPathsRaw.has(p));
|
|
386
|
+
}
|
|
387
|
+
const normalizeSafe = async (p) => {
|
|
388
|
+
try {
|
|
389
|
+
return await normalize(p);
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
return p;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const normalizeCap = Math.max(maxFilesForSelection * 4, 12);
|
|
396
|
+
const boundedCandidates = rawCandidateFiles.slice(0, normalizeCap);
|
|
397
|
+
const overflowCandidates = rawCandidateFiles.slice(normalizeCap);
|
|
398
|
+
const [normBounded, normKeptTail, normInstruction] = await Promise.all([
|
|
399
|
+
Promise.all(boundedCandidates.map(normalizeSafe)),
|
|
400
|
+
Promise.all([...keptTailReadPathsRaw].map(normalizeSafe)),
|
|
401
|
+
Promise.all([...instructionPathsRaw].map(normalizeSafe)),
|
|
402
|
+
]);
|
|
403
|
+
excludedReadStatePreserveKeys.push(...normKeptTail, ...normInstruction);
|
|
404
|
+
const excluded = new Set([...normKeptTail, ...normInstruction]);
|
|
405
|
+
const filteredBounded = boundedCandidates.filter((_, i) => !excluded.has(normBounded[i]));
|
|
406
|
+
return [...filteredBounded, ...overflowCandidates];
|
|
407
|
+
})()
|
|
408
|
+
: rawCandidateFiles;
|
|
322
409
|
if (att && Array.isArray(candidateFiles) && candidateFiles.length > 0) {
|
|
323
|
-
const maxFiles =
|
|
410
|
+
const maxFiles = maxFilesForSelection;
|
|
324
411
|
const perFileCap = Math.max(200, att.maxCharsPerFile ?? 16_000);
|
|
325
412
|
let remaining = Math.min(maxFiles * perFileCap, Math.floor(window * 0.15) * cpt);
|
|
326
413
|
const blocks = [];
|
|
@@ -369,7 +456,7 @@ export async function maybeCompact(opts) {
|
|
|
369
456
|
...(restatedListings !== undefined ? { announcedListings: restatedListings } : {}),
|
|
370
457
|
}, false);
|
|
371
458
|
try {
|
|
372
|
-
opts.onApplied?.(attachedComplete);
|
|
459
|
+
opts.onApplied?.(attachedComplete, excludedReadStatePreserveKeys);
|
|
373
460
|
}
|
|
374
461
|
catch {
|
|
375
462
|
}
|
|
@@ -194,10 +194,12 @@ export interface Prepared {
|
|
|
194
194
|
promptOverheadTokens: number;
|
|
195
195
|
readTaskFile?: (path: string) => Promise<string | null>;
|
|
196
196
|
recentlyReadFiles?: () => string[];
|
|
197
|
+
normalizeAttachmentPath?: (raw: string) => Promise<string>;
|
|
198
|
+
isDedupStubResult?: (resultText: string) => boolean;
|
|
197
199
|
onCompactionApplied?: (attachedComplete: ReadonlyArray<{
|
|
198
200
|
path: string;
|
|
199
201
|
content: string;
|
|
200
|
-
}>) => void;
|
|
202
|
+
}>, preserveReadState?: ReadonlyArray<string>) => void;
|
|
201
203
|
lspDiagnostics?: {
|
|
202
204
|
registry: import("../lsp-diagnostics.js").LspDiagnosticsRegistry;
|
|
203
205
|
nudge: (rawPath: string) => void;
|
|
@@ -55,7 +55,7 @@ import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.j
|
|
|
55
55
|
import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
|
|
56
56
|
import { createMonitorTool } from "../../tools/monitor.js";
|
|
57
57
|
import { createWorktreeTools } from "../../tools/worktree.js";
|
|
58
|
-
import { applyCompactionToReadFileState, bashReversibilityProbe, createHandsToolkit, seedReadFileStateFromContext, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
58
|
+
import { applyCompactionToReadFileState, bashReversibilityProbe, createHandsToolkit, isReadDedupStubResult, seedReadFileStateFromContext, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
59
59
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
60
60
|
import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool } from "../ask-question.js";
|
|
61
61
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
@@ -3291,6 +3291,20 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
|
|
|
3291
3291
|
}
|
|
3292
3292
|
}
|
|
3293
3293
|
: undefined;
|
|
3294
|
+
const normalizeAttachmentPath = handsEnabled
|
|
3295
|
+
? async (path) => {
|
|
3296
|
+
if (attachmentRootCanonical === undefined)
|
|
3297
|
+
return path;
|
|
3298
|
+
try {
|
|
3299
|
+
const rk = await resolveKey(executionEnv, attachmentRootCanonical, path, undefined, handsCwdRef?.current, additionalRootsCanonical);
|
|
3300
|
+
return rk.ok ? rk.key : path;
|
|
3301
|
+
}
|
|
3302
|
+
catch {
|
|
3303
|
+
return path;
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
: undefined;
|
|
3307
|
+
const isDedupStubResult = handsEnabled ? isReadDedupStubResult : undefined;
|
|
3294
3308
|
const recentlyReadFiles = handsEnabled && readFileStateForCheckpoint
|
|
3295
3309
|
? () => [...readFileStateForCheckpoint.entries()]
|
|
3296
3310
|
.filter(([, v]) => v.seededFromContext !== true)
|
|
@@ -3298,7 +3312,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
|
|
|
3298
3312
|
.map(([path]) => path)
|
|
3299
3313
|
: undefined;
|
|
3300
3314
|
const onCompactionApplied = handsEnabled && readFileStateForCheckpoint
|
|
3301
|
-
? (attachedComplete) => applyCompactionToReadFileState(readFileStateForCheckpoint, attachedComplete)
|
|
3315
|
+
? (attachedComplete, preserveReadState) => applyCompactionToReadFileState(readFileStateForCheckpoint, attachedComplete, preserveReadState)
|
|
3302
3316
|
: undefined;
|
|
3303
3317
|
const detectExternalChanges = handsEnabled && readFileStateForCheckpoint
|
|
3304
3318
|
? async (maxFiles) => {
|
|
@@ -3382,7 +3396,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
|
|
|
3382
3396
|
: undefined;
|
|
3383
3397
|
overheadState.promptChars = systemPrompt.length;
|
|
3384
3398
|
const preparedHolder = {};
|
|
3385
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ownedEnv, suspendRef, reviewRef, reviewRequestRef, suspendLoopRef, suspendForResource, ...(callCapRef ? { callCapRef } : {}), ...(cutKills ? { cutKills } : {}), ...(callCapRef ? { callIssuedAtRef } : {}), suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, promptOverheadTokens, readTaskFile, recentlyReadFiles, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3399
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ownedEnv, suspendRef, reviewRef, reviewRequestRef, suspendLoopRef, suspendForResource, ...(callCapRef ? { callCapRef } : {}), ...(cutKills ? { cutKills } : {}), ...(callCapRef ? { callIssuedAtRef } : {}), suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3386
3400
|
const prepared = buildPrepared();
|
|
3387
3401
|
preparedHolder.current = prepared;
|
|
3388
3402
|
return prepared;
|
|
@@ -1823,6 +1823,11 @@ export class Runner {
|
|
|
1823
1823
|
? {
|
|
1824
1824
|
readFile: prepared.readTaskFile,
|
|
1825
1825
|
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
|
|
1826
|
+
...(prepared.normalizeAttachmentPath ? { normalizePath: prepared.normalizeAttachmentPath } : {}),
|
|
1827
|
+
...(prepared.isDedupStubResult ? { isDedupStubResult: prepared.isDedupStubResult } : {}),
|
|
1828
|
+
...(prepared.instructionSources
|
|
1829
|
+
? { instructionSourcePaths: prepared.instructionSources.filter((s) => s.contentHash !== null).map((s) => s.path) }
|
|
1830
|
+
: {}),
|
|
1826
1831
|
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
|
|
1827
1832
|
}
|
|
1828
1833
|
: undefined,
|
|
@@ -2257,6 +2262,11 @@ export class Runner {
|
|
|
2257
2262
|
? {
|
|
2258
2263
|
readFile: prepared.readTaskFile,
|
|
2259
2264
|
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
|
|
2265
|
+
...(prepared.normalizeAttachmentPath ? { normalizePath: prepared.normalizeAttachmentPath } : {}),
|
|
2266
|
+
...(prepared.isDedupStubResult ? { isDedupStubResult: prepared.isDedupStubResult } : {}),
|
|
2267
|
+
...(prepared.instructionSources
|
|
2268
|
+
? { instructionSourcePaths: prepared.instructionSources.filter((s) => s.contentHash !== null).map((s) => s.path) }
|
|
2269
|
+
: {}),
|
|
2260
2270
|
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
|
|
2261
2271
|
}
|
|
2262
2272
|
: undefined,
|
|
@@ -3539,6 +3549,11 @@ export class Runner {
|
|
|
3539
3549
|
? {
|
|
3540
3550
|
readFile: prepared.readTaskFile,
|
|
3541
3551
|
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
|
|
3552
|
+
...(prepared.normalizeAttachmentPath ? { normalizePath: prepared.normalizeAttachmentPath } : {}),
|
|
3553
|
+
...(prepared.isDedupStubResult ? { isDedupStubResult: prepared.isDedupStubResult } : {}),
|
|
3554
|
+
...(prepared.instructionSources
|
|
3555
|
+
? { instructionSourcePaths: prepared.instructionSources.filter((s) => s.contentHash !== null).map((s) => s.path) }
|
|
3556
|
+
: {}),
|
|
3542
3557
|
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
|
|
3543
3558
|
}
|
|
3544
3559
|
: undefined,
|
|
@@ -97,6 +97,7 @@ interface BackgroundAgentTaskHandle extends SemaTaskHandle {
|
|
|
97
97
|
name?: string;
|
|
98
98
|
sessionScoped?: true;
|
|
99
99
|
result?: string;
|
|
100
|
+
resultFull?: string;
|
|
100
101
|
error?: string;
|
|
101
102
|
resultIsPartial?: boolean;
|
|
102
103
|
stopSource?: StopSource;
|
|
@@ -291,6 +292,7 @@ export declare class TaskRegistry {
|
|
|
291
292
|
settleBackgroundAgent(id: string, outcome: {
|
|
292
293
|
status: "completed" | "failed" | "killed";
|
|
293
294
|
result?: string;
|
|
295
|
+
resultFull?: string;
|
|
294
296
|
error?: string;
|
|
295
297
|
stoppedBy?: StopSource;
|
|
296
298
|
seq?: number;
|
|
@@ -353,6 +355,7 @@ export declare class TaskRegistry {
|
|
|
353
355
|
settleRevivedAgent(id: string, cycle: number, outcome: {
|
|
354
356
|
status: "completed" | "failed" | "killed";
|
|
355
357
|
result?: string;
|
|
358
|
+
resultFull?: string;
|
|
356
359
|
error?: string;
|
|
357
360
|
}): "completed" | "failed" | "killed" | undefined;
|
|
358
361
|
unmarkRetainedContinuation(id: string): void;
|
|
@@ -1074,6 +1074,8 @@ export class TaskRegistry {
|
|
|
1074
1074
|
handle.result = outcome.result;
|
|
1075
1075
|
handle.resultIsPartial = true;
|
|
1076
1076
|
backfilled = true;
|
|
1077
|
+
if (outcome.resultFull !== undefined)
|
|
1078
|
+
handle.resultFull = outcome.resultFull;
|
|
1077
1079
|
}
|
|
1078
1080
|
if (handle.error === undefined && outcome.error !== undefined && outcome.error !== BG_AGENT_REAP_STOP_ERROR) {
|
|
1079
1081
|
handle.error = outcome.error;
|
|
@@ -1083,6 +1085,7 @@ export class TaskRegistry {
|
|
|
1083
1085
|
handle.updatedAt = Date.now();
|
|
1084
1086
|
this.durableAgentWrite(handle, {
|
|
1085
1087
|
...(handle.result !== undefined ? { finalOutput: handle.result } : {}),
|
|
1088
|
+
...(handle.resultFull !== undefined ? { finalOutputFull: handle.resultFull } : {}),
|
|
1086
1089
|
...(handle.resultIsPartial ? { resultIsPartial: true } : {}),
|
|
1087
1090
|
...(handle.error !== undefined ? { error: handle.error } : {}),
|
|
1088
1091
|
});
|
|
@@ -1103,6 +1106,8 @@ export class TaskRegistry {
|
|
|
1103
1106
|
}
|
|
1104
1107
|
if (outcome.result !== undefined)
|
|
1105
1108
|
handle.result = outcome.result;
|
|
1109
|
+
if (outcome.resultFull !== undefined)
|
|
1110
|
+
handle.resultFull = outcome.resultFull;
|
|
1106
1111
|
if (outcome.status === "killed" && outcome.result !== undefined && outcome.result !== "") {
|
|
1107
1112
|
handle.resultIsPartial = true;
|
|
1108
1113
|
}
|
|
@@ -1119,6 +1124,7 @@ export class TaskRegistry {
|
|
|
1119
1124
|
settledAt: Date.now(),
|
|
1120
1125
|
...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
|
|
1121
1126
|
...(handle.result !== undefined ? { finalOutput: handle.result } : {}),
|
|
1127
|
+
...(handle.resultFull !== undefined ? { finalOutputFull: handle.resultFull } : {}),
|
|
1122
1128
|
...(handle.resultIsPartial ? { resultIsPartial: true } : {}),
|
|
1123
1129
|
...(handle.error !== undefined ? { error: handle.error } : {}),
|
|
1124
1130
|
}, ["parkedCheckpointToken", "parkClaimId", "parkedAt"]);
|
|
@@ -1841,7 +1847,7 @@ The agent is durably suspended, waiting for an approval decision. It resumes whe
|
|
|
1841
1847
|
const body = `status: ${row.status}
|
|
1842
1848
|
${row.error ? `error: ${row.error}
|
|
1843
1849
|
` : ""}${row.finalOutput ? `--- result${row.resultIsPartial ? " (partial — produced before the task was stopped)" : ""} ---
|
|
1844
|
-
${clipTaskOutput(row.finalOutput)}` : "(no result text)"}`;
|
|
1850
|
+
${clipTaskOutput(row.finalOutputFull ?? row.finalOutput)}` : "(no result text)"}`;
|
|
1845
1851
|
return {
|
|
1846
1852
|
content: delimitUntrusted(`TaskOutput ${row.handle}`, body),
|
|
1847
1853
|
details: {
|
|
@@ -2019,13 +2025,14 @@ ${clipTaskOutput(row.finalOutput)}` : "(no result text)"}`;
|
|
|
2019
2025
|
if (abort !== undefined)
|
|
2020
2026
|
handle.abort = abort;
|
|
2021
2027
|
handle.result = undefined;
|
|
2028
|
+
handle.resultFull = undefined;
|
|
2022
2029
|
handle.error = undefined;
|
|
2023
2030
|
handle.resultIsPartial = undefined;
|
|
2024
2031
|
handle.stopSource = undefined;
|
|
2025
2032
|
handle.reviveCycle = (handle.reviveCycle ?? 0) + 1;
|
|
2026
2033
|
handle.cycleSeq = (handle.cycleSeq ?? 1) + 1;
|
|
2027
2034
|
handle.updatedAt = Date.now();
|
|
2028
|
-
this.durableAgentWrite(handle, { status: "running" }, ["settledAt", "stoppedBy", "finalOutput", "error", "resultIsPartial", "summary", "recentSteps", "editedFiles", "usage"]);
|
|
2035
|
+
this.durableAgentWrite(handle, { status: "running" }, ["settledAt", "stoppedBy", "finalOutput", "finalOutputFull", "error", "resultIsPartial", "summary", "recentSteps", "editedFiles", "usage"]);
|
|
2029
2036
|
return { ok: true, cycle: handle.reviveCycle };
|
|
2030
2037
|
}
|
|
2031
2038
|
settleRevivedAgent(id, cycle, outcome) {
|
|
@@ -2579,7 +2586,7 @@ The agent is still working — you will be notified when it completes.`
|
|
|
2579
2586
|
: `status: ${handle.status}
|
|
2580
2587
|
${handle.error ? `error: ${handle.error}
|
|
2581
2588
|
` : ""}${handle.result ? `--- result${handle.resultIsPartial ? " (partial — produced before the task was stopped)" : ""} ---
|
|
2582
|
-
${clipTaskOutput(handle.result, handle.outputFile)}` : "(no result text)"}`;
|
|
2589
|
+
${clipTaskOutput(handle.resultFull ?? handle.result, handle.outputFile)}` : "(no result text)"}`;
|
|
2583
2590
|
return {
|
|
2584
2591
|
content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
|
|
2585
2592
|
details: {
|
package/dist/tools/fs/index.d.ts
CHANGED
|
@@ -10,11 +10,12 @@ export declare const MAX_EDIT_BYTES: number;
|
|
|
10
10
|
export type ReadImageDownsamplerOption = ImageDownsampler | false | undefined;
|
|
11
11
|
export declare function msTimeoutToSec(timeoutMs: number | undefined): number;
|
|
12
12
|
export declare function seededFileUnchangedReminder(filePath: string): string;
|
|
13
|
+
export declare function isReadDedupStubResult(resultText: string): boolean;
|
|
13
14
|
export declare function seedReadFileStateFromContext(state: ReadFileState, key: string, content: string): void;
|
|
14
15
|
export declare function applyCompactionToReadFileState(state: ReadFileState, attachedComplete: ReadonlyArray<{
|
|
15
16
|
path: string;
|
|
16
17
|
content: string;
|
|
17
|
-
}>): void;
|
|
18
|
+
}>, preserveKeys?: ReadonlyArray<string>): void;
|
|
18
19
|
export interface CwdRef {
|
|
19
20
|
current: string;
|
|
20
21
|
}
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -102,12 +102,17 @@ function countLines(s) {
|
|
|
102
102
|
export function seededFileUnchangedReminder(filePath) {
|
|
103
103
|
return `<system-reminder>This file is already in your context (see "Contents of ${filePath}" above) and has not changed on disk. Use that content instead of re-reading.</system-reminder>`;
|
|
104
104
|
}
|
|
105
|
+
export function isReadDedupStubResult(resultText) {
|
|
106
|
+
return (resultText.includes("unchanged since you last read it") ||
|
|
107
|
+
resultText.includes("has not changed on disk. Use that content instead of re-reading"));
|
|
108
|
+
}
|
|
105
109
|
export function seedReadFileStateFromContext(state, key, content) {
|
|
106
110
|
state.set(key, { hash: sha256(content), totalLines: countLines(content), truncated: false, lastReadAt: Date.now(), seededFromContext: true });
|
|
107
111
|
}
|
|
108
|
-
export function applyCompactionToReadFileState(state, attachedComplete) {
|
|
112
|
+
export function applyCompactionToReadFileState(state, attachedComplete, preserveKeys = []) {
|
|
113
|
+
const preserve = new Set(preserveKeys);
|
|
109
114
|
for (const [k, v] of [...state]) {
|
|
110
|
-
if (v.seededFromContext !== true)
|
|
115
|
+
if (v.seededFromContext !== true && !preserve.has(k))
|
|
111
116
|
state.delete(k);
|
|
112
117
|
}
|
|
113
118
|
for (const f of attachedComplete) {
|