scream-code 0.5.1 → 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.
Files changed (2) hide show
  1. package/dist/main.mjs +255 -20
  2. package/package.json +2 -2
package/dist/main.mjs CHANGED
@@ -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
@@ -75991,6 +76038,8 @@ function buildGoalReminder(goal) {
75991
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.");
75992
76039
  lines.push("");
75993
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.");
75994
76043
  return lines.join("\n");
75995
76044
  }
75996
76045
  function maxBudgetFraction(goal) {
@@ -76488,7 +76537,12 @@ const DEFAULT_APPROVE_TOOLS = new Set([
76488
76537
  "Agent",
76489
76538
  "AskUserQuestion",
76490
76539
  "Skill",
76491
- "WolfPack"
76540
+ "WolfPack",
76541
+ "CreateGoal",
76542
+ "UpdateGoal",
76543
+ "GetGoal",
76544
+ "SetGoalBudget",
76545
+ "WriteGoalNote"
76492
76546
  ]);
76493
76547
  var DefaultToolApprovePermissionPolicy = class {
76494
76548
  name = "default-tool-approve";
@@ -90872,6 +90926,151 @@ const DEFAULT_AGENT_PROFILES = loadAgentProfilesFromSources([
90872
90926
  ].map((file) => `profile/default/${file}`), PROFILE_SOURCES);
90873
90927
  //#endregion
90874
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
+ }
90875
91074
  var ToolManager = class {
90876
91075
  agent;
90877
91076
  builtinTools = /* @__PURE__ */ new Map();
@@ -91150,7 +91349,7 @@ var ToolManager = class {
91150
91349
  this.agent.cron && new CronListTool(this.agent.cron),
91151
91350
  this.agent.cron && new CronDeleteTool(this.agent.cron),
91152
91351
  this.agent.type === "main" && new CreateGoalTool(this.agent),
91153
- this.agent.type === "main" && new UpdateGoalTool(this.agent),
91352
+ this.agent.type === "main" && new UpdateGoalTool(this.agent, createGoalGrader(this.agent)),
91154
91353
  this.agent.type === "main" && new GetGoalTool(this.agent),
91155
91354
  this.agent.type === "main" && new SetGoalBudgetTool(this.agent),
91156
91355
  this.agent.type === "main" && new WriteGoalNoteTool(this.agent),
@@ -121976,6 +122175,9 @@ var GoalStatusMessageComponent = class {
121976
122175
  };
121977
122176
  //#endregion
121978
122177
  //#region src/tui/commands/goal.ts
122178
+ const GOAL_STATUS_DISMISS_MS = 1e4;
122179
+ let activeGoalPanel;
122180
+ let activeGoalTimer;
121979
122181
  const CONTROL_SUBCOMMANDS = new Set([
121980
122182
  "pause",
121981
122183
  "resume",
@@ -122125,13 +122327,28 @@ async function showGoalStatus(host) {
122125
122327
  }
122126
122328
  try {
122127
122329
  const result = await session.getGoal();
122128
- host.state.transcriptContainer.addChild(new GoalStatusMessageComponent(result.goal, host.state.theme.colors));
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);
122129
122335
  host.state.ui.requestRender();
122130
122336
  } catch (error) {
122131
122337
  const message = error instanceof Error ? error.message : String(error);
122132
122338
  host.showError(`获取目标状态失败:${message}`);
122133
122339
  }
122134
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
+ }
122135
122352
  //#endregion
122136
122353
  //#region src/utils/git/git-status.ts
122137
122354
  /**
@@ -122687,10 +122904,7 @@ var FooterComponent = class {
122687
122904
  if (state.permissionMode === "yolo") left.push(chalk.hex(colors.warning).bold("YES"));
122688
122905
  if (state.planMode) left.push(chalk.hex(colors.primary).bold("plan"));
122689
122906
  if (state.wolfpackMode) left.push(chalk.hex(colors.primary).bold("wolfpack"));
122690
- if (state.goalActive && state.goal) {
122691
- const goalText = state.goal.length > 20 ? state.goal.slice(0, 20) + "…" : state.goal;
122692
- left.push(chalk.hex(colors.success).bold(`🎯 ${goalText}`));
122693
- }
122907
+ if (state.goalActive) left.push(chalk.hex(colors.primary).bold("goal"));
122694
122908
  const model = shortenModel(modelDisplayName(state));
122695
122909
  if (model) {
122696
122910
  const thinkingLabel = state.thinking ? " 思考中" : "";
@@ -135933,6 +136147,27 @@ var ScreamTUI = class ScreamTUI {
135933
136147
  const footerWrap = new GutterContainer(1, 1);
135934
136148
  footerWrap.addChild(this.state.footer);
135935
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
+ };
135936
136171
  }
135937
136172
  handlePlanToggle(next) {
135938
136173
  handlePlanCommand(this, next ? "on" : "off");
@@ -136598,7 +136833,7 @@ const SHADOW_CHARS = new Set([
136598
136833
  ]);
136599
136834
  const SHEEN_STEP = 2;
136600
136835
  const SHEEN_INTERVAL_MS = 150;
136601
- const LOADING_DURATION_MS = 2200;
136836
+ const LOADING_DURATION_MS = 1500;
136602
136837
  const THEME_ACCENT = {
136603
136838
  dark: [
136604
136839
  78,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "The Starting Point for Next-Gen Agents",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "publishConfig": {
42
42
  "access": "public",
43
- "provenance": false
43
+ "provenance": true
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsdown",