scream-code 0.11.2 → 0.11.3

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.
@@ -76030,6 +76030,52 @@ function canSplitAfterContext(messages, index) {
76030
76030
  /** Max consecutive compaction failures before auto-compaction is
76031
76031
  * disabled for the remainder of the turn. Resets each turn. */
76032
76032
  const MAX_CONSECUTIVE_FAILURES = 3;
76033
+ /** Truncate tool call arguments to a brief summary for the compaction history. */
76034
+ function truncateArgsForSummary(args) {
76035
+ if (args === null || args === void 0) return "";
76036
+ try {
76037
+ const parsed = JSON.parse(args);
76038
+ if (typeof parsed !== "object" || parsed === null) return "";
76039
+ const entries = Object.entries(parsed);
76040
+ const parts = [];
76041
+ for (const [key, value] of entries) {
76042
+ const valStr = typeof value === "string" ? value.length > 50 ? `"${value.slice(0, 50)}…"` : `"${value}"` : String(value);
76043
+ parts.push(`${key}=${valStr}`);
76044
+ }
76045
+ return parts.slice(0, 3).join(", ");
76046
+ } catch {
76047
+ return args.slice(0, 80);
76048
+ }
76049
+ }
76050
+ /** Extract a brief tool-call history from compacted messages so the model
76051
+ * knows what was already tried after compaction. Prevents repeating
76052
+ * failed approaches. */
76053
+ function formatToolCallHistory(messages) {
76054
+ const resultStatus = /* @__PURE__ */ new Map();
76055
+ for (const msg of messages) if (msg.role === "tool" && msg.toolCallId !== void 0) resultStatus.set(msg.toolCallId, msg.isError === true);
76056
+ const entries = [];
76057
+ for (const msg of messages) {
76058
+ if (msg.role !== "assistant" || msg.toolCalls.length === 0) continue;
76059
+ for (const tc of msg.toolCalls) {
76060
+ const isError = resultStatus.get(tc.id) ?? false;
76061
+ const argsSummary = truncateArgsForSummary(tc.arguments);
76062
+ entries.push({
76063
+ name: tc.name,
76064
+ argsSummary,
76065
+ isError
76066
+ });
76067
+ }
76068
+ }
76069
+ if (entries.length === 0) return "";
76070
+ return [
76071
+ "## Recent Tool Calls",
76072
+ "",
76073
+ ...entries.slice(-15).map((e) => {
76074
+ const status = e.isError ? "error" : "success";
76075
+ return `- ${e.name}(${e.argsSummary}) -> ${status}`;
76076
+ })
76077
+ ].join("\n");
76078
+ }
76033
76079
  /** Minimal system prompt used during compaction. The full agent system
76034
76080
  * prompt contains tool descriptions and runtime injections that contradict
76035
76081
  * the compaction instruction ("DO NOT CALL ANY TOOLS"). This compact prompt
@@ -76284,7 +76330,8 @@ var FullCompaction = class {
76284
76330
  const messagesToCompactForOps = originalHistory.slice(0, compactedCount);
76285
76331
  const fileOps = createFileOps();
76286
76332
  for (const msg of messagesToCompactForOps) extractFileOpsFromMessage(msg, fileOps);
76287
- const processedSummary = this.postProcessSummary(summary, fileOps);
76333
+ const toolCallHistory = formatToolCallHistory(messagesToCompactForOps);
76334
+ const processedSummary = this.postProcessSummary(summary, fileOps, toolCallHistory);
76288
76335
  const tokensAfter = estimateTokens$1(processedSummary) + estimateTokensForMessages(recent);
76289
76336
  const result = {
76290
76337
  summary: processedSummary,
@@ -76397,7 +76444,7 @@ var FullCompaction = class {
76397
76444
  * compression. Without this, both are lost after compaction because the
76398
76445
  * original messages containing them are removed from the context window.
76399
76446
  */
76400
- postProcessSummary(summary, fileOps) {
76447
+ postProcessSummary(summary, fileOps, toolCallHistory) {
76401
76448
  const todos = this.agent.tools.storeData()["todo"] ?? [];
76402
76449
  const sections = [summary.trim()];
76403
76450
  if (todos.length > 0) {
@@ -76412,6 +76459,7 @@ var FullCompaction = class {
76412
76459
  }
76413
76460
  const filesSection = formatFileOperations(fileOps);
76414
76461
  if (filesSection.length > 0) sections.push(filesSection);
76462
+ if (toolCallHistory.length > 0) sections.push(toolCallHistory);
76415
76463
  return sections.join("\n\n");
76416
76464
  }
76417
76465
  };
@@ -76518,7 +76566,7 @@ const DEFAULT_CONFIG = {
76518
76566
  keepRecentTokens: 4e4,
76519
76567
  pruneMinReclaimTokens: 2e4,
76520
76568
  minContentTokens: 100,
76521
- minContextUsageRatio: .5,
76569
+ minContextUsageRatio: .3,
76522
76570
  truncatedMarker: "[Old tool result content cleared]",
76523
76571
  uselessMarker: "[Uneventful result elided]",
76524
76572
  noMatchesMarker: "[no matches]"
@@ -77865,8 +77913,15 @@ const TOOL_OUTPUT_EMPTY_TEXT = "Tool output is empty.";
77865
77913
  /** Maximum token count for tool results persisted in conversation history.
77866
77914
  * Results exceeding this limit are truncated to avoid bloating every
77867
77915
  * subsequent API request with stale data. The model can re-read the
77868
- * full content via read_file when needed. */
77869
- const MAX_TOOL_RESULT_TOKENS = 8e3;
77916
+ * full content via read_file when needed.
77917
+ *
77918
+ * Lowered from 8000 to 5000 to reduce input-context pressure on long
77919
+ * sessions. When input grows large, the available output space
77920
+ * (context_window - input_tokens) shrinks, causing the model to hit
77921
+ * max_tokens before emitting a tool call. Smaller tool-result footprints
77922
+ * leave more room for output. The current turn always sees the full
77923
+ * result via streaming; only the history copy is truncated. */
77924
+ const MAX_TOOL_RESULT_TOKENS = 5e3;
77870
77925
  const TOOL_TRUNCATION_NOTICE = "\n[content truncated — use read_file to re-read if needed]";
77871
77926
  var ContextMemory = class {
77872
77927
  agent;
@@ -96038,10 +96093,25 @@ function parseEnvBudget(raw) {
96038
96093
  }
96039
96094
  /**
96040
96095
  * Compute the effective `max_completion_tokens` cap.
96041
- */
96096
+ *
96097
+ * Aligned with oh-my-pi's approach: use a reasonable output-token cap
96098
+ * instead of the full context window. `max_context_tokens` is the total
96099
+ * (input + output) budget, not the output-only budget. Using it as
96100
+ * max_tokens tells the API "generate up to 128K output tokens", which is
96101
+ * incorrect - the actual max output is much smaller. The API server
96102
+ * clamps it to context_window - input_tokens, which shrinks as context
96103
+ * grows, causing "max_tokens limit - no tool call" errors on long
96104
+ * sessions.
96105
+ *
96106
+ * The 64K cap matches oh-my-pi's OUTPUT_CAP_WHEN_UNKNOWN and is well
96107
+ * above any current model's real output limit (typically 8K-32K), so it
96108
+ * never artificially limits output while preventing the context-window
96109
+ * value from being used directly.
96110
+ */
96111
+ const OUTPUT_TOKEN_CAP = 64e3;
96042
96112
  function computeCompletionBudgetCap(args) {
96043
96113
  const maxCtx = args.capability?.max_context_tokens ?? 0;
96044
- const cap = args.budget.hardCap ?? (maxCtx > 0 ? maxCtx : args.budget.fallback ?? DEFAULT_UNKNOWN_CONTEXT_FALLBACK);
96114
+ const cap = args.budget.hardCap ?? (maxCtx > 0 ? Math.min(maxCtx, OUTPUT_TOKEN_CAP) : args.budget.fallback ?? DEFAULT_UNKNOWN_CONTEXT_FALLBACK);
96045
96115
  return Math.max(MIN_FLOOR, cap);
96046
96116
  }
96047
96117
  /**
@@ -120218,7 +120288,7 @@ function optionalBuildString(value) {
120218
120288
  return typeof value === "string" && value.length > 0 ? value : void 0;
120219
120289
  }
120220
120290
  const SCREAM_BUILD_INFO = {
120221
- version: optionalBuildString("0.11.2"),
120291
+ version: optionalBuildString("0.11.3"),
120222
120292
  channel: optionalBuildString(""),
120223
120293
  commit: optionalBuildString(""),
120224
120294
  buildTarget: optionalBuildString("darwin-arm64")
@@ -125398,13 +125468,17 @@ function findProjectRoot(startDir) {
125398
125468
  let dir = resolve(startDir);
125399
125469
  let markerDir;
125400
125470
  while (true) {
125401
- if (existsSync(resolve(dir, ".git"))) return dir;
125471
+ const hasGit = existsSync(resolve(dir, ".git"));
125402
125472
  if (markerDir === void 0) {
125403
125473
  for (const marker of PROJECT_ROOT_MARKERS) if (existsSync(resolve(dir, marker))) {
125404
125474
  markerDir = dir;
125405
125475
  break;
125406
125476
  }
125407
125477
  }
125478
+ if (hasGit) {
125479
+ if (markerDir === void 0 || markerDir === dir) return dir;
125480
+ return PROJECT_ROOT_MARKERS.some((m) => existsSync(resolve(dir, m))) ? dir : markerDir;
125481
+ }
125408
125482
  const parent = dirname$1(dir);
125409
125483
  if (parent === dir) break;
125410
125484
  dir = parent;
@@ -126958,7 +127032,7 @@ var ThinkingComponent = class {
126958
127032
  this.stopSpinner();
126959
127033
  }
126960
127034
  dispose() {
126961
- this.stopSpinner();
127035
+ this.finalize();
126962
127036
  }
126963
127037
  setExpanded(expanded) {
126964
127038
  if (this.expanded === expanded) return;
@@ -148086,7 +148160,11 @@ async function runWebServer(opts) {
148086
148160
  }
148087
148161
  ws.send(JSON.stringify({ type: "server_empty" }));
148088
148162
  });
148089
- await new Promise((resolve) => {
148163
+ await new Promise((resolve, reject) => {
148164
+ httpServer.once("error", (err) => {
148165
+ if (err.code === "EADDRINUSE") reject(/* @__PURE__ */ new Error(`端口 ${opts.port} 已被占用,请先关闭占用该端口的进程,或使用 --port <port> 指定其他端口。`));
148166
+ else reject(err);
148167
+ });
148090
148168
  httpServer.listen(opts.port, "127.0.0.1", resolve);
148091
148169
  });
148092
148170
  const url = `http://localhost:${opts.port}`;
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
7
  //#region src/main.ts
8
8
  try {
9
- (await import("./app-8_tNuOds.mjs")).main();
9
+ (await import("./app-D6B25mE5.mjs")).main();
10
10
  } catch (error) {
11
11
  process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
12
12
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",