scream-code 0.5.0 → 0.5.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/main.mjs +307 -37
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -55982,7 +55982,7 @@ var GoalMode = class {
|
|
|
55982
55982
|
if (state === void 0 || state.status !== "active") return null;
|
|
55983
55983
|
return this.toSnapshot(state);
|
|
55984
55984
|
}
|
|
55985
|
-
async createGoal(input,
|
|
55985
|
+
async createGoal(input, _actor = "user") {
|
|
55986
55986
|
const objective = input.objective.trim();
|
|
55987
55987
|
if (objective.length === 0) throw new ScreamError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
|
|
55988
55988
|
if (objective.length > MAX_GOAL_OBJECTIVE_LENGTH) throw new ScreamError(ErrorCodes.GOAL_OBJECTIVE_TOO_LONG, `Goal objective cannot exceed ${MAX_GOAL_OBJECTIVE_LENGTH} characters`);
|
|
@@ -56056,7 +56056,7 @@ var GoalMode = class {
|
|
|
56056
56056
|
this.appendStatusUpdate(state, actor, input.reason);
|
|
56057
56057
|
return this.toSnapshot(state);
|
|
56058
56058
|
}
|
|
56059
|
-
async setBudgetLimits(input,
|
|
56059
|
+
async setBudgetLimits(input, _actor = "user") {
|
|
56060
56060
|
const state = this.requireState();
|
|
56061
56061
|
state.budgetLimits = {
|
|
56062
56062
|
...state.budgetLimits,
|
|
@@ -56244,6 +56244,15 @@ function buildGoalCompletionSummaryPrompt(goal) {
|
|
|
56244
56244
|
"Write a concise final message for the user. State that the goal is complete, summarize the main work completed, and mention any validation you ran. Do not call more goal tools."
|
|
56245
56245
|
].join("\n");
|
|
56246
56246
|
}
|
|
56247
|
+
function buildGradingFeedbackPrompt(reason) {
|
|
56248
|
+
return [
|
|
56249
|
+
"Goal verification failed. An independent reviewer found that the completion criteria were not genuinely met.",
|
|
56250
|
+
"",
|
|
56251
|
+
`Reviewer feedback:\n${reason}`,
|
|
56252
|
+
"",
|
|
56253
|
+
"Address every issue listed above before calling UpdateGoal with complete again. Do not re-submit until all issues are resolved."
|
|
56254
|
+
].join("\n");
|
|
56255
|
+
}
|
|
56247
56256
|
function buildGoalBlockedReasonPrompt(goal) {
|
|
56248
56257
|
return [
|
|
56249
56258
|
buildGoalBlockedMessage(goal),
|
|
@@ -56279,13 +56288,28 @@ const UpdateGoalToolInputSchema = z.object({ status: z.enum([
|
|
|
56279
56288
|
"paused",
|
|
56280
56289
|
"blocked"
|
|
56281
56290
|
]).describe("The lifecycle status to set for the current goal.") }).strict();
|
|
56291
|
+
const MAX_GRADER_OUTPUT_CHARS = 4e3;
|
|
56292
|
+
function extractRecentOutput(history) {
|
|
56293
|
+
const parts = [];
|
|
56294
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
56295
|
+
const msg = history[i];
|
|
56296
|
+
if (msg === void 0 || msg.role !== "assistant") continue;
|
|
56297
|
+
const text = msg.content.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
56298
|
+
if (text) parts.unshift(text);
|
|
56299
|
+
if (parts.join("\n\n").length >= MAX_GRADER_OUTPUT_CHARS) break;
|
|
56300
|
+
}
|
|
56301
|
+
const joined = parts.join("\n\n");
|
|
56302
|
+
return joined.length > MAX_GRADER_OUTPUT_CHARS ? `${joined.slice(0, MAX_GRADER_OUTPUT_CHARS)}…` : joined;
|
|
56303
|
+
}
|
|
56282
56304
|
var UpdateGoalTool = class {
|
|
56283
56305
|
agent;
|
|
56306
|
+
grader;
|
|
56284
56307
|
name = "UpdateGoal";
|
|
56285
56308
|
description = "Update the current goal's lifecycle status. Use `complete` when the goal is achieved, `blocked` when you cannot proceed, `paused` to park it, or `active` to resume.";
|
|
56286
56309
|
parameters = toInputJsonSchema(UpdateGoalToolInputSchema);
|
|
56287
|
-
constructor(agent) {
|
|
56310
|
+
constructor(agent, grader) {
|
|
56288
56311
|
this.agent = agent;
|
|
56312
|
+
this.grader = grader;
|
|
56289
56313
|
}
|
|
56290
56314
|
resolveExecution(args) {
|
|
56291
56315
|
const goal = this.agent.goal;
|
|
@@ -56297,17 +56321,7 @@ var UpdateGoalTool = class {
|
|
|
56297
56321
|
await goal.resumeGoal({}, "model");
|
|
56298
56322
|
return { output: "Goal resumed." };
|
|
56299
56323
|
}
|
|
56300
|
-
if (args.status === "complete")
|
|
56301
|
-
const completed = await goal.markComplete({}, "model");
|
|
56302
|
-
if (completed !== null) this.agent.context.appendSystemReminder(buildGoalCompletionSummaryPrompt(completed), {
|
|
56303
|
-
kind: "system_trigger",
|
|
56304
|
-
name: GOAL_COMPLETION_REMINDER_NAME
|
|
56305
|
-
});
|
|
56306
|
-
return {
|
|
56307
|
-
output: "Goal marked complete.",
|
|
56308
|
-
stopTurn: true
|
|
56309
|
-
};
|
|
56310
|
-
}
|
|
56324
|
+
if (args.status === "complete") return this.handleComplete(goal);
|
|
56311
56325
|
if (args.status === "blocked") {
|
|
56312
56326
|
const blocked = await goal.markBlocked({}, "model");
|
|
56313
56327
|
if (blocked !== null) this.agent.context.appendSystemReminder(buildGoalBlockedReasonPrompt(blocked), {
|
|
@@ -56327,6 +56341,39 @@ var UpdateGoalTool = class {
|
|
|
56327
56341
|
}
|
|
56328
56342
|
};
|
|
56329
56343
|
}
|
|
56344
|
+
async handleComplete(goal) {
|
|
56345
|
+
const goalState = goal.getGoal().goal;
|
|
56346
|
+
if (!goalState) return { output: "No active goal." };
|
|
56347
|
+
const output = extractRecentOutput(this.agent.context.history);
|
|
56348
|
+
await goal.pauseGoal({ reason: "verifying" }, "system");
|
|
56349
|
+
let pass;
|
|
56350
|
+
let reason;
|
|
56351
|
+
try {
|
|
56352
|
+
const result = await this.grader(goalState.objective, goalState.completionCriterion, output);
|
|
56353
|
+
pass = result.pass;
|
|
56354
|
+
reason = result.reason;
|
|
56355
|
+
} catch {
|
|
56356
|
+
pass = true;
|
|
56357
|
+
reason = "Grader unavailable";
|
|
56358
|
+
}
|
|
56359
|
+
await goal.resumeGoal({}, "system");
|
|
56360
|
+
if (pass) {
|
|
56361
|
+
const completed = await goal.markComplete({}, "model");
|
|
56362
|
+
if (completed !== null) this.agent.context.appendSystemReminder(buildGoalCompletionSummaryPrompt(completed), {
|
|
56363
|
+
kind: "system_trigger",
|
|
56364
|
+
name: GOAL_COMPLETION_REMINDER_NAME
|
|
56365
|
+
});
|
|
56366
|
+
return {
|
|
56367
|
+
output: `Goal verified and marked complete.\n${reason}`,
|
|
56368
|
+
stopTurn: true
|
|
56369
|
+
};
|
|
56370
|
+
}
|
|
56371
|
+
this.agent.context.appendSystemReminder(buildGradingFeedbackPrompt(reason), {
|
|
56372
|
+
kind: "system_trigger",
|
|
56373
|
+
name: "goal_grading_feedback"
|
|
56374
|
+
});
|
|
56375
|
+
return { output: `Verification failed: ${reason}. Continue working.` };
|
|
56376
|
+
}
|
|
56330
56377
|
};
|
|
56331
56378
|
//#endregion
|
|
56332
56379
|
//#region ../../packages/agent-core/src/tools/builtin/goal/write-goal-note.ts
|
|
@@ -73362,18 +73409,21 @@ var MemoryMemoStore = class {
|
|
|
73362
73409
|
const record = JSON.parse(rawLine);
|
|
73363
73410
|
if (record["type"] !== "memory_memo" || !record["entry"]) return void 0;
|
|
73364
73411
|
const entry = record["entry"];
|
|
73365
|
-
if (record["version"] === 1 || entry["userRequirement"] !== void 0 && entry["userNeed"] === void 0)
|
|
73366
|
-
|
|
73367
|
-
|
|
73368
|
-
|
|
73369
|
-
|
|
73370
|
-
|
|
73371
|
-
|
|
73372
|
-
|
|
73373
|
-
|
|
73374
|
-
|
|
73375
|
-
|
|
73376
|
-
|
|
73412
|
+
if (record["version"] === 1 || entry["userRequirement"] !== void 0 && entry["userNeed"] === void 0) {
|
|
73413
|
+
const str = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
73414
|
+
return {
|
|
73415
|
+
id: str(entry["id"]),
|
|
73416
|
+
sourceSessionId: str(entry["sourceSessionId"]),
|
|
73417
|
+
sourceSessionTitle: str(entry["sourceSessionTitle"], void 0),
|
|
73418
|
+
userNeed: str(entry["userRequirement"]),
|
|
73419
|
+
approach: str(entry["solution"]),
|
|
73420
|
+
outcome: str(entry["completionStatus"]),
|
|
73421
|
+
whatFailed: str(entry["problemsEncountered"], "none"),
|
|
73422
|
+
whatWorked: "none",
|
|
73423
|
+
extractionSource: entry["extractionSource"] === "exit" ? "exit" : "compaction",
|
|
73424
|
+
recordedAt: typeof entry["recordedAt"] === "number" ? entry["recordedAt"] : 0
|
|
73425
|
+
};
|
|
73426
|
+
}
|
|
73377
73427
|
return entry;
|
|
73378
73428
|
} catch {
|
|
73379
73429
|
return;
|
|
@@ -75988,6 +76038,8 @@ function buildGoalReminder(goal) {
|
|
|
75988
76038
|
lines.push("When you discover important facts, verify a hypothesis, or hit a dead end, call WriteGoalNote to record it. Future turns will read these notes automatically. Keep notes concise and actionable.");
|
|
75989
76039
|
lines.push("");
|
|
75990
76040
|
lines.push("Goal mode is iterative. Keep the self-audit brief each turn. Do not explore unrelated interpretations once the goal can be decided. If the objective is simple, already answered, impossible, unsafe, or contradictory, do not run another goal turn. Explain briefly if useful, then call UpdateGoal with `complete` or `blocked` in the same turn. Otherwise, self-audit against the objective and any completion criteria above, then do one coherent slice of work toward the objective. Use multiple turns when the task naturally has multiple phases. Call UpdateGoal with `complete` only when all required work is done, any stated validation has passed, and there is no useful next action. Do not mark complete after only producing a plan, summary, first pass, or partial result. If an external condition or required user input prevents progress, or the objective cannot be completed as stated, call UpdateGoal with `blocked`. Otherwise keep working — after your turn ends you will be prompted to continue. Call UpdateGoal as soon as the goal is genuinely done or cannot proceed; don't keep going once there is nothing left to do.");
|
|
76041
|
+
lines.push("");
|
|
76042
|
+
lines.push("When you call UpdateGoal with `complete`, an independent reviewer will verify that the completion criteria are met. Provide a clear summary of what you accomplished in your final response so the reviewer can evaluate it.");
|
|
75991
76043
|
return lines.join("\n");
|
|
75992
76044
|
}
|
|
75993
76045
|
function maxBudgetFraction(goal) {
|
|
@@ -76485,7 +76537,12 @@ const DEFAULT_APPROVE_TOOLS = new Set([
|
|
|
76485
76537
|
"Agent",
|
|
76486
76538
|
"AskUserQuestion",
|
|
76487
76539
|
"Skill",
|
|
76488
|
-
"WolfPack"
|
|
76540
|
+
"WolfPack",
|
|
76541
|
+
"CreateGoal",
|
|
76542
|
+
"UpdateGoal",
|
|
76543
|
+
"GetGoal",
|
|
76544
|
+
"SetGoalBudget",
|
|
76545
|
+
"WriteGoalNote"
|
|
76489
76546
|
]);
|
|
76490
76547
|
var DefaultToolApprovePermissionPolicy = class {
|
|
76491
76548
|
name = "default-tool-approve";
|
|
@@ -90869,6 +90926,151 @@ const DEFAULT_AGENT_PROFILES = loadAgentProfilesFromSources([
|
|
|
90869
90926
|
].map((file) => `profile/default/${file}`), PROFILE_SOURCES);
|
|
90870
90927
|
//#endregion
|
|
90871
90928
|
//#region ../../packages/agent-core/src/agent/tool/index.ts
|
|
90929
|
+
const CRITERIA_SYSTEM_PROMPT = [
|
|
90930
|
+
"You generate concrete, verifiable acceptance criteria for a given objective.",
|
|
90931
|
+
"Criteria must be specific and testable — state what should work end-to-end, not just what should exist.",
|
|
90932
|
+
"Avoid vague criteria like \"feature works\" or \"code is correct\".",
|
|
90933
|
+
"Respond with JSON only: {\"criteria\": [\"criterion 1\", \"criterion 2\", ...]}"
|
|
90934
|
+
].join(" ");
|
|
90935
|
+
const GRADER_SYSTEM_PROMPT = [
|
|
90936
|
+
"You are a strict goal completion evaluator. Your default judgment is FAIL. Only PASS when there is clear, specific evidence that every acceptance criterion is genuinely met end-to-end.",
|
|
90937
|
+
"Evaluate across three dimensions:",
|
|
90938
|
+
"- Completeness: every acceptance criterion is individually met with concrete evidence in the output. Partial completion is FAIL.",
|
|
90939
|
+
"- Conformance: the work matches what was asked — no scope drift, no over-engineering, no cutting corners.",
|
|
90940
|
+
"- Substance: the output is real, finished, working work — not just a plan, outline, scaffold, stub, mock, or partial implementation, unless the objective specifically asks for those. Surface-level appearance without end-to-end correctness is FAIL.",
|
|
90941
|
+
"When FAIL, you MUST list specific issues with actionable fix directions. Do not accept plausible-sounding but unverified claims of completion.",
|
|
90942
|
+
"Respond with JSON only."
|
|
90943
|
+
].join(" ");
|
|
90944
|
+
function buildCriteriaPrompt(objective) {
|
|
90945
|
+
return [
|
|
90946
|
+
"## Objective",
|
|
90947
|
+
objective,
|
|
90948
|
+
"",
|
|
90949
|
+
"Generate 3-8 concrete, verifiable acceptance criteria for this objective.",
|
|
90950
|
+
"Each criterion should describe a specific, testable behavior or outcome — focus on end-to-end correctness, not surface existence.",
|
|
90951
|
+
"Respond with JSON: {\"criteria\": [\"criterion 1\", \"criterion 2\", ...]}"
|
|
90952
|
+
].join("\n");
|
|
90953
|
+
}
|
|
90954
|
+
function buildGraderPrompt(objective, criteria, output) {
|
|
90955
|
+
return [
|
|
90956
|
+
"## Objective",
|
|
90957
|
+
objective,
|
|
90958
|
+
"",
|
|
90959
|
+
"## Acceptance Criteria",
|
|
90960
|
+
criteria,
|
|
90961
|
+
"",
|
|
90962
|
+
"## Agent Output",
|
|
90963
|
+
output || "(no output captured)",
|
|
90964
|
+
"",
|
|
90965
|
+
"Evaluate each dimension independently against the acceptance criteria, then decide overall PASS/FAIL.",
|
|
90966
|
+
"When FAIL, list every specific issue with an actionable fix direction so the agent knows exactly what to address next.",
|
|
90967
|
+
"Respond with JSON:",
|
|
90968
|
+
"{\"completeness\":{\"pass\":true/false,\"detail\":\"...\"},\"conformance\":{\"pass\":true/false,\"detail\":\"...\"},\"substance\":{\"pass\":true/false,\"detail\":\"...\"},\"issues\":[\"issue 1: what to fix\",\"issue 2: what to fix\"],\"pass\":true/false,\"reason\":\"overall summary\"}"
|
|
90969
|
+
].join("\n");
|
|
90970
|
+
}
|
|
90971
|
+
function parseGraderResponse(text) {
|
|
90972
|
+
try {
|
|
90973
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
90974
|
+
if (!match) return {
|
|
90975
|
+
pass: true,
|
|
90976
|
+
reason: "No JSON found in grader response",
|
|
90977
|
+
summary: ""
|
|
90978
|
+
};
|
|
90979
|
+
const parsed = JSON.parse(match[0]);
|
|
90980
|
+
const overallPass = parsed.pass === true;
|
|
90981
|
+
const overallReason = typeof parsed.reason === "string" ? parsed.reason : "No reason provided";
|
|
90982
|
+
if (![
|
|
90983
|
+
parsed.completeness,
|
|
90984
|
+
parsed.conformance,
|
|
90985
|
+
parsed.substance
|
|
90986
|
+
].some((d) => d !== void 0)) return {
|
|
90987
|
+
pass: overallPass,
|
|
90988
|
+
reason: overallReason,
|
|
90989
|
+
summary: ""
|
|
90990
|
+
};
|
|
90991
|
+
const lines = [];
|
|
90992
|
+
const failedDims = [];
|
|
90993
|
+
for (const [name, dim] of Object.entries({
|
|
90994
|
+
Completeness: parsed.completeness,
|
|
90995
|
+
Conformance: parsed.conformance,
|
|
90996
|
+
Substance: parsed.substance
|
|
90997
|
+
})) {
|
|
90998
|
+
if (dim === void 0) continue;
|
|
90999
|
+
const ok = dim.pass === true;
|
|
91000
|
+
const detail = typeof dim.detail === "string" ? dim.detail : "";
|
|
91001
|
+
lines.push(` ${ok ? "✓" : "✗"} ${name}: ${detail}`);
|
|
91002
|
+
if (!ok) failedDims.push(`${name}: ${detail}`);
|
|
91003
|
+
}
|
|
91004
|
+
const issues = Array.isArray(parsed.issues) ? parsed.issues.filter((i) => typeof i === "string") : [];
|
|
91005
|
+
if (issues.length > 0) {
|
|
91006
|
+
lines.push("");
|
|
91007
|
+
lines.push(" Issues to fix:");
|
|
91008
|
+
for (const issue of issues) lines.push(` - ${issue}`);
|
|
91009
|
+
}
|
|
91010
|
+
const summary = lines.join("\n");
|
|
91011
|
+
const reasonParts = [overallReason];
|
|
91012
|
+
if (failedDims.length > 0) reasonParts.push(failedDims.join("\n"));
|
|
91013
|
+
if (issues.length > 0) reasonParts.push(`Issues to fix:\n${issues.map((i) => `- ${i}`).join("\n")}`);
|
|
91014
|
+
return {
|
|
91015
|
+
pass: overallPass,
|
|
91016
|
+
reason: reasonParts.join("\n"),
|
|
91017
|
+
summary
|
|
91018
|
+
};
|
|
91019
|
+
} catch {
|
|
91020
|
+
return {
|
|
91021
|
+
pass: true,
|
|
91022
|
+
reason: "Failed to parse grader response",
|
|
91023
|
+
summary: ""
|
|
91024
|
+
};
|
|
91025
|
+
}
|
|
91026
|
+
}
|
|
91027
|
+
function extractResponseText(response) {
|
|
91028
|
+
return response.message.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
91029
|
+
}
|
|
91030
|
+
async function generateAcceptanceCriteria(agent, objective) {
|
|
91031
|
+
const prompt = buildCriteriaPrompt(objective);
|
|
91032
|
+
const text = extractResponseText(await agent.rawGenerate(agent.config.provider, CRITERIA_SYSTEM_PROMPT, [], [{
|
|
91033
|
+
role: "user",
|
|
91034
|
+
content: [{
|
|
91035
|
+
type: "text",
|
|
91036
|
+
text: prompt
|
|
91037
|
+
}],
|
|
91038
|
+
toolCalls: []
|
|
91039
|
+
}]));
|
|
91040
|
+
try {
|
|
91041
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
91042
|
+
if (!match) return "";
|
|
91043
|
+
const parsed = JSON.parse(match[0]);
|
|
91044
|
+
if (!Array.isArray(parsed.criteria)) return "";
|
|
91045
|
+
return parsed.criteria.filter((c) => typeof c === "string").map((c, i) => `${i + 1}. ${c}`).join("\n");
|
|
91046
|
+
} catch {
|
|
91047
|
+
return "";
|
|
91048
|
+
}
|
|
91049
|
+
}
|
|
91050
|
+
function createGoalGrader(agent) {
|
|
91051
|
+
return async (objective, criterion, output) => {
|
|
91052
|
+
let criteria;
|
|
91053
|
+
if (criterion !== void 0) criteria = criterion;
|
|
91054
|
+
else {
|
|
91055
|
+
criteria = await generateAcceptanceCriteria(agent, objective);
|
|
91056
|
+
if (!criteria) criteria = "No specific criteria defined. Evaluate based on whether the objective is clearly achieved.";
|
|
91057
|
+
}
|
|
91058
|
+
const user = buildGraderPrompt(objective, criteria, output);
|
|
91059
|
+
const result = parseGraderResponse(extractResponseText(await agent.rawGenerate(agent.config.provider, GRADER_SYSTEM_PROMPT, [], [{
|
|
91060
|
+
role: "user",
|
|
91061
|
+
content: [{
|
|
91062
|
+
type: "text",
|
|
91063
|
+
text: user
|
|
91064
|
+
}],
|
|
91065
|
+
toolCalls: []
|
|
91066
|
+
}])));
|
|
91067
|
+
const reason = result.summary ? `${result.reason}\n${result.summary}` : result.reason;
|
|
91068
|
+
return {
|
|
91069
|
+
pass: result.pass,
|
|
91070
|
+
reason
|
|
91071
|
+
};
|
|
91072
|
+
};
|
|
91073
|
+
}
|
|
90872
91074
|
var ToolManager = class {
|
|
90873
91075
|
agent;
|
|
90874
91076
|
builtinTools = /* @__PURE__ */ new Map();
|
|
@@ -91147,7 +91349,7 @@ var ToolManager = class {
|
|
|
91147
91349
|
this.agent.cron && new CronListTool(this.agent.cron),
|
|
91148
91350
|
this.agent.cron && new CronDeleteTool(this.agent.cron),
|
|
91149
91351
|
this.agent.type === "main" && new CreateGoalTool(this.agent),
|
|
91150
|
-
this.agent.type === "main" && new UpdateGoalTool(this.agent),
|
|
91352
|
+
this.agent.type === "main" && new UpdateGoalTool(this.agent, createGoalGrader(this.agent)),
|
|
91151
91353
|
this.agent.type === "main" && new GetGoalTool(this.agent),
|
|
91152
91354
|
this.agent.type === "main" && new SetGoalBudgetTool(this.agent),
|
|
91153
91355
|
this.agent.type === "main" && new WriteGoalNoteTool(this.agent),
|
|
@@ -121887,7 +122089,7 @@ function wrap(text, width, maxLines) {
|
|
|
121887
122089
|
let current = "";
|
|
121888
122090
|
for (const word of words) {
|
|
121889
122091
|
const candidate = current.length === 0 ? word : `${current} ${word}`;
|
|
121890
|
-
if (candidate
|
|
122092
|
+
if (visibleWidth(candidate) > width && current.length > 0) {
|
|
121891
122093
|
lines.push(current);
|
|
121892
122094
|
current = word;
|
|
121893
122095
|
} else current = candidate;
|
|
@@ -121973,6 +122175,9 @@ var GoalStatusMessageComponent = class {
|
|
|
121973
122175
|
};
|
|
121974
122176
|
//#endregion
|
|
121975
122177
|
//#region src/tui/commands/goal.ts
|
|
122178
|
+
const GOAL_STATUS_DISMISS_MS = 1e4;
|
|
122179
|
+
let activeGoalPanel;
|
|
122180
|
+
let activeGoalTimer;
|
|
121976
122181
|
const CONTROL_SUBCOMMANDS = new Set([
|
|
121977
122182
|
"pause",
|
|
121978
122183
|
"resume",
|
|
@@ -122122,13 +122327,28 @@ async function showGoalStatus(host) {
|
|
|
122122
122327
|
}
|
|
122123
122328
|
try {
|
|
122124
122329
|
const result = await session.getGoal();
|
|
122125
|
-
|
|
122330
|
+
dismissGoalPanel(host);
|
|
122331
|
+
const panel = new GoalStatusMessageComponent(result.goal, host.state.theme.colors);
|
|
122332
|
+
host.state.transcriptContainer.addChild(panel);
|
|
122333
|
+
activeGoalPanel = panel;
|
|
122334
|
+
activeGoalTimer = setTimeout(() => dismissGoalPanel(host), GOAL_STATUS_DISMISS_MS);
|
|
122126
122335
|
host.state.ui.requestRender();
|
|
122127
122336
|
} catch (error) {
|
|
122128
122337
|
const message = error instanceof Error ? error.message : String(error);
|
|
122129
122338
|
host.showError(`获取目标状态失败:${message}`);
|
|
122130
122339
|
}
|
|
122131
122340
|
}
|
|
122341
|
+
function dismissGoalPanel(host) {
|
|
122342
|
+
if (activeGoalTimer !== void 0) {
|
|
122343
|
+
clearTimeout(activeGoalTimer);
|
|
122344
|
+
activeGoalTimer = void 0;
|
|
122345
|
+
}
|
|
122346
|
+
if (activeGoalPanel !== void 0) {
|
|
122347
|
+
host.state.transcriptContainer.removeChild(activeGoalPanel);
|
|
122348
|
+
activeGoalPanel = void 0;
|
|
122349
|
+
host.state.ui.requestRender();
|
|
122350
|
+
}
|
|
122351
|
+
}
|
|
122132
122352
|
//#endregion
|
|
122133
122353
|
//#region src/utils/git/git-status.ts
|
|
122134
122354
|
/**
|
|
@@ -122684,10 +122904,7 @@ var FooterComponent = class {
|
|
|
122684
122904
|
if (state.permissionMode === "yolo") left.push(chalk.hex(colors.warning).bold("YES"));
|
|
122685
122905
|
if (state.planMode) left.push(chalk.hex(colors.primary).bold("plan"));
|
|
122686
122906
|
if (state.wolfpackMode) left.push(chalk.hex(colors.primary).bold("wolfpack"));
|
|
122687
|
-
if (state.goalActive
|
|
122688
|
-
const goalText = state.goal.length > 20 ? state.goal.slice(0, 20) + "…" : state.goal;
|
|
122689
|
-
left.push(chalk.hex(colors.success).bold(`🎯 ${goalText}`));
|
|
122690
|
-
}
|
|
122907
|
+
if (state.goalActive) left.push(chalk.hex(colors.primary).bold("goal"));
|
|
122691
122908
|
const model = shortenModel(modelDisplayName(state));
|
|
122692
122909
|
if (model) {
|
|
122693
122910
|
const thinkingLabel = state.thinking ? " 思考中" : "";
|
|
@@ -130460,6 +130677,7 @@ var StreamingUIController = class {
|
|
|
130460
130677
|
}
|
|
130461
130678
|
this.host.setAppState({ streamingPhase: "idle" });
|
|
130462
130679
|
this.host.resetLivePane();
|
|
130680
|
+
this.host.onTurnCompleted();
|
|
130463
130681
|
notifyTerminalOnce(state, `turn-complete:${completedTurnKey}`, {
|
|
130464
130682
|
title: "Scream Code 任务完成",
|
|
130465
130683
|
body: state.appState.sessionTitle ?? void 0
|
|
@@ -133609,6 +133827,7 @@ var SessionManager = class {
|
|
|
133609
133827
|
this.host.showError("历史回放期间无法启动新会话。");
|
|
133610
133828
|
return;
|
|
133611
133829
|
}
|
|
133830
|
+
await this.extractMemoriesBeforeSwitch();
|
|
133612
133831
|
let session;
|
|
133613
133832
|
try {
|
|
133614
133833
|
session = await this.createSessionFromCurrentState();
|
|
@@ -133666,6 +133885,20 @@ var SessionManager = class {
|
|
|
133666
133885
|
this.host.updateQueueDisplay();
|
|
133667
133886
|
this.host.stopMemoryIdleTimer();
|
|
133668
133887
|
}
|
|
133888
|
+
async extractMemoriesBeforeSwitch() {
|
|
133889
|
+
const session = this.host.session;
|
|
133890
|
+
if (session === void 0) return;
|
|
133891
|
+
if (this.host.state.appState.streamingPhase !== "idle") return;
|
|
133892
|
+
this.host.state.footer.setTransientHint("正在整理会话记忆...");
|
|
133893
|
+
this.host.state.ui.requestRender();
|
|
133894
|
+
try {
|
|
133895
|
+
await Promise.race([session.extractMemoriesOnExit(), new Promise((resolve) => setTimeout(resolve, 3e4))]);
|
|
133896
|
+
this.host.showStatus("已沉淀关键信息至记忆备忘录");
|
|
133897
|
+
} catch {} finally {
|
|
133898
|
+
this.host.state.footer.setTransientHint(null);
|
|
133899
|
+
this.host.state.ui.requestRender();
|
|
133900
|
+
}
|
|
133901
|
+
}
|
|
133669
133902
|
requireSession() {
|
|
133670
133903
|
if (this.host.session === void 0) throw new Error(NO_ACTIVE_SESSION_MESSAGE);
|
|
133671
133904
|
return this.host.session;
|
|
@@ -134297,6 +134530,7 @@ function sourceLabel(source) {
|
|
|
134297
134530
|
var MemoryPickerComponent = class extends Container {
|
|
134298
134531
|
store;
|
|
134299
134532
|
colors;
|
|
134533
|
+
ui;
|
|
134300
134534
|
onCancel;
|
|
134301
134535
|
onInject;
|
|
134302
134536
|
focused = false;
|
|
@@ -134317,11 +134551,13 @@ var MemoryPickerComponent = class extends Container {
|
|
|
134317
134551
|
this.total = opts.total;
|
|
134318
134552
|
this.loading = opts.loading;
|
|
134319
134553
|
this.colors = opts.colors;
|
|
134554
|
+
this.ui = opts.ui;
|
|
134320
134555
|
this.onCancel = opts.onCancel;
|
|
134321
134556
|
this.onInject = opts.onInject;
|
|
134322
134557
|
}
|
|
134323
134558
|
async loadMemos() {
|
|
134324
134559
|
this.loading = true;
|
|
134560
|
+
this.ui?.requestRender();
|
|
134325
134561
|
try {
|
|
134326
134562
|
const result = await this.store.list({
|
|
134327
134563
|
limit: 50,
|
|
@@ -134335,6 +134571,7 @@ var MemoryPickerComponent = class extends Container {
|
|
|
134335
134571
|
this.total = 0;
|
|
134336
134572
|
} finally {
|
|
134337
134573
|
this.loading = false;
|
|
134574
|
+
this.ui?.requestRender();
|
|
134338
134575
|
}
|
|
134339
134576
|
}
|
|
134340
134577
|
handleInput(data) {
|
|
@@ -134426,6 +134663,7 @@ var MemoryPickerComponent = class extends Container {
|
|
|
134426
134663
|
this.detailMemo = null;
|
|
134427
134664
|
}
|
|
134428
134665
|
if (this.detailMemo) this.mode = "detail";
|
|
134666
|
+
this.ui?.requestRender();
|
|
134429
134667
|
}
|
|
134430
134668
|
async deleteAndReload(id) {
|
|
134431
134669
|
try {
|
|
@@ -134434,6 +134672,7 @@ var MemoryPickerComponent = class extends Container {
|
|
|
134434
134672
|
await this.loadMemos();
|
|
134435
134673
|
this.mode = "list";
|
|
134436
134674
|
if (this.selectedIndex >= this.memos.length) this.selectedIndex = Math.max(0, this.memos.length - 1);
|
|
134675
|
+
this.ui?.requestRender();
|
|
134437
134676
|
}
|
|
134438
134677
|
render(width) {
|
|
134439
134678
|
const c = this.colors;
|
|
@@ -135449,6 +135688,7 @@ var DialogManager = class {
|
|
|
135449
135688
|
total,
|
|
135450
135689
|
loading: !hasData,
|
|
135451
135690
|
colors: this.host.state.theme.colors,
|
|
135691
|
+
ui: this.host.state.ui,
|
|
135452
135692
|
onCancel: () => {
|
|
135453
135693
|
this.host.state.activeDialog = null;
|
|
135454
135694
|
this.restoreEditor();
|
|
@@ -135819,16 +136059,25 @@ var ScreamTUI = class ScreamTUI {
|
|
|
135819
136059
|
if (this.state.appState.isReplaying) return;
|
|
135820
136060
|
const session = this.session;
|
|
135821
136061
|
if (session === void 0) return;
|
|
136062
|
+
this.state.footer.setTransientHint("正在整理会话记忆...");
|
|
136063
|
+
this.state.ui.requestRender();
|
|
135822
136064
|
try {
|
|
135823
136065
|
await session.extractMemoriesOnExit();
|
|
135824
136066
|
this.lastMemoryExtractionTime = Date.now();
|
|
135825
136067
|
this.showStatus("已沉淀关键信息至记忆备忘录");
|
|
135826
|
-
} catch {}
|
|
136068
|
+
} catch {} finally {
|
|
136069
|
+
this.state.footer.setTransientHint(null);
|
|
136070
|
+
this.state.ui.requestRender();
|
|
136071
|
+
}
|
|
135827
136072
|
}
|
|
135828
136073
|
/** Called after compaction extraction to avoid duplicate idle extraction within cooldown. */
|
|
135829
136074
|
markMemoryExtracted() {
|
|
135830
136075
|
this.lastMemoryExtractionTime = Date.now();
|
|
135831
136076
|
}
|
|
136077
|
+
/** Called by StreamingUIController when a turn finishes with no queued continuations. */
|
|
136078
|
+
onTurnCompleted() {
|
|
136079
|
+
this.startMemoryIdleTimer();
|
|
136080
|
+
}
|
|
135832
136081
|
/** Trigger an immediate cc-connect liveness poll. Called by /cc after start/stop/restart. */
|
|
135833
136082
|
refreshCcStatus() {
|
|
135834
136083
|
setTimeout(() => {
|
|
@@ -135898,6 +136147,27 @@ var ScreamTUI = class ScreamTUI {
|
|
|
135898
136147
|
const footerWrap = new GutterContainer(1, 1);
|
|
135899
136148
|
footerWrap.addChild(this.state.footer);
|
|
135900
136149
|
this.state.ui.addChild(footerWrap);
|
|
136150
|
+
this.installFixedBottomRegion();
|
|
136151
|
+
}
|
|
136152
|
+
/**
|
|
136153
|
+
* Pin the editor + footer to the terminal bottom. The pi-tui patch adds a
|
|
136154
|
+
* `fixedBottomLineCount` property: the last N rendered lines stay pinned
|
|
136155
|
+
* while the transcript above scrolls independently.
|
|
136156
|
+
*
|
|
136157
|
+
* We override `doRender` to measure the editor + footer height each frame
|
|
136158
|
+
* (they change with multi-line input or terminal resize) and set the count
|
|
136159
|
+
* before the real render runs.
|
|
136160
|
+
*/
|
|
136161
|
+
installFixedBottomRegion() {
|
|
136162
|
+
const ui = this.state.ui;
|
|
136163
|
+
const originalDoRender = ui.doRender.bind(ui);
|
|
136164
|
+
const editorContainer = this.state.editorContainer;
|
|
136165
|
+
const footerWrap = ui.children.at(-1);
|
|
136166
|
+
ui.doRender = () => {
|
|
136167
|
+
const w = ui.terminal?.columns ?? 0;
|
|
136168
|
+
if (w > 0) ui.fixedBottomLineCount = editorContainer.render(w).length + (footerWrap ? footerWrap.render(w).length : 0);
|
|
136169
|
+
originalDoRender();
|
|
136170
|
+
};
|
|
135901
136171
|
}
|
|
135902
136172
|
handlePlanToggle(next) {
|
|
135903
136173
|
handlePlanCommand(this, next ? "on" : "off");
|
|
@@ -135910,7 +136180,7 @@ var ScreamTUI = class ScreamTUI {
|
|
|
135910
136180
|
}
|
|
135911
136181
|
this.persistInputHistory(text);
|
|
135912
136182
|
dispatchInput(this, text);
|
|
135913
|
-
this.
|
|
136183
|
+
this.stopMemoryIdleTimer();
|
|
135914
136184
|
}
|
|
135915
136185
|
sendNormalUserInput(text) {
|
|
135916
136186
|
if (this.state.appState.model.trim().length === 0) {
|
|
@@ -136563,7 +136833,7 @@ const SHADOW_CHARS = new Set([
|
|
|
136563
136833
|
]);
|
|
136564
136834
|
const SHEEN_STEP = 2;
|
|
136565
136835
|
const SHEEN_INTERVAL_MS = 150;
|
|
136566
|
-
const LOADING_DURATION_MS =
|
|
136836
|
+
const LOADING_DURATION_MS = 1500;
|
|
136567
136837
|
const THEME_ACCENT = {
|
|
136568
136838
|
dark: [
|
|
136569
136839
|
78,
|