@sema-agent/core 2.4.0 → 2.5.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.js +17 -14
- package/dist/core/auto-compaction.d.ts +2 -0
- package/dist/core/auto-compaction.js +2 -1
- package/dist/core/mcp.js +4 -1
- package/dist/core/runner/prepare-task.d.ts +5 -0
- package/dist/core/runner/prepare-task.js +21 -1
- package/dist/core/runner/runtask.js +39 -9
- package/dist/core/runner/tool-disclosure.d.ts +1 -0
- package/dist/core/runner/tool-disclosure.js +16 -5
- package/dist/core/session-reconcile.d.ts +1 -0
- package/dist/core/session-reconcile.js +40 -20
- package/dist/core/task-registry-agent.d.ts +2 -0
- package/dist/core/task-registry-agent.js +11 -1
- package/dist/core/task-registry-shared.d.ts +1 -0
- package/dist/core/task-registry.d.ts +2 -0
- package/dist/core/task-registry.js +11 -0
- package/dist/core/types.d.ts +10 -3
- package/dist/engine/compaction/compaction.d.ts +5 -0
- package/dist/engine/compaction/compaction.js +68 -2
- package/dist/engine/compaction/utils.d.ts +6 -0
- package/dist/engine/compaction/utils.js +53 -3
- package/dist/engine/harness/messages.d.ts +1 -1
- package/dist/engine/harness/messages.js +11 -3
- package/dist/engine/loop/types.d.ts +2 -0
- package/dist/engine/session/import-validate.js +30 -1
- package/dist/engine/session/session.js +7 -5
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/orchestration/workflow-types.d.ts +1 -0
- package/dist/orchestration/workflow.js +10 -1
- package/dist/tools/fs/fs-bash.js +11 -5
- package/package.json +1 -1
|
@@ -706,6 +706,8 @@ export function settleBackgroundAgentLane(core, id, outcome) {
|
|
|
706
706
|
handle.errorCode = outcome.errorCode;
|
|
707
707
|
if (outcome.retryable !== undefined)
|
|
708
708
|
handle.errorRetryable = outcome.retryable;
|
|
709
|
+
if (outcome.errorKind !== undefined)
|
|
710
|
+
handle.errorKind = outcome.errorKind;
|
|
709
711
|
}
|
|
710
712
|
handle.updatedAt = Date.now();
|
|
711
713
|
if (outcome.seq !== undefined)
|
|
@@ -844,6 +846,9 @@ export function reviveBackgroundAgentLane(core, id, access, abort) {
|
|
|
844
846
|
handle.resultFull = undefined;
|
|
845
847
|
handle.spillRef = undefined;
|
|
846
848
|
handle.error = undefined;
|
|
849
|
+
handle.errorCode = undefined;
|
|
850
|
+
handle.errorRetryable = undefined;
|
|
851
|
+
handle.errorKind = undefined;
|
|
847
852
|
handle.resultIsPartial = undefined;
|
|
848
853
|
handle.stopSource = undefined;
|
|
849
854
|
handle.completionId = undefined;
|
|
@@ -1030,6 +1035,7 @@ ${clipTaskOutput(row.finalOutputFull ?? row.finalOutput)}` : "(no result text)"}
|
|
|
1030
1035
|
...(row.resultIsPartial ? { partial_result: true } : {}),
|
|
1031
1036
|
...(row.completionId !== undefined ? { completionId: row.completionId } : {}),
|
|
1032
1037
|
},
|
|
1038
|
+
...(row.status === "failed" ? { isError: true } : {}),
|
|
1033
1039
|
};
|
|
1034
1040
|
}
|
|
1035
1041
|
export async function spillClippedAgentResult(handle, full, clipped, store, sessionId) {
|
|
@@ -1065,6 +1071,9 @@ The agent is durably suspended, waiting for an approval decision. It resumes whe
|
|
|
1065
1071
|
}
|
|
1066
1072
|
const fullResult = handle.result ? (handle.resultFull ?? handle.result) : undefined;
|
|
1067
1073
|
const resultText = fullResult !== undefined ? await spillClippedAgentResult(handle, fullResult, clipTaskOutput(fullResult, handle.outputFile), store, sessionId) : undefined;
|
|
1074
|
+
const kindClause = handle.status === "failed" && handle.errorKind !== undefined && handle.errorRetryable !== undefined
|
|
1075
|
+
? ` (error_kind: ${handle.errorKind}, retryable: ${handle.errorRetryable})`
|
|
1076
|
+
: "";
|
|
1068
1077
|
const body = running
|
|
1069
1078
|
? oneShot === true
|
|
1070
1079
|
? `status: running
|
|
@@ -1072,7 +1081,7 @@ This is a ONE-SHOT submission — there is no later turn for a background notifi
|
|
|
1072
1081
|
: `status: running
|
|
1073
1082
|
The agent is still working — you will be notified when it completes.`
|
|
1074
1083
|
: `status: ${handle.status}
|
|
1075
|
-
${handle.error ? `error: ${handle.error}
|
|
1084
|
+
${handle.error ? `error: ${handle.error}${kindClause}
|
|
1076
1085
|
` : ""}${handle.result ? `--- result${handle.resultIsPartial ? " (partial — produced before the task was stopped)" : ""} ---
|
|
1077
1086
|
${resultText}` : "(no result text)"}`;
|
|
1078
1087
|
return {
|
|
@@ -1092,6 +1101,7 @@ ${resultText}` : "(no result text)"}`;
|
|
|
1092
1101
|
...(handle.resultIsPartial ? { partial_result: true } : {}),
|
|
1093
1102
|
...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
|
|
1094
1103
|
},
|
|
1104
|
+
...(handle.status === "failed" ? { isError: true } : {}),
|
|
1095
1105
|
};
|
|
1096
1106
|
}
|
|
1097
1107
|
export async function stopBackgroundAgentLane(core, handle) {
|
|
@@ -135,6 +135,7 @@ export declare class TaskRegistry {
|
|
|
135
135
|
error?: string;
|
|
136
136
|
errorCode?: string;
|
|
137
137
|
retryable?: boolean;
|
|
138
|
+
errorKind?: string;
|
|
138
139
|
stoppedBy?: StopSource;
|
|
139
140
|
seq?: number;
|
|
140
141
|
}): "completed" | "failed" | "killed" | undefined;
|
|
@@ -170,6 +171,7 @@ export declare class TaskRegistry {
|
|
|
170
171
|
error?: string;
|
|
171
172
|
errorCode?: string;
|
|
172
173
|
retryable?: boolean;
|
|
174
|
+
errorKind?: string;
|
|
173
175
|
}): "completed" | "failed" | "killed" | undefined;
|
|
174
176
|
unmarkRetainedContinuation(id: string): void;
|
|
175
177
|
attachAgentNotify(id: string, notify: NonNullable<BackgroundAgentTaskHandle["notify"]>, cycle?: number): void;
|
|
@@ -368,6 +368,17 @@ export class TaskRegistry {
|
|
|
368
368
|
async settleKilledForOwner(access, opts) {
|
|
369
369
|
const source = opts?.source ?? "parent";
|
|
370
370
|
const clause = (by) => by === "user" ? "stopped by user" : by === "parent" ? "its parent run ended" : `stopped by ${by}`;
|
|
371
|
+
if (source === "user") {
|
|
372
|
+
for (const handle of this.handles.values()) {
|
|
373
|
+
if (handle.type !== "background_agent" || handle.status !== "running")
|
|
374
|
+
continue;
|
|
375
|
+
if (!canAccess(handle, { owner: access.owner, scope: access.scope }))
|
|
376
|
+
continue;
|
|
377
|
+
if (opts?.skipSessionScoped && handle.sessionScoped)
|
|
378
|
+
continue;
|
|
379
|
+
this.markStopSource(handle.id, source);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
371
382
|
let settled = 0;
|
|
372
383
|
for (const handle of this.handles.values()) {
|
|
373
384
|
if (handle.type !== "background_bash" && handle.type !== "monitor")
|
package/dist/core/types.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
47
47
|
offload?: boolean;
|
|
48
48
|
offloadThresholdChars?: number;
|
|
49
49
|
defer?: boolean;
|
|
50
|
+
alwaysLoad?: boolean;
|
|
50
51
|
contract?: {
|
|
51
52
|
contractId: string;
|
|
52
53
|
implementationRevision: string;
|
|
@@ -256,6 +257,7 @@ export interface TaskSpec {
|
|
|
256
257
|
tools?: ToolSpec[];
|
|
257
258
|
excludeTools?: string[];
|
|
258
259
|
deferTools?: string[];
|
|
260
|
+
alwaysLoadTools?: string[];
|
|
259
261
|
promptProfile?: "simple" | "classic";
|
|
260
262
|
agents?: AgentDefinition[];
|
|
261
263
|
toolPolicy?: import("./tool-policy.js").ToolPolicy;
|
|
@@ -514,9 +516,9 @@ export type TaskEvent = ({
|
|
|
514
516
|
};
|
|
515
517
|
usageMissing?: true;
|
|
516
518
|
stopReason?: string;
|
|
517
|
-
} & TaskEventIdentity) | {
|
|
519
|
+
} & TaskEventIdentity) | ({
|
|
518
520
|
type: "compacted";
|
|
519
|
-
trigger: "auto" | "manual";
|
|
521
|
+
trigger: "auto" | "manual" | "forced";
|
|
520
522
|
tokensBefore: number;
|
|
521
523
|
tokensAfter?: number;
|
|
522
524
|
triggerTokensBefore?: number;
|
|
@@ -534,7 +536,12 @@ export type TaskEvent = ({
|
|
|
534
536
|
clampedRatio?: number;
|
|
535
537
|
clampReason?: "budget" | "walltime" | "tolerance";
|
|
536
538
|
phaseDurations?: import("./auto-compaction.js").CompactionPhaseDurations;
|
|
537
|
-
} | ({
|
|
539
|
+
} & TaskEventIdentity) | ({
|
|
540
|
+
type: "compaction_outcome";
|
|
541
|
+
outcome: Exclude<CompactOutcome, "compacted"> | "suppressed";
|
|
542
|
+
trigger: "auto" | "manual" | "forced";
|
|
543
|
+
reason?: string;
|
|
544
|
+
} & TaskEventIdentity) | ({
|
|
538
545
|
type: "steering_injected";
|
|
539
546
|
source: "deadline_nudge" | "finalize" | "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools";
|
|
540
547
|
preview: string;
|
|
@@ -8,6 +8,9 @@ export interface CompactionDetails {
|
|
|
8
8
|
modifiedFiles: string[];
|
|
9
9
|
modifiedFilesByRecency?: string[];
|
|
10
10
|
invokedSkills?: InvokedSkillRetention[];
|
|
11
|
+
elidedMessages?: number;
|
|
12
|
+
persistedOutputRefs?: string[];
|
|
13
|
+
activeTools?: string[];
|
|
11
14
|
}
|
|
12
15
|
export interface CompactionResult<T = unknown> {
|
|
13
16
|
summary: string;
|
|
@@ -68,6 +71,8 @@ export interface CompactionPreparation {
|
|
|
68
71
|
previousSummary?: string;
|
|
69
72
|
fileOps: FileOperations;
|
|
70
73
|
invokedSkills: InvokedSkillRetention[];
|
|
74
|
+
persistedOutputRefs?: string[];
|
|
75
|
+
elidedMessages?: number;
|
|
71
76
|
settings: CompactionSettings;
|
|
72
77
|
}
|
|
73
78
|
export declare function prepareCompaction(pathEntries: SessionTreeEntry[], settings: CompactionSettings, charsPerToken?: number, windowTokens?: number): Result<CompactionPreparation | undefined, CompactionError>;
|
|
@@ -2,7 +2,7 @@ import { resolveAgentCoreCompleteFn, } from "../loop/runtime-deps.js";
|
|
|
2
2
|
import { asAgentMessage, convertToLlm, createCompactionSummaryMessage, createCustomMessage, } from "../harness/messages.js";
|
|
3
3
|
import { buildSessionContext } from "../session/session.js";
|
|
4
4
|
import { CompactionError, err, ok, } from "../harness/types.js";
|
|
5
|
-
import { budgetInvokedSkillsRetention, computeFileLists, createFileOps, extractFileOpsFromMessage, extractInvokedSkills, formatFileOperations, readRetainedInvokedSkills, replaceInvokedSkillBodiesForSummary, serializeConversation, stripFileOperationsFooter, } from "./utils.js";
|
|
5
|
+
import { budgetInvokedSkillsRetention, computeFileLists, createFileOps, extractFileOpsFromMessage, extractInvokedSkills, extractPersistedOutputRefs, formatFileOperations, formatPersistedOutputRefs, PERSISTED_OUTPUT_REFS_MAX_ENTRIES, readElidedMessages, readPersistedOutputRefs, readRetainedInvokedSkills, replaceInvokedSkillBodiesForSummary, serializeConversation, stripFileOperationsFooter, } from "./utils.js";
|
|
6
6
|
function safeJsonStringify(value) {
|
|
7
7
|
try {
|
|
8
8
|
return JSON.stringify(value) ?? "undefined";
|
|
@@ -256,6 +256,51 @@ export function findTurnStartIndex(entries, entryIndex, startIndex) {
|
|
|
256
256
|
}
|
|
257
257
|
return -1;
|
|
258
258
|
}
|
|
259
|
+
function enforceToolPairContainment(entries, startIndex, endIndex, cutIndex) {
|
|
260
|
+
const callSites = new Map();
|
|
261
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
262
|
+
const entry = entries[i];
|
|
263
|
+
if (entry.type !== "message")
|
|
264
|
+
continue;
|
|
265
|
+
const msg = entry.message;
|
|
266
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content))
|
|
267
|
+
continue;
|
|
268
|
+
for (const block of msg.content) {
|
|
269
|
+
if (block.type === "toolCall" && typeof block.id === "string") {
|
|
270
|
+
const at = callSites.get(block.id);
|
|
271
|
+
if (at === undefined)
|
|
272
|
+
callSites.set(block.id, [i]);
|
|
273
|
+
else
|
|
274
|
+
at.push(i);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (callSites.size === 0)
|
|
279
|
+
return cutIndex;
|
|
280
|
+
for (let i = cutIndex; i < endIndex; i++) {
|
|
281
|
+
const entry = entries[i];
|
|
282
|
+
if (entry.type !== "message")
|
|
283
|
+
continue;
|
|
284
|
+
const msg = entry.message;
|
|
285
|
+
if (msg.role !== "toolResult")
|
|
286
|
+
continue;
|
|
287
|
+
const sites = callSites.get(msg.toolCallId);
|
|
288
|
+
if (sites === undefined)
|
|
289
|
+
continue;
|
|
290
|
+
let site = -1;
|
|
291
|
+
for (const s of sites) {
|
|
292
|
+
if (s < i)
|
|
293
|
+
site = s;
|
|
294
|
+
else
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
if (site === -1 || site >= cutIndex)
|
|
298
|
+
continue;
|
|
299
|
+
cutIndex = site;
|
|
300
|
+
i = site;
|
|
301
|
+
}
|
|
302
|
+
return cutIndex;
|
|
303
|
+
}
|
|
259
304
|
export function findCutPoint(entries, startIndex, endIndex, keepRecentTokens, charsPerToken = DEFAULT_CHARS_PER_TOKEN) {
|
|
260
305
|
const cutPoints = findValidCutPoints(entries, startIndex, endIndex);
|
|
261
306
|
if (cutPoints.length === 0) {
|
|
@@ -290,6 +335,7 @@ export function findCutPoint(entries, startIndex, endIndex, keepRecentTokens, ch
|
|
|
290
335
|
break;
|
|
291
336
|
}
|
|
292
337
|
}
|
|
338
|
+
cutIndex = enforceToolPairContainment(entries, startIndex, endIndex, cutIndex);
|
|
293
339
|
const cutEntry = entries[cutIndex];
|
|
294
340
|
const isUserMessage = cutEntry.type === "message" && cutEntry.message.role === "user";
|
|
295
341
|
const turnStartIndex = isUserMessage ? -1 : findTurnStartIndex(entries, cutIndex, startIndex);
|
|
@@ -721,6 +767,21 @@ export function prepareCompaction(pathEntries, settings, charsPerToken = DEFAULT
|
|
|
721
767
|
? Math.max(0, windowTokens - settings.reserveTokens - settings.keepRecentTokens) * charsPerToken
|
|
722
768
|
: undefined;
|
|
723
769
|
const invokedSkills = budgetInvokedSkillsRetention(extractInvokedSkills([...messagesToSummarize, ...turnPrefixMessages], prevRetainedSkills), retentionBudgetChars);
|
|
770
|
+
const refSet = new Set();
|
|
771
|
+
if (prevCompactionIndex >= 0 && !pathEntries[prevCompactionIndex].fromHook) {
|
|
772
|
+
for (const r of readPersistedOutputRefs(pathEntries[prevCompactionIndex].details)) {
|
|
773
|
+
refSet.add(r);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
extractPersistedOutputRefs(messagesToSummarize, refSet);
|
|
777
|
+
if (cutPoint.isSplitTurn) {
|
|
778
|
+
extractPersistedOutputRefs(turnPrefixMessages, refSet);
|
|
779
|
+
}
|
|
780
|
+
const persistedOutputRefs = [...refSet].slice(-PERSISTED_OUTPUT_REFS_MAX_ENTRIES);
|
|
781
|
+
const prevElided = prevCompactionIndex >= 0 && !pathEntries[prevCompactionIndex].fromHook
|
|
782
|
+
? readElidedMessages(pathEntries[prevCompactionIndex].details)
|
|
783
|
+
: undefined;
|
|
784
|
+
const elidedMessages = (prevElided ?? 0) + messagesToSummarize.length + turnPrefixMessages.length;
|
|
724
785
|
const [summarizeReady, prefixReady] = replaceInvokedSkillBodiesForSummary([messagesToSummarize, turnPrefixMessages], new Set(invokedSkills.map((s) => s.name)));
|
|
725
786
|
return ok({
|
|
726
787
|
firstKeptEntryId,
|
|
@@ -731,6 +792,8 @@ export function prepareCompaction(pathEntries, settings, charsPerToken = DEFAULT
|
|
|
731
792
|
previousSummary,
|
|
732
793
|
fileOps,
|
|
733
794
|
invokedSkills,
|
|
795
|
+
persistedOutputRefs,
|
|
796
|
+
elidedMessages,
|
|
734
797
|
settings,
|
|
735
798
|
});
|
|
736
799
|
}
|
|
@@ -750,7 +813,7 @@ Summarize the prefix to provide context for the retained suffix:
|
|
|
750
813
|
Be concise. Focus on what's needed to understand the kept suffix.`;
|
|
751
814
|
export { computeFileLists, serializeConversation } from "./utils.js";
|
|
752
815
|
export async function compact(preparation, model, apiKey, headers, customInstructions, signal, thinkingLevel, streamFn, runtime, charsPerToken, onInputTruncated, onPtlRetry) {
|
|
753
|
-
const { firstKeptEntryId, messagesToSummarize, turnPrefixMessages, isSplitTurn, tokensBefore, previousSummary, fileOps, invokedSkills, settings, } = preparation;
|
|
816
|
+
const { firstKeptEntryId, messagesToSummarize, turnPrefixMessages, isSplitTurn, tokensBefore, previousSummary, fileOps, invokedSkills, persistedOutputRefs, elidedMessages, settings, } = preparation;
|
|
754
817
|
if (!firstKeptEntryId) {
|
|
755
818
|
return err(new CompactionError("invalid_session", "First kept entry has no UUID - session may need migration"));
|
|
756
819
|
}
|
|
@@ -784,6 +847,7 @@ export async function compact(preparation, model, apiKey, headers, customInstruc
|
|
|
784
847
|
}
|
|
785
848
|
const { readFiles, modifiedFiles, modifiedFilesByRecency } = computeFileLists(fileOps);
|
|
786
849
|
summary += formatFileOperations(readFiles, modifiedFiles);
|
|
850
|
+
summary += formatPersistedOutputRefs(persistedOutputRefs ?? []);
|
|
787
851
|
return ok({
|
|
788
852
|
summary,
|
|
789
853
|
firstKeptEntryId,
|
|
@@ -793,6 +857,8 @@ export async function compact(preparation, model, apiKey, headers, customInstruc
|
|
|
793
857
|
modifiedFiles,
|
|
794
858
|
modifiedFilesByRecency,
|
|
795
859
|
...(invokedSkills !== undefined && invokedSkills.length > 0 ? { invokedSkills } : {}),
|
|
860
|
+
...(persistedOutputRefs !== undefined && persistedOutputRefs.length > 0 ? { persistedOutputRefs } : {}),
|
|
861
|
+
...(elidedMessages !== undefined && elidedMessages > 0 ? { elidedMessages } : {}),
|
|
796
862
|
},
|
|
797
863
|
});
|
|
798
864
|
}
|
|
@@ -21,6 +21,12 @@ export declare function budgetInvokedSkillsRetention(newestFirst: InvokedSkillRe
|
|
|
21
21
|
export declare function replaceInvokedSkillBodiesForSummary(groups: AgentMessage[][], retainedNames: ReadonlySet<string>): AgentMessage[][];
|
|
22
22
|
export declare function readRetainedInvokedSkills(details: unknown): InvokedSkillRetention[];
|
|
23
23
|
export declare function renderInvokedSkillsRetention(skills: InvokedSkillRetention[]): string;
|
|
24
|
+
export declare const PERSISTED_OUTPUT_REFS_MAX_ENTRIES = 50;
|
|
25
|
+
export declare function extractPersistedOutputRefs(messages: AgentMessage[], into: Set<string>): void;
|
|
26
|
+
export declare function readElidedMessages(details: unknown): number | undefined;
|
|
27
|
+
export declare function readCompactionActiveTools(details: unknown): string[];
|
|
28
|
+
export declare function readPersistedOutputRefs(details: unknown): string[];
|
|
29
|
+
export declare function formatPersistedOutputRefs(refs: string[]): string;
|
|
24
30
|
export declare function computeFileLists(fileOps: FileOperations): {
|
|
25
31
|
readFiles: string[];
|
|
26
32
|
modifiedFiles: string[];
|
|
@@ -182,6 +182,52 @@ export function renderInvokedSkillsRetention(skills) {
|
|
|
182
182
|
`If a skill's content was truncated, invoke the Skill tool again for the full text.\n\n` +
|
|
183
183
|
`${sections.join("\n\n")}\n</invoked-skills>`);
|
|
184
184
|
}
|
|
185
|
+
const PERSISTED_OUTPUT_REF_RE = /<persisted-output ref="([^"]+)"/g;
|
|
186
|
+
const OFFLOAD_TOOL_NAME_LITERAL = "ReadToolResult";
|
|
187
|
+
export const PERSISTED_OUTPUT_REFS_MAX_ENTRIES = 50;
|
|
188
|
+
export function extractPersistedOutputRefs(messages, into) {
|
|
189
|
+
for (const msg of messages) {
|
|
190
|
+
if (msg.role !== "toolResult" || !Array.isArray(msg.content))
|
|
191
|
+
continue;
|
|
192
|
+
for (const block of msg.content) {
|
|
193
|
+
if (block.type !== "text" || typeof block.text !== "string")
|
|
194
|
+
continue;
|
|
195
|
+
for (const m of block.text.matchAll(PERSISTED_OUTPUT_REF_RE)) {
|
|
196
|
+
into.add(m[1]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export function readElidedMessages(details) {
|
|
202
|
+
if (details === null || typeof details !== "object")
|
|
203
|
+
return undefined;
|
|
204
|
+
const raw = details.elidedMessages;
|
|
205
|
+
return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 0 ? raw : undefined;
|
|
206
|
+
}
|
|
207
|
+
export function readCompactionActiveTools(details) {
|
|
208
|
+
if (details === null || typeof details !== "object")
|
|
209
|
+
return [];
|
|
210
|
+
const raw = details.activeTools;
|
|
211
|
+
if (!Array.isArray(raw))
|
|
212
|
+
return [];
|
|
213
|
+
return raw.filter((n) => typeof n === "string" && n.length > 0);
|
|
214
|
+
}
|
|
215
|
+
export function readPersistedOutputRefs(details) {
|
|
216
|
+
if (details === null || typeof details !== "object")
|
|
217
|
+
return [];
|
|
218
|
+
const raw = details.persistedOutputRefs;
|
|
219
|
+
if (!Array.isArray(raw))
|
|
220
|
+
return [];
|
|
221
|
+
return raw.filter((r) => typeof r === "string" && r.length > 0);
|
|
222
|
+
}
|
|
223
|
+
export function formatPersistedOutputRefs(refs) {
|
|
224
|
+
if (refs.length === 0)
|
|
225
|
+
return "";
|
|
226
|
+
return (`\n\n<persisted-tool-outputs>\n` +
|
|
227
|
+
`Tool outputs elided by compaction were persisted in full and remain retrievable — call ` +
|
|
228
|
+
`${OFFLOAD_TOOL_NAME_LITERAL} with a ref to read one back:\n` +
|
|
229
|
+
`${capList(refs)}\n</persisted-tool-outputs>`);
|
|
230
|
+
}
|
|
185
231
|
function touchModified(fileOps, path) {
|
|
186
232
|
fileOps.modifiedOrder.delete(path);
|
|
187
233
|
fileOps.modifiedOrder.add(path);
|
|
@@ -217,9 +263,13 @@ export function formatFileOperations(readFiles, modifiedFiles) {
|
|
|
217
263
|
return `\n\n${sections.join("\n\n")}`;
|
|
218
264
|
}
|
|
219
265
|
export function stripFileOperationsFooter(summary) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.replace(/\n\n<modified-files>\n[\s\S]*?\n
|
|
266
|
+
let out = summary;
|
|
267
|
+
for (;;) {
|
|
268
|
+
const next = out.replace(/\n\n<(read-files|modified-files|persisted-tool-outputs)>\n[\s\S]*?\n<\/\1>\s*$/, "");
|
|
269
|
+
if (next === out)
|
|
270
|
+
return out;
|
|
271
|
+
out = next;
|
|
272
|
+
}
|
|
223
273
|
}
|
|
224
274
|
const TOOL_RESULT_MAX_CHARS = 2000;
|
|
225
275
|
function safeJsonStringify(value) {
|
|
@@ -6,7 +6,7 @@ export declare function asAgentMessage(message: HarnessMessage): AgentMessage;
|
|
|
6
6
|
export declare const COMPACTION_SUMMARY_PREFIX = "This session is being continued from a previous conversation that ran out of context. The summary below covers the earlier portion of the conversation.\n\n<summary>\n";
|
|
7
7
|
export declare const COMPACTION_SUMMARY_SUFFIX = "\n</summary>\n\nRecent messages are preserved verbatim below. Continue the conversation from where it left off without asking the user any further questions. Resume directly \u2014 do not acknowledge the summary, do not recap what was happening, do not preface with \"I'll continue\" or similar. Pick up the last task as if the break never happened.";
|
|
8
8
|
export declare function bashExecutionToText(msg: BashExecutionMessage): string;
|
|
9
|
-
export declare function createCompactionSummaryMessage(summary: string, tokensBefore: number, timestamp: string): CompactionSummaryMessage;
|
|
9
|
+
export declare function createCompactionSummaryMessage(summary: string, tokensBefore: number, timestamp: string, elidedMessages?: number): CompactionSummaryMessage;
|
|
10
10
|
export declare function createCustomMessage(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details: unknown, timestamp: string): CustomMessage;
|
|
11
11
|
export declare const NORMALIZED_CONTENT_PREFIX = "[invalid content block normalized to text]";
|
|
12
12
|
export declare function normalizeLlmMessageContent<T extends Message>(message: T): T;
|
|
@@ -36,12 +36,13 @@ export function bashExecutionToText(msg) {
|
|
|
36
36
|
}
|
|
37
37
|
return text;
|
|
38
38
|
}
|
|
39
|
-
export function createCompactionSummaryMessage(summary, tokensBefore, timestamp) {
|
|
39
|
+
export function createCompactionSummaryMessage(summary, tokensBefore, timestamp, elidedMessages) {
|
|
40
40
|
return {
|
|
41
41
|
role: "compactionSummary",
|
|
42
42
|
summary,
|
|
43
43
|
tokensBefore,
|
|
44
44
|
timestamp: requireSessionTimestampMs(timestamp, "compaction summary timestamp"),
|
|
45
|
+
...(elidedMessages !== undefined ? { elidedMessages } : {}),
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
export function createCustomMessage(customType, content, display, details, timestamp) {
|
|
@@ -128,17 +129,24 @@ export function convertToLlm(messages) {
|
|
|
128
129
|
...(message.provenance === "engine-note" ? { provenance: "engine-note" } : {}),
|
|
129
130
|
});
|
|
130
131
|
}
|
|
131
|
-
case "compactionSummary":
|
|
132
|
+
case "compactionSummary": {
|
|
133
|
+
const n = message.elidedMessages;
|
|
134
|
+
const disclosure = n !== undefined && n > 0
|
|
135
|
+
? `\n\n(For scale: this summary stands in for ${n} earlier message${n === 1 ? "" : "s"}${message.tokensBefore > 0
|
|
136
|
+
? ` — approximately ${message.tokensBefore} tokens of context before compaction`
|
|
137
|
+
: ""}.)`
|
|
138
|
+
: "";
|
|
132
139
|
return {
|
|
133
140
|
role: "user",
|
|
134
141
|
content: [
|
|
135
142
|
{
|
|
136
143
|
type: "text",
|
|
137
|
-
text: COMPACTION_SUMMARY_PREFIX + message.summary + COMPACTION_SUMMARY_SUFFIX,
|
|
144
|
+
text: COMPACTION_SUMMARY_PREFIX + message.summary + COMPACTION_SUMMARY_SUFFIX + disclosure,
|
|
138
145
|
},
|
|
139
146
|
],
|
|
140
147
|
timestamp: normalizeCompactionSummaryTimestamp(message.timestamp),
|
|
141
148
|
};
|
|
149
|
+
}
|
|
142
150
|
case "user":
|
|
143
151
|
case "assistant":
|
|
144
152
|
case "toolResult":
|
|
@@ -129,6 +129,7 @@ export interface CompactionSummaryMessage {
|
|
|
129
129
|
summary: string;
|
|
130
130
|
tokensBefore: number;
|
|
131
131
|
timestamp: number | string;
|
|
132
|
+
elidedMessages?: number;
|
|
132
133
|
tokensAfter?: number;
|
|
133
134
|
firstKeptEntryId?: string;
|
|
134
135
|
details?: unknown;
|
|
@@ -161,6 +162,7 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unk
|
|
|
161
162
|
execute: (toolCallId: string, params: Static<TParameters>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<TDetails>) => Promise<AgentToolResult<TDetails>>;
|
|
162
163
|
executionMode?: ToolExecutionMode;
|
|
163
164
|
mcpMaxResultSizeChars?: number;
|
|
165
|
+
mcpAlwaysLoad?: boolean;
|
|
164
166
|
isConcurrencySafe?: (args: unknown) => boolean;
|
|
165
167
|
}
|
|
166
168
|
export interface AgentContext {
|
|
@@ -3,11 +3,14 @@ import { leafIdAfterEntry } from "./storage-base.js";
|
|
|
3
3
|
import { parseSessionTimestampMs } from "./timestamps.js";
|
|
4
4
|
import { flattenableUserText, normalizeEngineSegments } from "../../core/untrusted-text.js";
|
|
5
5
|
import { normalizePromptEpoch } from "../../prompt-assembly/epoch.js";
|
|
6
|
-
import { SKILL_RETENTION_PER_SKILL_MAX_CHARS, SKILL_RETENTION_TOTAL_MAX_CHARS, } from "../compaction/utils.js";
|
|
6
|
+
import { PERSISTED_OUTPUT_REFS_MAX_ENTRIES, SKILL_RETENTION_PER_SKILL_MAX_CHARS, SKILL_RETENTION_TOTAL_MAX_CHARS, } from "../compaction/utils.js";
|
|
7
7
|
const PATH_LIST_MAX_CHARS = 4096;
|
|
8
8
|
const PATH_LIST_MAX_ENTRIES = 1000;
|
|
9
9
|
const INVOKED_SKILLS_MAX_ENTRIES = 1000;
|
|
10
10
|
const INVOKED_SKILL_NAME_MAX_CHARS = 1024;
|
|
11
|
+
const PERSISTED_REF_MAX_CHARS = 256;
|
|
12
|
+
const ACTIVE_TOOL_NAME_MAX_CHARS = 1024;
|
|
13
|
+
const ACTIVE_TOOLS_MAX_ENTRIES = 1000;
|
|
11
14
|
export class StreamingImportValidator {
|
|
12
15
|
seen = new Set();
|
|
13
16
|
parentOf = new Map();
|
|
@@ -157,6 +160,32 @@ export class StreamingImportValidator {
|
|
|
157
160
|
throw new SessionError("invalid_session", `compaction "${e.id}" carries an invokedSkills area of ${totalChars} chars (max ${SKILL_RETENTION_TOTAL_MAX_CHARS})`);
|
|
158
161
|
}
|
|
159
162
|
}
|
|
163
|
+
const elided = e.details?.elidedMessages;
|
|
164
|
+
if (elided !== undefined && !(typeof elided === "number" && Number.isSafeInteger(elided) && elided >= 0)) {
|
|
165
|
+
throw new SessionError("invalid_session", `compaction "${e.id}" carries a structurally invalid elidedMessages count (non-negative integer)`);
|
|
166
|
+
}
|
|
167
|
+
const refs = e.details?.persistedOutputRefs;
|
|
168
|
+
if (refs !== undefined) {
|
|
169
|
+
if (!Array.isArray(refs) || refs.length > PERSISTED_OUTPUT_REFS_MAX_ENTRIES) {
|
|
170
|
+
throw new SessionError("invalid_session", `compaction "${e.id}" carries a structurally invalid persistedOutputRefs list (array of at most ${PERSISTED_OUTPUT_REFS_MAX_ENTRIES} refs)`);
|
|
171
|
+
}
|
|
172
|
+
for (const r of refs) {
|
|
173
|
+
if (typeof r !== "string" || r.length === 0 || r.length > PERSISTED_REF_MAX_CHARS) {
|
|
174
|
+
throw new SessionError("invalid_session", `compaction "${e.id}" carries a persistedOutputRefs entry that is not a non-empty string of at most ${PERSISTED_REF_MAX_CHARS} chars`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const activeToolsCarrier = e.details?.activeTools;
|
|
179
|
+
if (activeToolsCarrier !== undefined) {
|
|
180
|
+
if (!Array.isArray(activeToolsCarrier) || activeToolsCarrier.length > ACTIVE_TOOLS_MAX_ENTRIES) {
|
|
181
|
+
throw new SessionError("invalid_session", `compaction "${e.id}" carries a structurally invalid activeTools list (array of at most ${ACTIVE_TOOLS_MAX_ENTRIES} names)`);
|
|
182
|
+
}
|
|
183
|
+
for (const n of activeToolsCarrier) {
|
|
184
|
+
if (typeof n !== "string" || n.length === 0 || n.length > ACTIVE_TOOL_NAME_MAX_CHARS) {
|
|
185
|
+
throw new SessionError("invalid_session", `compaction "${e.id}" carries an activeTools entry that is not a non-empty string of at most ${ACTIVE_TOOL_NAME_MAX_CHARS} chars`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
160
189
|
}
|
|
161
190
|
this.parentOf.set(e.id, e.parentId);
|
|
162
191
|
this.seen.add(e.id);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { asAgentMessage, createCompactionSummaryMessage, createCustomMessage, } from "../harness/messages.js";
|
|
2
|
-
import { SessionError, normalizeAnnouncedListing, normalizeCompactionStateCarrier, normalizeWorkspaceState } from "../harness/types.js";
|
|
2
|
+
import { SessionError, isValidModelChange, normalizeAnnouncedListing, normalizeCompactionStateCarrier, normalizeWorkspaceState } from "../harness/types.js";
|
|
3
3
|
import { normalizePromptEpoch } from "../../prompt-assembly/epoch.js";
|
|
4
|
-
import { budgetInvokedSkillsRetention, readRetainedInvokedSkills, renderInvokedSkillsRetention, } from "../compaction/utils.js";
|
|
4
|
+
import { budgetInvokedSkillsRetention, readElidedMessages, readRetainedInvokedSkills, renderInvokedSkillsRetention, } from "../compaction/utils.js";
|
|
5
5
|
const RETENTION_CLAMP_DEFAULT_CHARS_PER_TOKEN = 4;
|
|
6
6
|
const RETENTION_CLAMP_WINDOW_FRACTION = 0.5;
|
|
7
7
|
export function buildSessionContext(pathEntries, opts) {
|
|
@@ -25,7 +25,9 @@ export function buildSessionContext(pathEntries, opts) {
|
|
|
25
25
|
model = { provider: entry.provider, modelId: entry.modelId };
|
|
26
26
|
}
|
|
27
27
|
else if (entry.type === "message" && entry.message.role === "assistant") {
|
|
28
|
-
|
|
28
|
+
if (isValidModelChange({ provider: entry.message.provider, modelId: entry.message.model })) {
|
|
29
|
+
model = { provider: entry.message.provider, modelId: entry.message.model };
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
32
|
else if (entry.type === "compaction") {
|
|
31
33
|
compaction = entry;
|
|
@@ -64,7 +66,7 @@ export function buildSessionContext(pathEntries, opts) {
|
|
|
64
66
|
retainedSkills = budgetInvokedSkillsRetention(retainedSkills, budgetChars);
|
|
65
67
|
}
|
|
66
68
|
const retainedSkillsBlock = renderInvokedSkillsRetention(retainedSkills);
|
|
67
|
-
messages.push(asAgentMessage(createCompactionSummaryMessage(compaction.summary + retainedSkillsBlock, compaction.tokensBefore, compaction.timestamp)));
|
|
69
|
+
messages.push(asAgentMessage(createCompactionSummaryMessage(compaction.summary + retainedSkillsBlock, compaction.tokensBefore, compaction.timestamp, readElidedMessages(compaction.details))));
|
|
68
70
|
const compactionIdx = pathEntries.findIndex((e) => e.type === "compaction" && e.id === compaction.id);
|
|
69
71
|
let foundFirstKept = false;
|
|
70
72
|
for (let i = 0; i < compactionIdx; i++) {
|
|
@@ -242,7 +244,7 @@ export class StoredSession {
|
|
|
242
244
|
const carried = await (async () => {
|
|
243
245
|
try {
|
|
244
246
|
const ctx = buildSessionContext(await this.getBranch());
|
|
245
|
-
return { thinkingLevel: ctx.thinkingLevel, model: ctx.model };
|
|
247
|
+
return { thinkingLevel: ctx.thinkingLevel, ...(ctx.model !== null ? { model: ctx.model } : {}) };
|
|
246
248
|
}
|
|
247
249
|
catch {
|
|
248
250
|
return undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -151,6 +151,13 @@ export { PRESENT_PLAN_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME } from "./core/presen
|
|
|
151
151
|
export { type WorkflowRunStore, type WorkflowRunSummary, summarizeWorkflowRun, isTerminalWorkflowStatus, WorkflowRunStoreError, InMemoryWorkflowRunStore, } from "./core/workflow-run-store.js";
|
|
152
152
|
export { FileWorkflowRunStore, type FileWorkflowRunStoreOptions } from "./stores/file/workflow-run-store.js";
|
|
153
153
|
export { workflowRunStoreContract } from "./core/workflow-run-store-contract.js";
|
|
154
|
+
export { CONTRACT_KIT_ENGINE_VERSION } from "./core/store-contracts/contract-kit-version.js";
|
|
155
|
+
export { type ContractAssertionRunner } from "./core/store-contracts/contract-harness.js";
|
|
156
|
+
export { checkpointStoreContract } from "./core/store-contracts/checkpoint-store-contract.js";
|
|
157
|
+
export { sessionRepoContract } from "./core/store-contracts/session-repo-contract.js";
|
|
158
|
+
export { toolResultStoreContract } from "./core/store-contracts/tool-result-store-contract.js";
|
|
159
|
+
export { fileSnapshotStoreContract } from "./core/store-contracts/file-snapshot-store-contract.js";
|
|
160
|
+
export { MAILBOX_CONTRACT_SCOPE, mailboxStoreContract, mailboxAckOwnershipContract, mailboxBundledOnlyContract, } from "./core/store-contracts/mailbox-store-contract.js";
|
|
154
161
|
export { type BackgroundAgentStore, type BackgroundAgentRecord, type BackgroundAgentRowSummary, type BackgroundAgentUsage, canAccessAgentRecord, BackgroundAgentStoreError, InMemoryBackgroundAgentStore, reconcileParkedAgents, } from "./core/background-agent-store.js";
|
|
155
162
|
export { FileBackgroundAgentStore, type FileBackgroundAgentStoreOptions } from "./stores/file/background-agent-store.js";
|
|
156
163
|
export { type WorkflowJournalStore, type WorkflowJournalEntry, InMemoryWorkflowJournalStore, callKeyOrdinal, JOURNAL_OVERSIZE_ERROR_CODE, } from "./core/workflow-journal-store.js";
|
package/dist/index.js
CHANGED
|
@@ -138,6 +138,13 @@ export { PRESENT_PLAN_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME } from "./core/presen
|
|
|
138
138
|
export { summarizeWorkflowRun, isTerminalWorkflowStatus, WorkflowRunStoreError, InMemoryWorkflowRunStore, } from "./core/workflow-run-store.js";
|
|
139
139
|
export { FileWorkflowRunStore } from "./stores/file/workflow-run-store.js";
|
|
140
140
|
export { workflowRunStoreContract } from "./core/workflow-run-store-contract.js";
|
|
141
|
+
export { CONTRACT_KIT_ENGINE_VERSION } from "./core/store-contracts/contract-kit-version.js";
|
|
142
|
+
export {} from "./core/store-contracts/contract-harness.js";
|
|
143
|
+
export { checkpointStoreContract } from "./core/store-contracts/checkpoint-store-contract.js";
|
|
144
|
+
export { sessionRepoContract } from "./core/store-contracts/session-repo-contract.js";
|
|
145
|
+
export { toolResultStoreContract } from "./core/store-contracts/tool-result-store-contract.js";
|
|
146
|
+
export { fileSnapshotStoreContract } from "./core/store-contracts/file-snapshot-store-contract.js";
|
|
147
|
+
export { MAILBOX_CONTRACT_SCOPE, mailboxStoreContract, mailboxAckOwnershipContract, mailboxBundledOnlyContract, } from "./core/store-contracts/mailbox-store-contract.js";
|
|
141
148
|
export { canAccessAgentRecord, BackgroundAgentStoreError, InMemoryBackgroundAgentStore, reconcileParkedAgents, } from "./core/background-agent-store.js";
|
|
142
149
|
export { FileBackgroundAgentStore } from "./stores/file/background-agent-store.js";
|
|
143
150
|
export { InMemoryWorkflowJournalStore, callKeyOrdinal, JOURNAL_OVERSIZE_ERROR_CODE, } from "./core/workflow-journal-store.js";
|
|
@@ -2,7 +2,7 @@ export * from "./harness-types.js";
|
|
|
2
2
|
export { AgentHarness } from "../engine/harness/agent-harness.js";
|
|
3
3
|
export { CompactionError, ExecutionError, FileError, ok, err } from "../engine/harness/types.js";
|
|
4
4
|
export { DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, compact, computeFileLists, dryRunSummarizationClamp, estimateContextTokens, estimateTokens, prepareCompaction, shouldCompact, summaryOutputBudgetTokens, } from "../engine/compaction/compaction.js";
|
|
5
|
-
export { SKILL_RETENTION_PER_SKILL_MAX_CHARS, SKILL_RETENTION_TOTAL_MAX_CHARS, SKILL_RETENTION_TRUNCATION_MARKER, readRetainedInvokedSkills, renderInvokedSkillsRetention, } from "../engine/compaction/utils.js";
|
|
5
|
+
export { SKILL_RETENTION_PER_SKILL_MAX_CHARS, SKILL_RETENTION_TOTAL_MAX_CHARS, SKILL_RETENTION_TRUNCATION_MARKER, readCompactionActiveTools, readRetainedInvokedSkills, renderInvokedSkillsRetention, } from "../engine/compaction/utils.js";
|
|
6
6
|
export { NodeExecutionEnv } from "../engine/execution-env/node-execution-env.js";
|
|
7
7
|
export { StoredSession, buildSessionContext } from "../engine/session/session.js";
|
|
8
8
|
export { getEntriesToFork } from "../engine/session/repo-utils.js";
|
package/dist/internal/harness.js
CHANGED
|
@@ -2,7 +2,7 @@ export * from "./harness-types.js";
|
|
|
2
2
|
export { AgentHarness } from "../engine/harness/agent-harness.js";
|
|
3
3
|
export { CompactionError, ExecutionError, FileError, ok, err } from "../engine/harness/types.js";
|
|
4
4
|
export { DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, compact, computeFileLists, dryRunSummarizationClamp, estimateContextTokens, estimateTokens, prepareCompaction, shouldCompact, summaryOutputBudgetTokens, } from "../engine/compaction/compaction.js";
|
|
5
|
-
export { SKILL_RETENTION_PER_SKILL_MAX_CHARS, SKILL_RETENTION_TOTAL_MAX_CHARS, SKILL_RETENTION_TRUNCATION_MARKER, readRetainedInvokedSkills, renderInvokedSkillsRetention, } from "../engine/compaction/utils.js";
|
|
5
|
+
export { SKILL_RETENTION_PER_SKILL_MAX_CHARS, SKILL_RETENTION_TOTAL_MAX_CHARS, SKILL_RETENTION_TRUNCATION_MARKER, readCompactionActiveTools, readRetainedInvokedSkills, renderInvokedSkillsRetention, } from "../engine/compaction/utils.js";
|
|
6
6
|
export { NodeExecutionEnv } from "../engine/execution-env/node-execution-env.js";
|
|
7
7
|
export { StoredSession, buildSessionContext } from "../engine/session/session.js";
|
|
8
8
|
export { getEntriesToFork } from "../engine/session/repo-utils.js";
|
|
@@ -641,6 +641,14 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
641
641
|
};
|
|
642
642
|
return { tail, onActivity };
|
|
643
643
|
};
|
|
644
|
+
const createWorkspaceObserver = (rec) => (workspace) => {
|
|
645
|
+
if (finalized)
|
|
646
|
+
return;
|
|
647
|
+
if (!workspace.isolated || rec.worktreeDir === workspace.cwd)
|
|
648
|
+
return;
|
|
649
|
+
rec.worktreeDir = workspace.cwd;
|
|
650
|
+
void persist("update");
|
|
651
|
+
};
|
|
644
652
|
let currentPhase;
|
|
645
653
|
let openMarkerPhase;
|
|
646
654
|
let currentGroup;
|
|
@@ -815,7 +823,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
815
823
|
opts.onForwardEvent(e.type === "task_progress" ? { ...e, workflowRunId: runId, workflowAgentLabel: label } : e);
|
|
816
824
|
}
|
|
817
825
|
: undefined;
|
|
818
|
-
const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}), ...spawnAttribution, ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), agentName: label };
|
|
826
|
+
const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}), ...spawnAttribution, ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), agentName: label, onWorkspaceResolved: createWorkspaceObserver(rec) };
|
|
819
827
|
let attempts = 0;
|
|
820
828
|
let throttleRetried = false;
|
|
821
829
|
let lastAttemptReason;
|
|
@@ -1160,6 +1168,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1160
1168
|
...(enrichedForwardS !== undefined ? { onForwardEvent: enrichedForwardS } : {}),
|
|
1161
1169
|
agentName: label,
|
|
1162
1170
|
onActivity,
|
|
1171
|
+
onWorkspaceResolved: createWorkspaceObserver(rec),
|
|
1163
1172
|
...(bceSink !== undefined
|
|
1164
1173
|
? {
|
|
1165
1174
|
onForwardEvent: (e) => {
|