scream-code 0.11.0 → 0.11.2
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/{app-CuAxNnOv.mjs → app-8_tNuOds.mjs} +447 -81
- package/dist/main.mjs +1 -1
- package/dist/public/assets/index-BJI4roHX.css +1 -0
- package/dist/public/assets/index-mUEA7L6U.js +90 -0
- package/dist/public/assets/index-mUEA7L6U.js.map +1 -0
- package/dist/public/index.html +2 -2
- package/package.json +1 -1
- package/dist/public/assets/bash-Yzrsuije.js +0 -2
- package/dist/public/assets/bash-Yzrsuije.js.map +0 -1
- package/dist/public/assets/css-DPfMkruS.js +0 -2
- package/dist/public/assets/css-DPfMkruS.js.map +0 -1
- package/dist/public/assets/diff-D97Zzqfu.js +0 -2
- package/dist/public/assets/diff-D97Zzqfu.js.map +0 -1
- package/dist/public/assets/html-GMplVEZG.js +0 -2
- package/dist/public/assets/html-GMplVEZG.js.map +0 -1
- package/dist/public/assets/index-2xNGacMZ.js +0 -91
- package/dist/public/assets/index-2xNGacMZ.js.map +0 -1
- package/dist/public/assets/index-CufIY2bi.css +0 -1
- package/dist/public/assets/javascript-wDzz0qaB.js +0 -2
- package/dist/public/assets/javascript-wDzz0qaB.js.map +0 -1
- package/dist/public/assets/json-Cp-IABpG.js +0 -2
- package/dist/public/assets/json-Cp-IABpG.js.map +0 -1
- package/dist/public/assets/python-B6aJPvgy.js +0 -2
- package/dist/public/assets/python-B6aJPvgy.js.map +0 -1
- package/dist/public/assets/typescript-BPQ3VLAy.js +0 -2
- package/dist/public/assets/typescript-BPQ3VLAy.js.map +0 -1
- package/dist/public/assets/vue-DVfSmUF7.js +0 -2
- package/dist/public/assets/vue-DVfSmUF7.js.map +0 -1
- package/dist/public/assets/yaml-Buea-lGh.js +0 -2
- package/dist/public/assets/yaml-Buea-lGh.js.map +0 -1
|
@@ -717,7 +717,7 @@ function isOrphanedToolCallMessage(lowerMessage) {
|
|
|
717
717
|
*/
|
|
718
718
|
function isOrphanedToolCallError(error) {
|
|
719
719
|
if (error instanceof APIOrphanedToolCallError) return true;
|
|
720
|
-
return isOrphanedToolCallMessage(errorMessage$
|
|
720
|
+
return isOrphanedToolCallMessage(errorMessage$7(error).toLowerCase());
|
|
721
721
|
}
|
|
722
722
|
/**
|
|
723
723
|
* The API returned an empty response (no content, no tool calls).
|
|
@@ -764,7 +764,7 @@ function isContextOverflowStatusError(statusCode, message) {
|
|
|
764
764
|
const lowerMessage = message.toLowerCase();
|
|
765
765
|
return CONTEXT_OVERFLOW_MESSAGE_PATTERNS.some((pattern) => pattern.test(lowerMessage));
|
|
766
766
|
}
|
|
767
|
-
function errorMessage$
|
|
767
|
+
function errorMessage$7(error) {
|
|
768
768
|
return error instanceof Error ? error.message : String(error);
|
|
769
769
|
}
|
|
770
770
|
//#endregion
|
|
@@ -47296,7 +47296,7 @@ function isAbortError$1(err) {
|
|
|
47296
47296
|
if (err instanceof Error) return err.name === "AbortError";
|
|
47297
47297
|
return false;
|
|
47298
47298
|
}
|
|
47299
|
-
function errorMessage$
|
|
47299
|
+
function errorMessage$6(err) {
|
|
47300
47300
|
if (err instanceof Error) return err.message;
|
|
47301
47301
|
return String(err);
|
|
47302
47302
|
}
|
|
@@ -52494,7 +52494,7 @@ var GoalMode = class {
|
|
|
52494
52494
|
const state = this.state;
|
|
52495
52495
|
if (state === void 0 || state.status !== "active") return null;
|
|
52496
52496
|
state.tokensUsed += Math.max(0, tokenDelta);
|
|
52497
|
-
this.persistState(state
|
|
52497
|
+
this.persistState(state);
|
|
52498
52498
|
this.appendGoalUpdate({ tokensUsed: state.tokensUsed });
|
|
52499
52499
|
return this.toSnapshot(state);
|
|
52500
52500
|
}
|
|
@@ -52516,7 +52516,7 @@ var GoalMode = class {
|
|
|
52516
52516
|
time: Date.now()
|
|
52517
52517
|
});
|
|
52518
52518
|
if (state.notes.length > MAX_GOAL_NOTES) state.notes = state.notes.slice(-60);
|
|
52519
|
-
this.persistState(state
|
|
52519
|
+
this.persistState(state);
|
|
52520
52520
|
return this.toSnapshot(state);
|
|
52521
52521
|
}
|
|
52522
52522
|
clearInternal(actor, opts = {}) {
|
|
@@ -52581,7 +52581,7 @@ var GoalMode = class {
|
|
|
52581
52581
|
wallClockMs: liveWallClockMs(state, Date.now()),
|
|
52582
52582
|
budget: computeBudgetReport(state, Date.now()),
|
|
52583
52583
|
terminalReason: state.terminalReason,
|
|
52584
|
-
notes: state.notes
|
|
52584
|
+
notes: state.notes.map((note) => ({ ...note }))
|
|
52585
52585
|
};
|
|
52586
52586
|
}
|
|
52587
52587
|
};
|
|
@@ -52865,7 +52865,7 @@ var UpdateGoalTool = class {
|
|
|
52865
52865
|
try {
|
|
52866
52866
|
await goal.pauseGoal({ reason: "verifying" }, "system");
|
|
52867
52867
|
} catch (error) {
|
|
52868
|
-
return toolError(`Failed to pause goal for verification: ${errorMessage$
|
|
52868
|
+
return toolError(`Failed to pause goal for verification: ${errorMessage$5(error)}`, goal);
|
|
52869
52869
|
}
|
|
52870
52870
|
let rawGrade;
|
|
52871
52871
|
try {
|
|
@@ -52873,7 +52873,7 @@ var UpdateGoalTool = class {
|
|
|
52873
52873
|
} catch (error) {
|
|
52874
52874
|
const resumeError = await resumeAfterGrading(goal);
|
|
52875
52875
|
if (resumeError !== void 0) return resumeError;
|
|
52876
|
-
const reason = `Goal verification could not be completed: ${errorMessage$
|
|
52876
|
+
const reason = `Goal verification could not be completed: ${errorMessage$5(error)}`;
|
|
52877
52877
|
this.appendGradingFeedback(reason);
|
|
52878
52878
|
return {
|
|
52879
52879
|
isError: true,
|
|
@@ -52901,7 +52901,7 @@ var UpdateGoalTool = class {
|
|
|
52901
52901
|
name: GOAL_COMPLETION_REMINDER_NAME
|
|
52902
52902
|
});
|
|
52903
52903
|
} catch (error) {
|
|
52904
|
-
return toolError(`Failed to mark verified goal complete: ${errorMessage$
|
|
52904
|
+
return toolError(`Failed to mark verified goal complete: ${errorMessage$5(error)}`, goal);
|
|
52905
52905
|
}
|
|
52906
52906
|
return {
|
|
52907
52907
|
output: `Goal verified and marked complete.\n${grade.reason}`,
|
|
@@ -52936,7 +52936,7 @@ async function resumeAfterGrading(goal) {
|
|
|
52936
52936
|
await goal.resumeGoal({}, "system");
|
|
52937
52937
|
return;
|
|
52938
52938
|
} catch (error) {
|
|
52939
|
-
return toolError(`Failed to restore active goal after verification: ${errorMessage$
|
|
52939
|
+
return toolError(`Failed to restore active goal after verification: ${errorMessage$5(error)}`, goal);
|
|
52940
52940
|
}
|
|
52941
52941
|
}
|
|
52942
52942
|
function toolError(message, goal) {
|
|
@@ -52945,7 +52945,7 @@ function toolError(message, goal) {
|
|
|
52945
52945
|
output: `${message}. Current goal status: ${goal.getGoal().goal?.status ?? "missing"}.`
|
|
52946
52946
|
};
|
|
52947
52947
|
}
|
|
52948
|
-
function errorMessage$
|
|
52948
|
+
function errorMessage$5(error) {
|
|
52949
52949
|
return error instanceof Error ? error.message : String(error);
|
|
52950
52950
|
}
|
|
52951
52951
|
//#endregion
|
|
@@ -78351,7 +78351,7 @@ async function runHook(command, input, options) {
|
|
|
78351
78351
|
detached: process.platform !== "win32"
|
|
78352
78352
|
});
|
|
78353
78353
|
} catch (error) {
|
|
78354
|
-
return allowResult({ stderr: errorMessage$
|
|
78354
|
+
return allowResult({ stderr: errorMessage$4(error) });
|
|
78355
78355
|
}
|
|
78356
78356
|
return new Promise((resolve) => {
|
|
78357
78357
|
let stdout = "";
|
|
@@ -78399,7 +78399,7 @@ async function runHook(command, input, options) {
|
|
|
78399
78399
|
child.on("error", (error) => {
|
|
78400
78400
|
settle(allowResult({
|
|
78401
78401
|
stdout,
|
|
78402
|
-
stderr: stderr + errorMessage$
|
|
78402
|
+
stderr: stderr + errorMessage$4(error)
|
|
78403
78403
|
}));
|
|
78404
78404
|
});
|
|
78405
78405
|
child.on("close", (code) => {
|
|
@@ -78495,7 +78495,7 @@ function tryKillProcess(child, signal) {
|
|
|
78495
78495
|
function isRecord$5(value) {
|
|
78496
78496
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
78497
78497
|
}
|
|
78498
|
-
function errorMessage$
|
|
78498
|
+
function errorMessage$4(error) {
|
|
78499
78499
|
return error instanceof Error ? error.message : String(error);
|
|
78500
78500
|
}
|
|
78501
78501
|
//#endregion
|
|
@@ -89581,7 +89581,7 @@ function parseToolCallArguments(raw) {
|
|
|
89581
89581
|
} catch {
|
|
89582
89582
|
return {
|
|
89583
89583
|
success: false,
|
|
89584
|
-
error: errorMessage$
|
|
89584
|
+
error: errorMessage$6(error)
|
|
89585
89585
|
};
|
|
89586
89586
|
}
|
|
89587
89587
|
}
|
|
@@ -89661,7 +89661,7 @@ async function prepareToolCall(step, call) {
|
|
|
89661
89661
|
toolCallId: call.toolCall.id,
|
|
89662
89662
|
error
|
|
89663
89663
|
});
|
|
89664
|
-
return settleError(effectiveArgs, error instanceof PathSecurityError ? error.message : `Tool "${call.toolName}" failed to resolve execution: ${errorMessage$
|
|
89664
|
+
return settleError(effectiveArgs, error instanceof PathSecurityError ? error.message : `Tool "${call.toolName}" failed to resolve execution: ${errorMessage$6(error)}`);
|
|
89665
89665
|
}
|
|
89666
89666
|
const displayFields = toolCallDisplayFieldsFromExecution(execution);
|
|
89667
89667
|
const settleAborted = () => settleError(effectiveArgs, abortedToolOutput(call.toolName, step.signal), displayFields);
|
|
@@ -89720,7 +89720,7 @@ async function runPrepareToolExecutionHook(step, call) {
|
|
|
89720
89720
|
return {
|
|
89721
89721
|
kind: "hookFailed",
|
|
89722
89722
|
args,
|
|
89723
|
-
output: `prepareToolExecution hook failed for "${call.toolName}": ${errorMessage$
|
|
89723
|
+
output: `prepareToolExecution hook failed for "${call.toolName}": ${errorMessage$6(error)}`
|
|
89724
89724
|
};
|
|
89725
89725
|
}
|
|
89726
89726
|
const effectiveArgs = hookResult?.updatedArgs ?? args;
|
|
@@ -89761,7 +89761,7 @@ async function runAuthorizeToolExecutionHook(step, call, args, execution) {
|
|
|
89761
89761
|
};
|
|
89762
89762
|
return {
|
|
89763
89763
|
block: true,
|
|
89764
|
-
reason: `authorizeToolExecution hook failed for "${call.toolName}": ${errorMessage$
|
|
89764
|
+
reason: `authorizeToolExecution hook failed for "${call.toolName}": ${errorMessage$6(error)}`
|
|
89765
89765
|
};
|
|
89766
89766
|
}
|
|
89767
89767
|
}
|
|
@@ -89788,7 +89788,7 @@ async function runRunnableToolCall(step, call, effectiveArgs, metadata, executio
|
|
|
89788
89788
|
toolCallId: toolCall.id,
|
|
89789
89789
|
error
|
|
89790
89790
|
});
|
|
89791
|
-
return makeErrorToolResult(call, effectiveArgs, aborted ? abortedToolOutput(toolName, signal) : `Tool "${toolName}" failed: ${errorMessage$
|
|
89791
|
+
return makeErrorToolResult(call, effectiveArgs, aborted ? abortedToolOutput(toolName, signal) : `Tool "${toolName}" failed: ${errorMessage$6(error)}`);
|
|
89792
89792
|
}
|
|
89793
89793
|
return makeToolResult(call, effectiveArgs, toolResult);
|
|
89794
89794
|
}
|
|
@@ -89820,7 +89820,7 @@ async function finalizePendingToolResult(step, pendingResult) {
|
|
|
89820
89820
|
toolCallId: pendingResult.toolCall.id,
|
|
89821
89821
|
error
|
|
89822
89822
|
});
|
|
89823
|
-
const output = aborted ? `Tool "${pendingResult.toolName}" aborted during finalizeToolResult hook.` : `finalizeToolResult hook failed for "${pendingResult.toolName}": ${errorMessage$
|
|
89823
|
+
const output = aborted ? `Tool "${pendingResult.toolName}" aborted during finalizeToolResult hook.` : `finalizeToolResult hook failed for "${pendingResult.toolName}": ${errorMessage$6(error)}`;
|
|
89824
89824
|
return {
|
|
89825
89825
|
...pendingResult,
|
|
89826
89826
|
stopTurn: pendingResult.stopTurn,
|
|
@@ -90178,7 +90178,7 @@ async function runTurn(input) {
|
|
|
90178
90178
|
usage
|
|
90179
90179
|
};
|
|
90180
90180
|
}
|
|
90181
|
-
dispatchEvent(makeInterruptedEvent(isMaxStepsExceededError(error) ? "max_steps" : "error", steps, activeStep, errorMessage$
|
|
90181
|
+
dispatchEvent(makeInterruptedEvent(isMaxStepsExceededError(error) ? "max_steps" : "error", steps, activeStep, errorMessage$6(error)));
|
|
90182
90182
|
throw error;
|
|
90183
90183
|
}
|
|
90184
90184
|
return {
|
|
@@ -94455,6 +94455,13 @@ var LspRegistry = class {
|
|
|
94455
94455
|
};
|
|
94456
94456
|
//#endregion
|
|
94457
94457
|
//#region ../../packages/agent-core/src/agent/tool/index.ts
|
|
94458
|
+
function cloneTodos(todos) {
|
|
94459
|
+
return todos.map((todo) => ({
|
|
94460
|
+
title: todo.title,
|
|
94461
|
+
status: todo.status,
|
|
94462
|
+
phase: todo.phase
|
|
94463
|
+
}));
|
|
94464
|
+
}
|
|
94458
94465
|
const CRITERIA_SYSTEM_PROMPT = [
|
|
94459
94466
|
"You generate concrete, verifiable acceptance criteria for a given objective.",
|
|
94460
94467
|
"Criteria must be specific and testable — state what should work end-to-end, not just what should exist.",
|
|
@@ -94645,12 +94652,20 @@ var ToolManager = class {
|
|
|
94645
94652
|
});
|
|
94646
94653
|
}
|
|
94647
94654
|
updateStore(key, value) {
|
|
94655
|
+
const storedValue = key === "todo" ? cloneTodos(value ?? []) : value;
|
|
94648
94656
|
this.agent.records.logRecord({
|
|
94649
94657
|
type: "tools.update_store",
|
|
94650
94658
|
key,
|
|
94651
|
-
value
|
|
94659
|
+
value: storedValue
|
|
94660
|
+
});
|
|
94661
|
+
this.store[key] = storedValue;
|
|
94662
|
+
if (key === "todo") this.agent.emitEvent({
|
|
94663
|
+
type: "todo.updated",
|
|
94664
|
+
todos: this.getTodos()
|
|
94652
94665
|
});
|
|
94653
|
-
|
|
94666
|
+
}
|
|
94667
|
+
getTodos() {
|
|
94668
|
+
return cloneTodos(this.store.todo ?? []);
|
|
94654
94669
|
}
|
|
94655
94670
|
registerUserTool(input) {
|
|
94656
94671
|
this.agent.records.logRecord({
|
|
@@ -96700,6 +96715,9 @@ var Agent = class {
|
|
|
96700
96715
|
getGoal: () => {
|
|
96701
96716
|
return this.goal.getGoal();
|
|
96702
96717
|
},
|
|
96718
|
+
getTodos: () => {
|
|
96719
|
+
return this.tools.getTodos();
|
|
96720
|
+
},
|
|
96703
96721
|
setGoalBudget: async (payload) => {
|
|
96704
96722
|
const { value, unit } = payload;
|
|
96705
96723
|
let budgetLimits;
|
|
@@ -114592,7 +114610,7 @@ function markitConversionCacheKey(bytes, extension) {
|
|
|
114592
114610
|
function cacheEntryPath(key) {
|
|
114593
114611
|
return path$8.join(documentConversionCacheDir(), `${key}.json`);
|
|
114594
114612
|
}
|
|
114595
|
-
function errorMessage$
|
|
114613
|
+
function errorMessage$3(error) {
|
|
114596
114614
|
return error instanceof Error ? error.message : String(error);
|
|
114597
114615
|
}
|
|
114598
114616
|
function parseCacheEntry(raw) {
|
|
@@ -114611,14 +114629,14 @@ async function readMarkitConversionCache(key) {
|
|
|
114611
114629
|
try {
|
|
114612
114630
|
raw = await fs$1.readFile(target, "utf8");
|
|
114613
114631
|
} catch (error) {
|
|
114614
|
-
if (!isEnoent(error)) log.debug("document conversion cache read failed", { error: errorMessage$
|
|
114632
|
+
if (!isEnoent(error)) log.debug("document conversion cache read failed", { error: errorMessage$3(error) });
|
|
114615
114633
|
return { status: "miss" };
|
|
114616
114634
|
}
|
|
114617
114635
|
let entry;
|
|
114618
114636
|
try {
|
|
114619
114637
|
entry = parseCacheEntry(raw);
|
|
114620
114638
|
} catch (error) {
|
|
114621
|
-
log.debug("document conversion cache read failed", { error: errorMessage$
|
|
114639
|
+
log.debug("document conversion cache read failed", { error: errorMessage$3(error) });
|
|
114622
114640
|
entry = null;
|
|
114623
114641
|
}
|
|
114624
114642
|
if (entry === null) {
|
|
@@ -114635,7 +114653,7 @@ async function pruneMarkitConversionCache(cacheDir) {
|
|
|
114635
114653
|
try {
|
|
114636
114654
|
names = await fs$1.readdir(cacheDir);
|
|
114637
114655
|
} catch (error) {
|
|
114638
|
-
if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$
|
|
114656
|
+
if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$3(error) });
|
|
114639
114657
|
return;
|
|
114640
114658
|
}
|
|
114641
114659
|
const now = Date.now();
|
|
@@ -114647,7 +114665,7 @@ async function pruneMarkitConversionCache(cacheDir) {
|
|
|
114647
114665
|
try {
|
|
114648
114666
|
stat = await fs$1.stat(entryPath);
|
|
114649
114667
|
} catch (error) {
|
|
114650
|
-
if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$
|
|
114668
|
+
if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$3(error) });
|
|
114651
114669
|
continue;
|
|
114652
114670
|
}
|
|
114653
114671
|
if (!stat.isFile()) continue;
|
|
@@ -114671,7 +114689,7 @@ async function pruneMarkitConversionCache(cacheDir) {
|
|
|
114671
114689
|
await fs$1.rm(entry.path, { force: true });
|
|
114672
114690
|
totalBytes -= entry.size;
|
|
114673
114691
|
} catch (error) {
|
|
114674
|
-
if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$
|
|
114692
|
+
if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$3(error) });
|
|
114675
114693
|
}
|
|
114676
114694
|
}
|
|
114677
114695
|
}
|
|
@@ -114689,11 +114707,11 @@ async function writeMarkitConversionCache(key, content) {
|
|
|
114689
114707
|
await fs$1.rename(tempPath, target);
|
|
114690
114708
|
} catch (error) {
|
|
114691
114709
|
await fs$1.rm(tempPath, { force: true }).catch(() => void 0);
|
|
114692
|
-
log.debug("document conversion cache write failed", { error: errorMessage$
|
|
114710
|
+
log.debug("document conversion cache write failed", { error: errorMessage$3(error) });
|
|
114693
114711
|
return;
|
|
114694
114712
|
}
|
|
114695
114713
|
pruneMarkitConversionCache(cacheDir).catch((error) => {
|
|
114696
|
-
log.debug("document conversion cache prune failed", { error: errorMessage$
|
|
114714
|
+
log.debug("document conversion cache prune failed", { error: errorMessage$3(error) });
|
|
114697
114715
|
});
|
|
114698
114716
|
}
|
|
114699
114717
|
//#endregion
|
|
@@ -116759,6 +116777,9 @@ var SessionAPIImpl = class {
|
|
|
116759
116777
|
getGoal({ agentId, ...payload }) {
|
|
116760
116778
|
return this.getAgent(agentId).getGoal(payload);
|
|
116761
116779
|
}
|
|
116780
|
+
getTodos({ agentId, ...payload }) {
|
|
116781
|
+
return this.getAgent(agentId).getTodos(payload);
|
|
116782
|
+
}
|
|
116762
116783
|
setGoalBudget({ agentId, ...payload }) {
|
|
116763
116784
|
return this.getAgent(agentId).setGoalBudget(payload);
|
|
116764
116785
|
}
|
|
@@ -118504,6 +118525,9 @@ var ScreamCore = class {
|
|
|
118504
118525
|
getGoal({ sessionId, ...payload }) {
|
|
118505
118526
|
return this.sessionApi(sessionId).getGoal(payload);
|
|
118506
118527
|
}
|
|
118528
|
+
getTodos({ sessionId, ...payload }) {
|
|
118529
|
+
return this.sessionApi(sessionId).getTodos(payload);
|
|
118530
|
+
}
|
|
118507
118531
|
setGoalBudget({ sessionId, ...payload }) {
|
|
118508
118532
|
return this.sessionApi(sessionId).setGoalBudget(payload);
|
|
118509
118533
|
}
|
|
@@ -118970,6 +118994,12 @@ var SDKRpcClient = class {
|
|
|
118970
118994
|
agentId: this.interactiveAgentId
|
|
118971
118995
|
});
|
|
118972
118996
|
}
|
|
118997
|
+
async getTodos(input) {
|
|
118998
|
+
return (await this.getRpc()).getTodos({
|
|
118999
|
+
sessionId: input.sessionId,
|
|
119000
|
+
agentId: this.interactiveAgentId
|
|
119001
|
+
});
|
|
119002
|
+
}
|
|
118973
119003
|
async setGoalBudget(input) {
|
|
118974
119004
|
return (await this.getRpc()).setGoalBudget({
|
|
118975
119005
|
sessionId: input.sessionId,
|
|
@@ -119289,7 +119319,7 @@ var SDKRpcClient = class {
|
|
|
119289
119319
|
type: "error",
|
|
119290
119320
|
sessionId: request.sessionId,
|
|
119291
119321
|
agentId: request.agentId,
|
|
119292
|
-
...makeErrorPayload(ErrorCodes.SESSION_APPROVAL_HANDLER_ERROR, errorMessage$
|
|
119322
|
+
...makeErrorPayload(ErrorCodes.SESSION_APPROVAL_HANDLER_ERROR, errorMessage$2(error))
|
|
119293
119323
|
});
|
|
119294
119324
|
return {
|
|
119295
119325
|
decision: "cancelled",
|
|
@@ -119307,7 +119337,7 @@ var SDKRpcClient = class {
|
|
|
119307
119337
|
type: "error",
|
|
119308
119338
|
sessionId: request.sessionId,
|
|
119309
119339
|
agentId: request.agentId,
|
|
119310
|
-
...makeErrorPayload(ErrorCodes.SESSION_QUESTION_HANDLER_ERROR, errorMessage$
|
|
119340
|
+
...makeErrorPayload(ErrorCodes.SESSION_QUESTION_HANDLER_ERROR, errorMessage$2(error))
|
|
119311
119341
|
});
|
|
119312
119342
|
return null;
|
|
119313
119343
|
}
|
|
@@ -119342,7 +119372,7 @@ var ClientAPI = class {
|
|
|
119342
119372
|
return this.client.toolCall(request);
|
|
119343
119373
|
}
|
|
119344
119374
|
};
|
|
119345
|
-
function errorMessage$
|
|
119375
|
+
function errorMessage$2(error) {
|
|
119346
119376
|
return error instanceof Error ? error.message : String(error);
|
|
119347
119377
|
}
|
|
119348
119378
|
//#endregion
|
|
@@ -119733,6 +119763,10 @@ var Session = class {
|
|
|
119733
119763
|
this.ensureOpen();
|
|
119734
119764
|
return this.rpc.getGoal({ sessionId: this.id });
|
|
119735
119765
|
}
|
|
119766
|
+
async getTodos() {
|
|
119767
|
+
this.ensureOpen();
|
|
119768
|
+
return this.rpc.getTodos({ sessionId: this.id });
|
|
119769
|
+
}
|
|
119736
119770
|
async setGoalBudget(value, unit) {
|
|
119737
119771
|
this.ensureOpen();
|
|
119738
119772
|
return this.rpc.setGoalBudget({
|
|
@@ -120184,7 +120218,7 @@ function optionalBuildString(value) {
|
|
|
120184
120218
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
120185
120219
|
}
|
|
120186
120220
|
const SCREAM_BUILD_INFO = {
|
|
120187
|
-
version: optionalBuildString("0.11.
|
|
120221
|
+
version: optionalBuildString("0.11.2"),
|
|
120188
120222
|
channel: optionalBuildString(""),
|
|
120189
120223
|
commit: optionalBuildString(""),
|
|
120190
120224
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -120273,7 +120307,7 @@ async function handleExport(deps, sessionId, output, opts) {
|
|
|
120273
120307
|
});
|
|
120274
120308
|
deps.stdout.write(`${result.zipPath}\n`);
|
|
120275
120309
|
} catch (error) {
|
|
120276
|
-
deps.stderr.write(`${errorMessage(error)}\n`);
|
|
120310
|
+
deps.stderr.write(`${errorMessage$1(error)}\n`);
|
|
120277
120311
|
deps.exit(1);
|
|
120278
120312
|
}
|
|
120279
120313
|
}
|
|
@@ -120336,7 +120370,7 @@ async function confirmPreviousSession(summary) {
|
|
|
120336
120370
|
rl.close();
|
|
120337
120371
|
}
|
|
120338
120372
|
}
|
|
120339
|
-
function errorMessage(error) {
|
|
120373
|
+
function errorMessage$1(error) {
|
|
120340
120374
|
return error instanceof Error ? error.message : String(error);
|
|
120341
120375
|
}
|
|
120342
120376
|
//#endregion
|
|
@@ -125537,6 +125571,28 @@ function detectGoalConflict(state, _action) {
|
|
|
125537
125571
|
return null;
|
|
125538
125572
|
}
|
|
125539
125573
|
//#endregion
|
|
125574
|
+
//#region src/utils/goal-refiner.md
|
|
125575
|
+
var goal_refiner_default = "You are a goal refiner. Given a brief task description, produce one clear, actionable objective sentence of at most 200 characters. Return only the objective: no explanation, quotation marks, labels, or prefixes.\n";
|
|
125576
|
+
/**
|
|
125577
|
+
* Normalize an LLM-refined objective. Invalid output falls back to the user's
|
|
125578
|
+
* original description so refinement never loses the requested task.
|
|
125579
|
+
*/
|
|
125580
|
+
function parseRefinedGoal(rawOutput, fallback) {
|
|
125581
|
+
const normalized = rawOutput.trim().replace(/^```(?:text)?\s*/i, "").replace(/\s*```$/, "").trim().replace(/^(?:objective|goal)\s*:\s*/i, "").replace(/^["'“‘](.*)["'”’]$/s, "$1").replaceAll(/\s+/g, " ").trim();
|
|
125582
|
+
if (normalized.length === 0 || normalized.length > 200) return fallback.trim();
|
|
125583
|
+
return normalized;
|
|
125584
|
+
}
|
|
125585
|
+
/** Refine a goal through the session LLM, with a deterministic raw-input fallback. */
|
|
125586
|
+
async function refineGoal(session, description) {
|
|
125587
|
+
const fallback = description.trim();
|
|
125588
|
+
if (fallback.length === 0) return "";
|
|
125589
|
+
try {
|
|
125590
|
+
return parseRefinedGoal(await session.generateText(goal_refiner_default, fallback), fallback);
|
|
125591
|
+
} catch {
|
|
125592
|
+
return fallback;
|
|
125593
|
+
}
|
|
125594
|
+
}
|
|
125595
|
+
//#endregion
|
|
125540
125596
|
//#region src/tui/commands/goal.ts
|
|
125541
125597
|
const GOAL_STATUS_DISMISS_MS = 1e4;
|
|
125542
125598
|
let activeGoalPanel;
|
|
@@ -125633,7 +125689,6 @@ async function createGoal(host, parsed) {
|
|
|
125633
125689
|
}
|
|
125634
125690
|
await showGoalConfigWizard(host, session, parsed.objective, parsed.replace);
|
|
125635
125691
|
}
|
|
125636
|
-
const GOAL_REFINER_SYSTEM_PROMPT = "You are a goal refiner. Given a brief task description, produce a single clear, actionable objective sentence (max 200 chars). Do not add explanations, quotes, or prefixes.";
|
|
125637
125692
|
/**
|
|
125638
125693
|
* Guided goal creation: collect a brief task description, refine it via the
|
|
125639
125694
|
* LLM into a single objective sentence, let the user confirm/edit, then enter
|
|
@@ -125659,13 +125714,7 @@ async function guidedGoalSetup(host) {
|
|
|
125659
125714
|
});
|
|
125660
125715
|
if (initialDesc === void 0) return;
|
|
125661
125716
|
host.showStatus(t("goal.setup_refining"));
|
|
125662
|
-
|
|
125663
|
-
try {
|
|
125664
|
-
objective = (await session.generateText(GOAL_REFINER_SYSTEM_PROMPT, initialDesc)).trim();
|
|
125665
|
-
if (objective.length === 0) objective = initialDesc;
|
|
125666
|
-
} catch {
|
|
125667
|
-
objective = initialDesc;
|
|
125668
|
-
}
|
|
125717
|
+
const objective = await refineGoal(session, initialDesc);
|
|
125669
125718
|
const confirmed = await promptText(host, TextInputDialogComponent, {
|
|
125670
125719
|
title: t("goal.setup_title_confirm"),
|
|
125671
125720
|
subtitle: t("goal.setup_confirm_hint"),
|
|
@@ -131153,7 +131202,7 @@ async function readMarketplaceText(location, fetchImpl) {
|
|
|
131153
131202
|
}
|
|
131154
131203
|
function parseMarketplaceEntry(value, index, location) {
|
|
131155
131204
|
if (!isRecord$3(value)) throw new TypeError(`插件市场条目 ${index + 1} 必须是一个对象.`);
|
|
131156
|
-
const id = requiredString(value, "id", index);
|
|
131205
|
+
const id = requiredString$1(value, "id", index);
|
|
131157
131206
|
const source = stringField$1(value, "source") ?? stringField$1(value, "url") ?? stringField$1(value, "downloadUrl");
|
|
131158
131207
|
if (source === void 0) throw new Error(`插件市场条目 ${id} 必须定义 "source".`);
|
|
131159
131208
|
return {
|
|
@@ -131188,7 +131237,7 @@ function resolveLocalPath(input, workDir) {
|
|
|
131188
131237
|
if (input.startsWith("~/")) return join(homedir(), input.slice(2));
|
|
131189
131238
|
return isAbsolute(input) ? input : resolve(workDir, input);
|
|
131190
131239
|
}
|
|
131191
|
-
function requiredString(value, field, index) {
|
|
131240
|
+
function requiredString$1(value, field, index) {
|
|
131192
131241
|
const result = stringField$1(value, field);
|
|
131193
131242
|
if (result === void 0) throw new Error(`插件市场条目 ${index + 1} 必须定义 "${field}".`);
|
|
131194
131243
|
return result;
|
|
@@ -135727,6 +135776,9 @@ var SessionEventHandler = class {
|
|
|
135727
135776
|
case "goal.updated":
|
|
135728
135777
|
this.handleGoalUpdated(event);
|
|
135729
135778
|
break;
|
|
135779
|
+
case "todo.updated":
|
|
135780
|
+
this.handleTodoUpdated(event);
|
|
135781
|
+
break;
|
|
135730
135782
|
default: break;
|
|
135731
135783
|
}
|
|
135732
135784
|
}
|
|
@@ -135830,7 +135882,7 @@ var SessionEventHandler = class {
|
|
|
135830
135882
|
handleTurnEnd(event, sendQueued) {
|
|
135831
135883
|
this.host.setAppState({ reconnectAttempt: 0 });
|
|
135832
135884
|
const todos = this.host.state.todoPanel.getTodos();
|
|
135833
|
-
if (todos.length > 0 && todos.every((
|
|
135885
|
+
if (todos.length > 0 && todos.every((todo) => todo.status === "done")) this.host.streamingUI.setTodoList([]);
|
|
135834
135886
|
this.host.streamingUI.resetToolUi();
|
|
135835
135887
|
this.host.streamingUI.finalizeTurn(sendQueued);
|
|
135836
135888
|
}
|
|
@@ -136001,17 +136053,7 @@ var SessionEventHandler = class {
|
|
|
136001
136053
|
synthetic: event.synthetic,
|
|
136002
136054
|
display: event.display
|
|
136003
136055
|
};
|
|
136004
|
-
|
|
136005
|
-
if (matchedCall !== void 0 && matchedCall.name === "TodoList" && !event.isError) {
|
|
136006
|
-
const rawTodos = matchedCall.args.todos;
|
|
136007
|
-
if (Array.isArray(rawTodos)) {
|
|
136008
|
-
const sanitized = rawTodos.filter((todo) => isTodoItemShape(todo)).map((t) => ({
|
|
136009
|
-
title: t.title,
|
|
136010
|
-
status: t.status
|
|
136011
|
-
}));
|
|
136012
|
-
streamingUI.setTodoList(sanitized);
|
|
136013
|
-
}
|
|
136014
|
-
}
|
|
136056
|
+
streamingUI.completeToolResult(event.toolCallId, resultData);
|
|
136015
136057
|
if (canTransitionTo(this.host.state.appState.streamingPhase, "waiting")) this.host.setAppState({ streamingPhase: "waiting" });
|
|
136016
136058
|
}
|
|
136017
136059
|
handleStatusUpdate(event) {
|
|
@@ -136155,6 +136197,9 @@ var SessionEventHandler = class {
|
|
|
136155
136197
|
goalActive: snapshot.status === "active"
|
|
136156
136198
|
});
|
|
136157
136199
|
}
|
|
136200
|
+
handleTodoUpdated(event) {
|
|
136201
|
+
this.host.streamingUI.setTodoList(event.todos);
|
|
136202
|
+
}
|
|
136158
136203
|
handleCompactionBegin(event) {
|
|
136159
136204
|
this.host.streamingUI.finalizeLiveTextBuffers();
|
|
136160
136205
|
this.lastCompactionTrigger = event.trigger;
|
|
@@ -146297,9 +146342,11 @@ function installStreamJsonTerminationHandlers(cleanup) {
|
|
|
146297
146342
|
/** Error carrying an HTTP status code for REST handlers. */
|
|
146298
146343
|
var HttpError = class extends Error {
|
|
146299
146344
|
statusCode;
|
|
146300
|
-
|
|
146345
|
+
code;
|
|
146346
|
+
constructor(statusCode, message, code) {
|
|
146301
146347
|
super(message);
|
|
146302
146348
|
this.statusCode = statusCode;
|
|
146349
|
+
this.code = code;
|
|
146303
146350
|
}
|
|
146304
146351
|
};
|
|
146305
146352
|
const HEARTBEAT_INTERVAL_MS = 3e4;
|
|
@@ -146331,6 +146378,16 @@ const contentTypes = {
|
|
|
146331
146378
|
ttf: "font/ttf"
|
|
146332
146379
|
};
|
|
146333
146380
|
/** Reads and parses a JSON request body (64 KiB cap). */
|
|
146381
|
+
function cloneSnapshot(value) {
|
|
146382
|
+
return structuredClone(value);
|
|
146383
|
+
}
|
|
146384
|
+
function errorMessage(error) {
|
|
146385
|
+
return error instanceof Error ? error.message : String(error);
|
|
146386
|
+
}
|
|
146387
|
+
function sendJson(res, statusCode, body) {
|
|
146388
|
+
res.writeHead(statusCode, { "Content-Type": "application/json" });
|
|
146389
|
+
res.end(JSON.stringify(body));
|
|
146390
|
+
}
|
|
146334
146391
|
function readJsonBody(req) {
|
|
146335
146392
|
return new Promise((resolve, reject) => {
|
|
146336
146393
|
let data = "";
|
|
@@ -146354,7 +146411,12 @@ function readJsonBody(req) {
|
|
|
146354
146411
|
return;
|
|
146355
146412
|
}
|
|
146356
146413
|
try {
|
|
146357
|
-
|
|
146414
|
+
const parsed = JSON.parse(data);
|
|
146415
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
146416
|
+
reject(new HttpError(400, "JSON body must be an object"));
|
|
146417
|
+
return;
|
|
146418
|
+
}
|
|
146419
|
+
resolve(parsed);
|
|
146358
146420
|
} catch {
|
|
146359
146421
|
reject(new HttpError(400, "Invalid JSON body"));
|
|
146360
146422
|
}
|
|
@@ -146362,6 +146424,75 @@ function readJsonBody(req) {
|
|
|
146362
146424
|
req.on("error", reject);
|
|
146363
146425
|
});
|
|
146364
146426
|
}
|
|
146427
|
+
function requiredString(body, field) {
|
|
146428
|
+
const value = body[field];
|
|
146429
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new HttpError(400, `Missing or empty ${field} field`, "request.invalid");
|
|
146430
|
+
return value.trim();
|
|
146431
|
+
}
|
|
146432
|
+
function optionalString(body, field) {
|
|
146433
|
+
const value = body[field];
|
|
146434
|
+
if (value === void 0) return void 0;
|
|
146435
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new HttpError(400, `${field} must be a non-empty string`, "request.invalid");
|
|
146436
|
+
return value.trim();
|
|
146437
|
+
}
|
|
146438
|
+
function optionalBoolean(body, field, fallback) {
|
|
146439
|
+
const value = body[field];
|
|
146440
|
+
if (value === void 0) return fallback;
|
|
146441
|
+
if (typeof value !== "boolean") throw new HttpError(400, `${field} must be a boolean`, "request.invalid");
|
|
146442
|
+
return value;
|
|
146443
|
+
}
|
|
146444
|
+
function parseGoalBudgets(body) {
|
|
146445
|
+
const raw = body["budgets"];
|
|
146446
|
+
if (raw === void 0) return [];
|
|
146447
|
+
if (!Array.isArray(raw)) throw new HttpError(400, "budgets must be an array", "request.invalid");
|
|
146448
|
+
const validUnits = new Set([
|
|
146449
|
+
"turns",
|
|
146450
|
+
"tokens",
|
|
146451
|
+
"milliseconds",
|
|
146452
|
+
"seconds",
|
|
146453
|
+
"minutes",
|
|
146454
|
+
"hours"
|
|
146455
|
+
]);
|
|
146456
|
+
const seen = /* @__PURE__ */ new Set();
|
|
146457
|
+
return raw.map((item) => {
|
|
146458
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) throw new HttpError(400, "Each budget must be an object", "request.invalid");
|
|
146459
|
+
const value = item["value"];
|
|
146460
|
+
const unit = item["unit"];
|
|
146461
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) throw new HttpError(400, "Budget value must be a positive safe integer", "request.invalid");
|
|
146462
|
+
if (typeof unit !== "string" || !validUnits.has(unit)) throw new HttpError(400, "Budget unit must be turns, tokens, milliseconds, seconds, minutes, or hours", "request.invalid");
|
|
146463
|
+
const typedUnit = unit;
|
|
146464
|
+
const dimension = typedUnit === "turns" || typedUnit === "tokens" ? typedUnit : "time";
|
|
146465
|
+
if (seen.has(dimension)) throw new HttpError(400, `Duplicate budget dimension: ${dimension}`, "request.invalid");
|
|
146466
|
+
seen.add(dimension);
|
|
146467
|
+
return {
|
|
146468
|
+
value,
|
|
146469
|
+
unit: typedUnit
|
|
146470
|
+
};
|
|
146471
|
+
});
|
|
146472
|
+
}
|
|
146473
|
+
function toHttpError(error) {
|
|
146474
|
+
if (error instanceof HttpError) return error;
|
|
146475
|
+
if (isScreamError(error)) switch (error.code) {
|
|
146476
|
+
case ErrorCodes.GOAL_OBJECTIVE_EMPTY:
|
|
146477
|
+
case ErrorCodes.GOAL_OBJECTIVE_TOO_LONG:
|
|
146478
|
+
case ErrorCodes.REQUEST_INVALID: return new HttpError(400, error.message, error.code);
|
|
146479
|
+
case ErrorCodes.GOAL_NOT_FOUND:
|
|
146480
|
+
case ErrorCodes.SESSION_NOT_FOUND: return new HttpError(404, error.message, error.code);
|
|
146481
|
+
case ErrorCodes.GOAL_ALREADY_EXISTS:
|
|
146482
|
+
case ErrorCodes.GOAL_NOT_RESUMABLE:
|
|
146483
|
+
case ErrorCodes.GOAL_STATUS_INVALID:
|
|
146484
|
+
case ErrorCodes.TURN_AGENT_BUSY: return new HttpError(409, error.message, error.code);
|
|
146485
|
+
default: return new HttpError(500, error.message, error.code);
|
|
146486
|
+
}
|
|
146487
|
+
return new HttpError(500, errorMessage(error));
|
|
146488
|
+
}
|
|
146489
|
+
function sendHttpError(res, error) {
|
|
146490
|
+
const mapped = toHttpError(error);
|
|
146491
|
+
sendJson(res, mapped.statusCode, {
|
|
146492
|
+
code: mapped.code ?? mapped.statusCode,
|
|
146493
|
+
message: mapped.message
|
|
146494
|
+
});
|
|
146495
|
+
}
|
|
146365
146496
|
function isVolatileEvent(event) {
|
|
146366
146497
|
switch (event.type) {
|
|
146367
146498
|
case "assistant.delta":
|
|
@@ -146549,6 +146680,14 @@ var WebSession = class {
|
|
|
146549
146680
|
nextSeq = 1;
|
|
146550
146681
|
epoch = 1;
|
|
146551
146682
|
cachedStatus = null;
|
|
146683
|
+
cachedGoal = null;
|
|
146684
|
+
cachedTodos = [];
|
|
146685
|
+
goalEventRevision = 0;
|
|
146686
|
+
todoEventRevision = 0;
|
|
146687
|
+
goalMutationTail = Promise.resolve();
|
|
146688
|
+
pendingGoalMutations = 0;
|
|
146689
|
+
coreSessionId;
|
|
146690
|
+
ready;
|
|
146552
146691
|
title = "New Session";
|
|
146553
146692
|
pendingApprovals = /* @__PURE__ */ new Map();
|
|
146554
146693
|
heartbeatTimer = null;
|
|
@@ -146570,13 +146709,14 @@ var WebSession = class {
|
|
|
146570
146709
|
this.yolo = opts.yolo;
|
|
146571
146710
|
this.homeDir = opts.homeDir ?? null;
|
|
146572
146711
|
this.createdAt = opts.createdAt;
|
|
146712
|
+
this.coreSessionId = session?.id ?? opts.coreSessionId ?? opts.sessionId;
|
|
146573
146713
|
this.onFork = opts.onFork;
|
|
146574
146714
|
if (opts.title) this.title = opts.title;
|
|
146575
146715
|
if (session) {
|
|
146576
|
-
this.refreshStatus();
|
|
146577
146716
|
this.subscribeEvents();
|
|
146578
146717
|
this.setupApprovalHandler();
|
|
146579
|
-
|
|
146718
|
+
this.ready = this.initializeCoreState();
|
|
146719
|
+
} else this.ready = Promise.resolve();
|
|
146580
146720
|
this.startHeartbeat();
|
|
146581
146721
|
}
|
|
146582
146722
|
get isActive() {
|
|
@@ -146646,6 +146786,8 @@ var WebSession = class {
|
|
|
146646
146786
|
const je = entry;
|
|
146647
146787
|
this.journal.push(je);
|
|
146648
146788
|
this.nextSeq = Math.max(this.nextSeq, je.seq + 1);
|
|
146789
|
+
if (this.session === null && je.payload.agentId === "main" && je.payload.type === "goal.updated") this.cachedGoal = cloneSnapshot(je.payload.snapshot);
|
|
146790
|
+
else if (this.session === null && je.payload.agentId === "main" && je.payload.type === "todo.updated") this.cachedTodos = cloneSnapshot(je.payload.todos);
|
|
146649
146791
|
}
|
|
146650
146792
|
const msgs = this.buildMessages();
|
|
146651
146793
|
if (msgs.length > 0 && !this.customTitle) this.title = deriveTitle(msgs);
|
|
@@ -146661,6 +146803,7 @@ var WebSession = class {
|
|
|
146661
146803
|
getMetadata() {
|
|
146662
146804
|
return {
|
|
146663
146805
|
sessionId: this.sessionId,
|
|
146806
|
+
coreSessionId: this.coreSessionId,
|
|
146664
146807
|
workDir: this.workDir,
|
|
146665
146808
|
title: this.title,
|
|
146666
146809
|
createdAt: this.createdAt,
|
|
@@ -146669,11 +146812,12 @@ var WebSession = class {
|
|
|
146669
146812
|
};
|
|
146670
146813
|
}
|
|
146671
146814
|
appendEvent(event) {
|
|
146815
|
+
const payload = event.type === "goal.updated" || event.type === "todo.updated" ? cloneSnapshot(event) : event;
|
|
146672
146816
|
const entry = {
|
|
146673
146817
|
seq: this.nextSeq++,
|
|
146674
146818
|
epoch: this.epoch,
|
|
146675
|
-
volatile: isVolatileEvent(
|
|
146676
|
-
payload
|
|
146819
|
+
volatile: isVolatileEvent(payload),
|
|
146820
|
+
payload
|
|
146677
146821
|
};
|
|
146678
146822
|
this.journal.push(entry);
|
|
146679
146823
|
if (!entry.volatile) this.persistEntry(entry);
|
|
@@ -146682,10 +146826,17 @@ var WebSession = class {
|
|
|
146682
146826
|
subscribeEvents() {
|
|
146683
146827
|
if (!this.session) return;
|
|
146684
146828
|
this.unsubscribe = this.session.onEvent((event) => {
|
|
146829
|
+
if ((event.type === "goal.updated" || event.type === "todo.updated") && event.agentId !== "main") return;
|
|
146685
146830
|
if (event.type === "turn.started") this.busy = true;
|
|
146686
146831
|
else if (event.type === "turn.ended") {
|
|
146687
146832
|
this.busy = false;
|
|
146688
146833
|
this.updateTitle();
|
|
146834
|
+
} else if (event.type === "goal.updated") {
|
|
146835
|
+
this.goalEventRevision += 1;
|
|
146836
|
+
this.cachedGoal = cloneSnapshot(event.snapshot);
|
|
146837
|
+
} else if (event.type === "todo.updated") {
|
|
146838
|
+
this.todoEventRevision += 1;
|
|
146839
|
+
this.cachedTodos = cloneSnapshot(event.todos);
|
|
146689
146840
|
}
|
|
146690
146841
|
if (event.type === "session.meta.updated" || event.type === "turn.ended") this.refreshStatus();
|
|
146691
146842
|
const entry = this.appendEvent(event);
|
|
@@ -146693,7 +146844,7 @@ var WebSession = class {
|
|
|
146693
146844
|
type: "event",
|
|
146694
146845
|
seq: entry.seq,
|
|
146695
146846
|
epoch: entry.epoch,
|
|
146696
|
-
payload:
|
|
146847
|
+
payload: entry.payload
|
|
146697
146848
|
}, entry.volatile);
|
|
146698
146849
|
if (event.type === "compaction.completed") {
|
|
146699
146850
|
const msgId = this.pendingCompactionMsgId;
|
|
@@ -146719,6 +146870,20 @@ var WebSession = class {
|
|
|
146719
146870
|
}
|
|
146720
146871
|
});
|
|
146721
146872
|
}
|
|
146873
|
+
async initializeCoreState() {
|
|
146874
|
+
const session = this.session;
|
|
146875
|
+
if (!session) return;
|
|
146876
|
+
const goalRevision = this.goalEventRevision;
|
|
146877
|
+
const todoRevision = this.todoEventRevision;
|
|
146878
|
+
const [status, goal, todos] = await Promise.all([
|
|
146879
|
+
session.getStatus().catch(() => null),
|
|
146880
|
+
session.getGoal(),
|
|
146881
|
+
session.getTodos()
|
|
146882
|
+
]);
|
|
146883
|
+
if (status !== null) this.cachedStatus = status;
|
|
146884
|
+
if (this.goalEventRevision === goalRevision) this.cachedGoal = cloneSnapshot(goal.goal);
|
|
146885
|
+
if (this.todoEventRevision === todoRevision) this.cachedTodos = cloneSnapshot(todos);
|
|
146886
|
+
}
|
|
146722
146887
|
async refreshStatus() {
|
|
146723
146888
|
if (!this.session) return;
|
|
146724
146889
|
try {
|
|
@@ -146809,7 +146974,7 @@ var WebSession = class {
|
|
|
146809
146974
|
});
|
|
146810
146975
|
return;
|
|
146811
146976
|
}
|
|
146812
|
-
if (!text || this.busy) {
|
|
146977
|
+
if (!text || this.busy || this.pendingGoalMutations > 0) {
|
|
146813
146978
|
this.send(ws, {
|
|
146814
146979
|
type: "error",
|
|
146815
146980
|
code: "session.busy",
|
|
@@ -146890,7 +147055,7 @@ var WebSession = class {
|
|
|
146890
147055
|
});
|
|
146891
147056
|
return;
|
|
146892
147057
|
}
|
|
146893
|
-
const missing = this.journal.
|
|
147058
|
+
const missing = this.journal.filter((entry) => entry.seq > lastSeq);
|
|
146894
147059
|
for (const entry of missing) if (!entry.volatile) this.send(ws, {
|
|
146895
147060
|
type: "event",
|
|
146896
147061
|
seq: entry.seq,
|
|
@@ -146988,7 +147153,7 @@ var WebSession = class {
|
|
|
146988
147153
|
return;
|
|
146989
147154
|
}
|
|
146990
147155
|
try {
|
|
146991
|
-
const result = await this.onFork(this.
|
|
147156
|
+
const result = await this.onFork(this.sessionId);
|
|
146992
147157
|
if (!result) {
|
|
146993
147158
|
fail("Fork 失败:无法复制当前会话。");
|
|
146994
147159
|
return;
|
|
@@ -147071,7 +147236,7 @@ var WebSession = class {
|
|
|
147071
147236
|
}, HEARTBEAT_INTERVAL_MS);
|
|
147072
147237
|
}
|
|
147073
147238
|
getSnapshot() {
|
|
147074
|
-
return {
|
|
147239
|
+
return cloneSnapshot({
|
|
147075
147240
|
sessionId: this.sessionId,
|
|
147076
147241
|
workDir: this.workDir,
|
|
147077
147242
|
seq: this.nextSeq - 1,
|
|
@@ -147097,8 +147262,10 @@ var WebSession = class {
|
|
|
147097
147262
|
},
|
|
147098
147263
|
busy: this.busy,
|
|
147099
147264
|
createdAt: this.createdAt,
|
|
147100
|
-
title: this.title
|
|
147101
|
-
|
|
147265
|
+
title: this.title,
|
|
147266
|
+
goal: this.cachedGoal,
|
|
147267
|
+
todos: this.cachedTodos
|
|
147268
|
+
});
|
|
147102
147269
|
}
|
|
147103
147270
|
getListItem() {
|
|
147104
147271
|
return {
|
|
@@ -147135,6 +147302,114 @@ var WebSession = class {
|
|
|
147135
147302
|
status: this.cachedStatus
|
|
147136
147303
|
}, false);
|
|
147137
147304
|
}
|
|
147305
|
+
getCoreSessionId() {
|
|
147306
|
+
return this.coreSessionId;
|
|
147307
|
+
}
|
|
147308
|
+
async refineGoalDescription(description) {
|
|
147309
|
+
await this.ready;
|
|
147310
|
+
return refineGoal(this.requireGoalSession(), description);
|
|
147311
|
+
}
|
|
147312
|
+
async createGoal(objective, completionCriterion, replace, budgets) {
|
|
147313
|
+
return this.serializeGoalMutation(async (session) => {
|
|
147314
|
+
let goal = await session.createGoal(objective, {
|
|
147315
|
+
completionCriterion,
|
|
147316
|
+
replace
|
|
147317
|
+
});
|
|
147318
|
+
try {
|
|
147319
|
+
for (const budget of budgets) goal = await session.setGoalBudget(budget.value, budget.unit);
|
|
147320
|
+
} catch (error) {
|
|
147321
|
+
throw new HttpError(500, `Goal configuration failed and the new goal was ${await this.pauseOrClearUnsafeActiveGoal(session)}: ${errorMessage(error)}`);
|
|
147322
|
+
}
|
|
147323
|
+
try {
|
|
147324
|
+
await this.startGoalExecution(session, objective);
|
|
147325
|
+
} catch (error) {
|
|
147326
|
+
throw new HttpError(500, `Goal was created but execution could not start; it was ${await this.pauseOrClearUnsafeActiveGoal(session)}: ${errorMessage(error)}`);
|
|
147327
|
+
}
|
|
147328
|
+
return cloneSnapshot(goal);
|
|
147329
|
+
});
|
|
147330
|
+
}
|
|
147331
|
+
async updateGoal(objective, budgets) {
|
|
147332
|
+
return this.serializeGoalMutation(async (session) => {
|
|
147333
|
+
const current = await session.getGoal();
|
|
147334
|
+
if (current.goal === null) throw new HttpError(404, "Goal not found", ErrorCodes.GOAL_NOT_FOUND);
|
|
147335
|
+
let goal = current.goal;
|
|
147336
|
+
if (objective !== void 0) goal = await session.updateGoalObjective(objective);
|
|
147337
|
+
for (const budget of budgets) goal = await session.setGoalBudget(budget.value, budget.unit);
|
|
147338
|
+
return cloneSnapshot(goal);
|
|
147339
|
+
});
|
|
147340
|
+
}
|
|
147341
|
+
async pauseGoal() {
|
|
147342
|
+
return this.serializeGoalMutation(async (session) => {
|
|
147343
|
+
const goal = await session.updateGoalStatus("paused");
|
|
147344
|
+
if (goal === null) throw new HttpError(404, "Goal not found", ErrorCodes.GOAL_NOT_FOUND);
|
|
147345
|
+
return cloneSnapshot(goal);
|
|
147346
|
+
}, true);
|
|
147347
|
+
}
|
|
147348
|
+
async resumeGoal() {
|
|
147349
|
+
return this.serializeGoalMutation(async (session) => {
|
|
147350
|
+
if ((await session.getGoal()).goal === null) throw new HttpError(404, "Goal not found", ErrorCodes.GOAL_NOT_FOUND);
|
|
147351
|
+
const goal = await session.updateGoalStatus("active");
|
|
147352
|
+
if (goal === null) throw new HttpError(404, "Goal not found", ErrorCodes.GOAL_NOT_FOUND);
|
|
147353
|
+
try {
|
|
147354
|
+
await this.startGoalExecution(session, "Continue working toward the active goal. Review current progress and remaining work before proceeding.");
|
|
147355
|
+
} catch (error) {
|
|
147356
|
+
throw new HttpError(500, `Goal was resumed but execution could not continue; it was ${await this.pauseOrClearUnsafeActiveGoal(session)}: ${errorMessage(error)}`);
|
|
147357
|
+
}
|
|
147358
|
+
return cloneSnapshot(goal);
|
|
147359
|
+
});
|
|
147360
|
+
}
|
|
147361
|
+
async cancelGoal() {
|
|
147362
|
+
await this.serializeGoalMutation(async (session) => {
|
|
147363
|
+
if ((await session.getGoal()).goal === null) throw new HttpError(404, "Goal not found", ErrorCodes.GOAL_NOT_FOUND);
|
|
147364
|
+
await session.cancelGoal();
|
|
147365
|
+
}, true);
|
|
147366
|
+
}
|
|
147367
|
+
requireGoalSession(allowBusy = false) {
|
|
147368
|
+
if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
|
|
147369
|
+
if (!allowBusy && this.busy) throw new HttpError(409, "Session is busy", ErrorCodes.TURN_AGENT_BUSY);
|
|
147370
|
+
return this.session;
|
|
147371
|
+
}
|
|
147372
|
+
serializeGoalMutation(mutation, allowBusy = false) {
|
|
147373
|
+
this.pendingGoalMutations += 1;
|
|
147374
|
+
const run = this.goalMutationTail.then(async () => {
|
|
147375
|
+
await this.ready;
|
|
147376
|
+
return mutation(this.requireGoalSession(allowBusy));
|
|
147377
|
+
});
|
|
147378
|
+
this.goalMutationTail = run.then(() => void 0, () => void 0);
|
|
147379
|
+
return run.finally(() => {
|
|
147380
|
+
this.pendingGoalMutations -= 1;
|
|
147381
|
+
});
|
|
147382
|
+
}
|
|
147383
|
+
async startGoalExecution(session, prompt) {
|
|
147384
|
+
this.busy = true;
|
|
147385
|
+
await this.persistUserMessage(prompt, this.nextSeq);
|
|
147386
|
+
this.broadcast({
|
|
147387
|
+
type: "user_message",
|
|
147388
|
+
text: prompt
|
|
147389
|
+
}, false);
|
|
147390
|
+
try {
|
|
147391
|
+
await session.prompt(prompt);
|
|
147392
|
+
} catch (error) {
|
|
147393
|
+
this.busy = false;
|
|
147394
|
+
throw error;
|
|
147395
|
+
}
|
|
147396
|
+
}
|
|
147397
|
+
async clearUnsafeActiveGoal(session) {
|
|
147398
|
+
try {
|
|
147399
|
+
await session.cancelGoal();
|
|
147400
|
+
} catch (rollbackError) {
|
|
147401
|
+
throw new HttpError(500, `Goal setup failed and rollback also failed: ${errorMessage(rollbackError)}`);
|
|
147402
|
+
}
|
|
147403
|
+
}
|
|
147404
|
+
async pauseOrClearUnsafeActiveGoal(session) {
|
|
147405
|
+
try {
|
|
147406
|
+
await session.updateGoalStatus("paused");
|
|
147407
|
+
return "paused";
|
|
147408
|
+
} catch {
|
|
147409
|
+
await this.clearUnsafeActiveGoal(session);
|
|
147410
|
+
return "cancelled";
|
|
147411
|
+
}
|
|
147412
|
+
}
|
|
147138
147413
|
get connectionCount() {
|
|
147139
147414
|
return this.connections.size;
|
|
147140
147415
|
}
|
|
@@ -147220,6 +147495,64 @@ var WebSession = class {
|
|
|
147220
147495
|
if (this.homeDir) await deletePersistedSession(this.homeDir, this.sessionId);
|
|
147221
147496
|
}
|
|
147222
147497
|
};
|
|
147498
|
+
async function handleGoalRoute(req, res, url, resolveSession) {
|
|
147499
|
+
const match = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/goal(?:/(refine|pause|resume|cancel))?$`).exec(url);
|
|
147500
|
+
if (!match) return false;
|
|
147501
|
+
const webSession = resolveSession(match[1]);
|
|
147502
|
+
if (!webSession) {
|
|
147503
|
+
sendJson(res, 404, {
|
|
147504
|
+
code: ErrorCodes.SESSION_NOT_FOUND,
|
|
147505
|
+
message: "Session not found"
|
|
147506
|
+
});
|
|
147507
|
+
return true;
|
|
147508
|
+
}
|
|
147509
|
+
const operation = match[2];
|
|
147510
|
+
const method = req.method ?? "GET";
|
|
147511
|
+
const expectedMethod = operation === void 0 ? new Set(["POST", "PATCH"]) : new Set(["POST"]);
|
|
147512
|
+
if (!expectedMethod.has(method)) {
|
|
147513
|
+
res.setHeader("Allow", [...expectedMethod].join(", "));
|
|
147514
|
+
sendJson(res, 405, {
|
|
147515
|
+
code: 405,
|
|
147516
|
+
message: "Method not allowed"
|
|
147517
|
+
});
|
|
147518
|
+
return true;
|
|
147519
|
+
}
|
|
147520
|
+
try {
|
|
147521
|
+
const body = await readJsonBody(req);
|
|
147522
|
+
if (operation === "refine") {
|
|
147523
|
+
sendJson(res, 200, { objective: await webSession.refineGoalDescription(requiredString(body, "description")) });
|
|
147524
|
+
return true;
|
|
147525
|
+
}
|
|
147526
|
+
if (operation === "pause") {
|
|
147527
|
+
await webSession.pauseGoal();
|
|
147528
|
+
sendJson(res, 202, { ok: true });
|
|
147529
|
+
return true;
|
|
147530
|
+
}
|
|
147531
|
+
if (operation === "resume") {
|
|
147532
|
+
await webSession.resumeGoal();
|
|
147533
|
+
sendJson(res, 202, { ok: true });
|
|
147534
|
+
return true;
|
|
147535
|
+
}
|
|
147536
|
+
if (operation === "cancel") {
|
|
147537
|
+
await webSession.cancelGoal();
|
|
147538
|
+
sendJson(res, 202, { ok: true });
|
|
147539
|
+
return true;
|
|
147540
|
+
}
|
|
147541
|
+
const budgets = parseGoalBudgets(body);
|
|
147542
|
+
if (method === "POST") {
|
|
147543
|
+
await webSession.createGoal(requiredString(body, "objective"), optionalString(body, "completionCriterion"), optionalBoolean(body, "replace", false), budgets);
|
|
147544
|
+
sendJson(res, 202, { ok: true });
|
|
147545
|
+
return true;
|
|
147546
|
+
}
|
|
147547
|
+
const objective = optionalString(body, "objective");
|
|
147548
|
+
if (objective === void 0 && body["budgets"] === void 0) throw new HttpError(400, "PATCH requires objective or budgets", "request.invalid");
|
|
147549
|
+
await webSession.updateGoal(objective, budgets);
|
|
147550
|
+
sendJson(res, 202, { ok: true });
|
|
147551
|
+
} catch (error) {
|
|
147552
|
+
sendHttpError(res, error);
|
|
147553
|
+
}
|
|
147554
|
+
return true;
|
|
147555
|
+
}
|
|
147223
147556
|
var SessionManager = class {
|
|
147224
147557
|
sessions = /* @__PURE__ */ new Map();
|
|
147225
147558
|
/** In-flight activation promises to deduplicate concurrent calls. */
|
|
@@ -147245,6 +147578,7 @@ var SessionManager = class {
|
|
|
147245
147578
|
const entries = await loadJournal(this.homeDir, meta.sessionId);
|
|
147246
147579
|
const ws = new WebSession(null, {
|
|
147247
147580
|
sessionId: meta.sessionId,
|
|
147581
|
+
coreSessionId: meta.coreSessionId ?? meta.sessionId,
|
|
147248
147582
|
workDir: meta.workDir,
|
|
147249
147583
|
permission: meta.permission,
|
|
147250
147584
|
yolo: this.yolo,
|
|
@@ -147274,6 +147608,12 @@ var SessionManager = class {
|
|
|
147274
147608
|
createdAt,
|
|
147275
147609
|
onFork: (id) => this.forkSession(id)
|
|
147276
147610
|
});
|
|
147611
|
+
try {
|
|
147612
|
+
await webSession.ready;
|
|
147613
|
+
} catch (error) {
|
|
147614
|
+
await webSession.close();
|
|
147615
|
+
throw error;
|
|
147616
|
+
}
|
|
147277
147617
|
await saveMetadata(this.homeDir, webSession.getMetadata());
|
|
147278
147618
|
this.sessions.set(sessionId, webSession);
|
|
147279
147619
|
log.info("web: session created", {
|
|
@@ -147297,13 +147637,8 @@ var SessionManager = class {
|
|
|
147297
147637
|
const existing = this.sessions.get(sessionId);
|
|
147298
147638
|
if (!existing) return null;
|
|
147299
147639
|
if (existing.isActive) return existing;
|
|
147300
|
-
const session = await this.harness.createSession({
|
|
147301
|
-
workDir: existing.workDir,
|
|
147302
|
-
model: this.model,
|
|
147303
|
-
permission: this.permission
|
|
147304
|
-
});
|
|
147305
147640
|
const meta = existing.getMetadata();
|
|
147306
|
-
await
|
|
147641
|
+
const session = await this.harness.resumeSession({ id: meta.coreSessionId ?? meta.sessionId });
|
|
147307
147642
|
const reactivated = new WebSession(session, {
|
|
147308
147643
|
sessionId,
|
|
147309
147644
|
workDir: meta.workDir,
|
|
@@ -147312,10 +147647,18 @@ var SessionManager = class {
|
|
|
147312
147647
|
homeDir: this.homeDir,
|
|
147313
147648
|
createdAt: meta.createdAt,
|
|
147314
147649
|
title: meta.title,
|
|
147650
|
+
coreSessionId: session.id,
|
|
147315
147651
|
onFork: (id) => this.forkSession(id)
|
|
147316
147652
|
});
|
|
147317
147653
|
const entries = await loadJournal(this.homeDir, sessionId);
|
|
147318
147654
|
reactivated.loadFromPersisted(entries);
|
|
147655
|
+
try {
|
|
147656
|
+
await reactivated.ready;
|
|
147657
|
+
} catch (error) {
|
|
147658
|
+
await reactivated.close();
|
|
147659
|
+
throw error;
|
|
147660
|
+
}
|
|
147661
|
+
await existing.close();
|
|
147319
147662
|
this.sessions.set(sessionId, reactivated);
|
|
147320
147663
|
await saveMetadata(this.homeDir, reactivated.getMetadata());
|
|
147321
147664
|
log.info("web: session reactivated", { sessionId });
|
|
@@ -147333,7 +147676,7 @@ var SessionManager = class {
|
|
|
147333
147676
|
const source = this.sessions.get(sourceId);
|
|
147334
147677
|
if (!source || !source.isActive || source.isBusy) return null;
|
|
147335
147678
|
try {
|
|
147336
|
-
const session = await this.harness.forkSession({ id:
|
|
147679
|
+
const session = await this.harness.forkSession({ id: source.getCoreSessionId() });
|
|
147337
147680
|
const newId = session.id;
|
|
147338
147681
|
const createdAt = Date.now();
|
|
147339
147682
|
const title = `${source.getTitle()} (fork)`;
|
|
@@ -147353,6 +147696,12 @@ var SessionManager = class {
|
|
|
147353
147696
|
await writeFile(getJournalPath(this.homeDir, newId), lines);
|
|
147354
147697
|
}
|
|
147355
147698
|
webSession.loadFromPersisted(entries);
|
|
147699
|
+
try {
|
|
147700
|
+
await webSession.ready;
|
|
147701
|
+
} catch (error) {
|
|
147702
|
+
await webSession.close();
|
|
147703
|
+
throw error;
|
|
147704
|
+
}
|
|
147356
147705
|
await saveMetadata(this.homeDir, webSession.getMetadata());
|
|
147357
147706
|
this.sessions.set(newId, webSession);
|
|
147358
147707
|
log.info("web: session forked", {
|
|
@@ -147520,6 +147869,7 @@ async function runWebServer(opts) {
|
|
|
147520
147869
|
}
|
|
147521
147870
|
return;
|
|
147522
147871
|
}
|
|
147872
|
+
if (await handleGoalRoute(req, res, url, (sessionId) => manager.get(sessionId))) return;
|
|
147523
147873
|
const snapshotMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/snapshot$`).exec(url);
|
|
147524
147874
|
if (snapshotMatch && method === "GET") {
|
|
147525
147875
|
const sessionId = snapshotMatch[1];
|
|
@@ -147532,8 +147882,12 @@ async function runWebServer(opts) {
|
|
|
147532
147882
|
}));
|
|
147533
147883
|
return;
|
|
147534
147884
|
}
|
|
147535
|
-
|
|
147536
|
-
|
|
147885
|
+
try {
|
|
147886
|
+
await ws.ready;
|
|
147887
|
+
sendJson(res, 200, ws.getSnapshot());
|
|
147888
|
+
} catch (error) {
|
|
147889
|
+
sendHttpError(res, error);
|
|
147890
|
+
}
|
|
147537
147891
|
return;
|
|
147538
147892
|
}
|
|
147539
147893
|
if (url === `${API_PREFIX}/git/status` && method === "GET") {
|
|
@@ -147701,12 +148055,24 @@ async function runWebServer(opts) {
|
|
|
147701
148055
|
manager.activateSession(sessionId).then((reactivated) => {
|
|
147702
148056
|
if (reactivated) reactivated.addConnection(ws);
|
|
147703
148057
|
else ws.close(1008, "Session not found");
|
|
148058
|
+
}).catch((error) => {
|
|
148059
|
+
log.warn("web: session activation failed", {
|
|
148060
|
+
sessionId,
|
|
148061
|
+
error: errorMessage(error)
|
|
148062
|
+
});
|
|
148063
|
+
ws.close(1011, "Session activation failed");
|
|
147704
148064
|
});
|
|
147705
148065
|
return;
|
|
147706
148066
|
}
|
|
147707
148067
|
manager.activateSession(sessionId).then((reactivated) => {
|
|
147708
148068
|
if (reactivated) reactivated.addConnection(ws);
|
|
147709
148069
|
else ws.close(1008, "Session not found");
|
|
148070
|
+
}).catch((error) => {
|
|
148071
|
+
log.warn("web: session activation failed", {
|
|
148072
|
+
sessionId,
|
|
148073
|
+
error: errorMessage(error)
|
|
148074
|
+
});
|
|
148075
|
+
ws.close(1011, "Session activation failed");
|
|
147710
148076
|
});
|
|
147711
148077
|
return;
|
|
147712
148078
|
}
|