scream-code 0.16.7 → 0.16.8

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.
@@ -4,8 +4,9 @@ import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
- import { $n as normalizeWorkDir, $t as lerpGradient, A as highlightLines, An as getKnowledgeStore, At as isTurnElapsedEnabled, B as ActivityGroupComponent, Bn as getDataDir, Bt as ENABLE_TERMINAL_THEME_REPORTING, Cn as getNoActiveSessionMessage, Ct as clearInfoPanelState, D as ToolCallComponent, Dn as isExperimentalFlagEnabled, Dt as createEditorTheme, E as UserMessageComponent, En as sortSlashCommands, Et as resolveThemeSync, F as StatusMessageComponent, Fn as TuiConfigParseError, Ft as detectTerminalTheme, G as AgentGroupComponent, Gn as CLI_UI_MODE, Gt as QUERY_TERMINAL_THEME, H as estimateTokens, Hn as getLogDir, Ht as OSC11_RESPONSE, I as replaceTabs, In as TuiLikePreferencesSchema, It as parseOsc11BackgroundTheme, J as getBreathingFrame, Jn as DEFAULT_CATALOG_URL, Jt as TERMINAL_THEME_DARK, K as WelcomeComponent, Kn as CLI_USER_AGENT_PRODUCT, Kt as TERMINAL_FOCUS_IN, L as sanitizeShellOutput, Ln as loadTuiConfig, Lt as DISABLE_TERMINAL_FOCUS_REPORTING, M as CachedContainer, Mn as startManualEmbeddingDownload, Mt as createThemeStyles, N as ThinkingComponent, Nn as PIXEL_PULSE_FRAMES, Nt as contrastTextHex, O as renderDiffLines, On as setExperimentalFlags, Ot as createMarkdownTheme, P as NoticeMessageComponent, Pn as PULSE_WAVE_FRAMES, Pt as getColorPalette, Q as refineGoal, Qn as encodeWorkDirKey, Qt as FooterComponent, R as SkillActivationComponent, Rn as saveTuiConfig, Rt as DISABLE_TERMINAL_THEME_REPORTING, S as readJsonlFile, Sn as getLlmNotSetMessage, St as supportsBalance, Tn as BUILTIN_SLASH_COMMANDS, U as getSharedSpeedTracker, Un as detectInstallSource, Ut as OSC11_RESPONSE_PREFIX, V as charsForTokenBudget, Vn as getInputHistoryFile, Vt as OSC11_QUERY, W as BackgroundAgentStatusComponent, Wn as CLI_COMMAND_NAME, Wt as OSC11_RESPONSE_PREFIX_NO_ESC, X as CompactionComponent, Xn as saveCatalogCache, Xt as isBusy, Y as resetBreathingClock, Yn as fetchCatalog, Yt as TERMINAL_THEME_LIGHT, Z as clearGoalState, Zn as ScreamHarness, Zt as isStreaming, _n as MAIN_AGENT_ID, a as syncDiagramCapabilityPrompt, an as printableChar, ar as MemoryMemoStore, at as toTerminalHyperlink, b as readUpdateCache, bn as getCtrlCHint, c as disposeChildren, cn as argsRecord, cr as resolveGlobalLogPath, d as isPlanExpandable, dn as parseStreamingArgs, dr as ErrorCodes, en as SubagentSlots, er as appendSessionIndexEntry, f as MoonLoader, fn as serializeToolResultOutput, fr as SCREAM_ERROR_INFO, ft as handleFusionPlanCommand, gn as EXIT_CONFIRM_WINDOW_MS, hn as EMPTY_SESSION_HINT_URL, i as buildRoleAdditionalText, ir as resolveScreamHome, it as isInternalMessage, j as langFromPath, jn as hasEmbeddingModelCache, jt as toggleEmptySessionHint, k as renderDiffLinesClustered, kn as embeddingFailureDetail, kt as isEmptySessionHintDismissed, l as hasDispose, ln as formatErrorMessage, lr as isScreamError, mn as truncateErrorMessage, mt as handlePlanCommand, n as clearEvalPanelState, nr as removeSessionIndexEntry, on as STATUS_BULLET, or as flushDiagnosticLogs, ot as changeThinkingLevel, p as formatMemoryMemoForInjection, pn as stringValue, q as BREATHE_CYCLE_MS, qn as PRODUCT_NAME, qt as TERMINAL_FOCUS_OUT, r as openUrl, sn as appendStreamingArgsPreview, sr as log, st as getModelCycleLevel, t as dispatchInput, tr as readSessionIndex, u as isExpandable, un as isTodoItemShape, ur as isOrphanedToolCallError, v as selectUpdateTarget, vn as SESSION_TIPS, w as getDaemonInstructions, wn as buildSkillSlashCommands, x as appendJsonlLine, xn as getCtrlDHint, xt as refreshProviderBalance, y as refreshUpdateCache, yn as TIP_ROTATION_INTERVAL_MS, z as AssistantMessageComponent, zn as detectShellEnvironment, zt as ENABLE_TERMINAL_FOCUS_REPORTING } from "./dispatch-Vw9ccFm7.mjs";
8
- import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-D9ByqSsK.mjs";
7
+ import { k as MemoryMemoStore } from "./src-CXmZg1VT.mjs";
8
+ import { $n as encodeWorkDirKey, $t as lerpGradient, A as highlightLines, An as embeddingFailureDetail, At as isTurnElapsedEnabled, B as ActivityGroupComponent, Bn as detectShellEnvironment, Bt as ENABLE_TERMINAL_THEME_REPORTING, Cn as getLlmNotSetMessage, Ct as clearInfoPanelState, D as ToolCallComponent, Dn as sortSlashCommands, Dt as createEditorTheme, E as UserMessageComponent, En as BUILTIN_SLASH_COMMANDS, Et as resolveThemeSync, F as StatusMessageComponent, Fn as PULSE_WAVE_FRAMES, Ft as detectTerminalTheme, G as AgentGroupComponent, Gn as CLI_COMMAND_NAME, Gt as QUERY_TERMINAL_THEME, H as estimateTokens, Hn as getInputHistoryFile, Ht as OSC11_RESPONSE, I as replaceTabs, In as TuiConfigParseError, It as parseOsc11BackgroundTheme, J as getBreathingFrame, Jn as PRODUCT_NAME, Jt as TERMINAL_THEME_DARK, K as WelcomeComponent, Kn as CLI_UI_MODE, Kt as TERMINAL_FOCUS_IN, L as sanitizeShellOutput, Ln as TuiLikePreferencesSchema, Lt as DISABLE_TERMINAL_FOCUS_REPORTING, M as CachedContainer, Mn as hasEmbeddingModelCache, Mt as createThemeStyles, N as ThinkingComponent, Nn as startManualEmbeddingDownload, Nt as contrastTextHex, O as renderDiffLines, On as isExperimentalFlagEnabled, Ot as createMarkdownTheme, P as NoticeMessageComponent, Pn as PIXEL_PULSE_FRAMES, Pt as getColorPalette, Q as refineGoal, Qn as ScreamHarness, Qt as FooterComponent, R as SkillActivationComponent, Rn as loadTuiConfig, Rt as DISABLE_TERMINAL_THEME_REPORTING, S as readJsonlFile, Sn as getCtrlDHint, St as supportsBalance, Tn as buildSkillSlashCommands, U as getSharedSpeedTracker, Un as getLogDir, Ut as OSC11_RESPONSE_PREFIX, V as charsForTokenBudget, Vn as getDataDir, Vt as OSC11_QUERY, W as BackgroundAgentStatusComponent, Wn as detectInstallSource, Wt as OSC11_RESPONSE_PREFIX_NO_ESC, X as CompactionComponent, Xn as fetchCatalog, Xt as isBusy, Y as resetBreathingClock, Yn as DEFAULT_CATALOG_URL, Yt as TERMINAL_THEME_LIGHT, Z as clearGoalState, Zn as saveCatalogCache, Zt as isStreaming, _n as EXIT_CONFIRM_WINDOW_MS, a as syncDiagramCapabilityPrompt, an as printableChar, ar as resolveScreamHome, at as toTerminalHyperlink, b as readUpdateCache, bn as TIP_ROTATION_INTERVAL_MS, c as disposeChildren, cn as appendStreamingArgsPreview, cr as resolveGlobalLogPath, d as isPlanExpandable, dn as isTodoItemShape, dr as ErrorCodes, en as SubagentSlots, er as normalizeWorkDir, f as MoonLoader, fn as parseStreamingArgs, fr as SCREAM_ERROR_INFO, ft as handleFusionPlanCommand, gn as EMPTY_SESSION_HINT_URL, hn as truncateErrorMessage, i as buildRoleAdditionalText, it as isInternalMessage, j as langFromPath, jn as getKnowledgeStore, jt as toggleEmptySessionHint, k as renderDiffLinesClustered, kn as setExperimentalFlags, kt as isEmptySessionHintDismissed, l as hasDispose, ln as argsRecord, lr as isScreamError, mn as stringValue, mt as handlePlanCommand, n as clearEvalPanelState, nr as readSessionIndex, on as STATUS_BULLET, or as flushDiagnosticLogs, ot as changeThinkingLevel, p as formatMemoryMemoForInjection, pn as serializeToolResultOutput, q as BREATHE_CYCLE_MS, qn as CLI_USER_AGENT_PRODUCT, qt as TERMINAL_FOCUS_OUT, r as openUrl, rr as removeSessionIndexEntry, sn as appendSessionHeaderHint, sr as log, st as getModelCycleLevel, t as dispatchInput, tr as appendSessionIndexEntry, u as isExpandable, un as formatErrorMessage, ur as isOrphanedToolCallError, v as selectUpdateTarget, vn as MAIN_AGENT_ID, w as getDaemonInstructions, wn as getNoActiveSessionMessage, x as appendJsonlLine, xn as getCtrlCHint, xt as refreshProviderBalance, y as refreshUpdateCache, yn as SESSION_TIPS, z as AssistantMessageComponent, zn as saveTuiConfig, zt as ENABLE_TERMINAL_FOCUS_REPORTING } from "./dispatch-CNitkOqX.mjs";
9
+ import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-6v2667rm.mjs";
9
10
  import { createRequire } from "node:module";
10
11
  import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
11
12
  import { access, appendFile, chmod, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
@@ -32,7 +33,7 @@ function optionalBuildString(value) {
32
33
  return typeof value === "string" && value.length > 0 ? value : void 0;
33
34
  }
34
35
  const SCREAM_BUILD_INFO = {
35
- version: optionalBuildString("0.16.7"),
36
+ version: optionalBuildString("0.16.8"),
36
37
  channel: optionalBuildString(""),
37
38
  commit: optionalBuildString(""),
38
39
  buildTarget: optionalBuildString("darwin-arm64")
@@ -1877,7 +1878,7 @@ var EditorKeyboardController = class {
1877
1878
  if (media === null) return;
1878
1879
  const meta = parseImageMeta(media.bytes);
1879
1880
  if (meta === null) return;
1880
- const attachment = this.imageStore.addImage(media.bytes, meta.mime, meta.width, meta.height);
1881
+ const attachment = this.imageStore.addImage(media.bytes, meta.mime, meta.width, meta.height, path);
1881
1882
  this.host.state.editor.insertTextAtCursor?.(`${attachment.placeholder} `);
1882
1883
  this.host.state.ui.requestRender();
1883
1884
  }
@@ -3164,7 +3165,8 @@ var SessionEventHandler = class {
3164
3165
  this.host.streamingUI.resetToolUi();
3165
3166
  this.host.streamingUI.finalizeLiveTextBuffers();
3166
3167
  this.host.streamingUI.endActivityGroup();
3167
- this.host.showError(`[${event.code}] ${event.message}`);
3168
+ const shown = appendSessionHeaderHint(`[${event.code}] ${event.message}`, (name) => t("error.session_header_hint", { name }));
3169
+ this.host.showError(shown);
3168
3170
  if (event.code === "turn.agent_busy" && !hadActiveTurn && this.host.state.appState.streamingPhase !== "idle") {
3169
3171
  this.host.setAppState({ streamingPhase: "idle" });
3170
3172
  this.host.resetLivePane();
@@ -4621,6 +4623,7 @@ var StreamingUIController = class StreamingUIController {
4621
4623
  completeToolResult(toolCallId, result) {
4622
4624
  const matchedCall = this._activeToolCalls.get(toolCallId);
4623
4625
  if (matchedCall !== void 0) this.onToolCallEnd(toolCallId, result);
4626
+ else if (this._pendingToolComponents.has(toolCallId)) this.onToolCallEnd(toolCallId, result);
4624
4627
  this._activeToolCalls.delete(toolCallId);
4625
4628
  this._streamingToolCallArguments.delete(toolCallId);
4626
4629
  return matchedCall;
@@ -6246,10 +6249,28 @@ function cronDetail(data) {
6246
6249
  }
6247
6250
  //#endregion
6248
6251
  //#region src/tui/utils/image-attachment-store.ts
6252
+ /**
6253
+ * Registry for media pasted into the input box.
6254
+ *
6255
+ * Each paste produces an `ImageAttachment` with an auto-incrementing id
6256
+ * or `VideoAttachment` with a human-readable placeholder (`[image #1
6257
+ * (640×480)]` / `[video #2 sample.mov]`). The placeholder is what the
6258
+ * user sees in the input field; on submit, `extractMediaAttachments`
6259
+ * walks the text and expands image placeholders to image content parts
6260
+ * and video placeholders to file-path tags for `ReadMediaFile`.
6261
+ *
6262
+ * Scope is per-`ScreamTUI` instance. Reloads (`/new`, `/clear`,
6263
+ * session switch) call `clear()` so ids restart from 1 and stale
6264
+ * prompt attachments are dropped. We intentionally do NOT persist
6265
+ * attachments across sessions — coding-agent doesn't either, and
6266
+ * `--resume` wouldn't know how to materialize the files anyway.
6267
+ */
6268
+ /** Temp-file basename prefix used by path-mode image materialization. */
6269
+ const TEMP_ATTACHMENT_PREFIX = "scream-attachment-";
6249
6270
  var ImageAttachmentStore = class {
6250
6271
  nextId = 1;
6251
6272
  byId = /* @__PURE__ */ new Map();
6252
- addImage(bytes, mime, width, height) {
6273
+ addImage(bytes, mime, width, height, sourcePath) {
6253
6274
  const id = this.nextId;
6254
6275
  this.nextId += 1;
6255
6276
  const attachment = {
@@ -6259,7 +6280,8 @@ var ImageAttachmentStore = class {
6259
6280
  mime,
6260
6281
  width,
6261
6282
  height,
6262
- placeholder: formatPlaceholder(id, width, height)
6283
+ placeholder: formatPlaceholder(id, width, height),
6284
+ ...sourcePath !== void 0 && sourcePath.length > 0 ? { sourcePath } : {}
6263
6285
  };
6264
6286
  this.byId.set(id, attachment);
6265
6287
  return attachment;
@@ -6285,6 +6307,14 @@ var ImageAttachmentStore = class {
6285
6307
  return this.byId.get(id);
6286
6308
  }
6287
6309
  clear() {
6310
+ const tempBase = join(tmpdir(), TEMP_ATTACHMENT_PREFIX);
6311
+ for (const attachment of this.byId.values()) {
6312
+ if (attachment.kind !== "image") continue;
6313
+ const path = attachment.sourcePath;
6314
+ if (path !== void 0 && path.startsWith(tempBase)) try {
6315
+ unlinkSync(path);
6316
+ } catch {}
6317
+ }
6288
6318
  this.byId.clear();
6289
6319
  this.nextId = 1;
6290
6320
  }
@@ -8688,8 +8718,25 @@ var QueuePaneComponent = class extends Container {
8688
8718
  };
8689
8719
  //#endregion
8690
8720
  //#region src/tui/utils/image-placeholder.ts
8721
+ /**
8722
+ * Scan submitted text for media placeholders and produce
8723
+ * the `PromptPart[]` we'll send to the SDK prompt endpoint.
8724
+ *
8725
+ * Rules:
8726
+ * - Only placeholders that resolve against `store` get extracted.
8727
+ * A literal `[image #999 ...]` the user typed themselves stays in
8728
+ * the text (we can't hallucinate files for it).
8729
+ * - Order is preserved for text/image/video segments. Image placeholders
8730
+ * expand to image content parts so the prompt reaches the provider
8731
+ * without relying on a model tool call. Video placeholders still expand
8732
+ * to file-path tags so `ReadMediaFile` can own video upload behavior.
8733
+ * - Adjacent text segments are flattened — empty / whitespace-only
8734
+ * segments drop out so we never emit `{type:'text', text:' '}`
8735
+ * noise between two media parts.
8736
+ */
8691
8737
  const PLACEHOLDER_REGEX = /\[(image|video) #(\d+) (?:(\(\d+×\d+\))|([^\]]+))\]/g;
8692
- function extractMediaAttachments(text, store) {
8738
+ function extractMediaAttachments(text, store, options = {}) {
8739
+ const imageMode = options.imageMode ?? "inline";
8693
8740
  const parts = [];
8694
8741
  const imageAttachmentIds = [];
8695
8742
  const videoAttachmentIds = [];
@@ -8709,6 +8756,13 @@ function extractMediaAttachments(text, store) {
8709
8756
  if (attachment.kind === "video") {
8710
8757
  pushText(parts, tagTextForVideo(attachment));
8711
8758
  videoAttachmentIds.push(id);
8759
+ } else if (imageMode === "path") {
8760
+ try {
8761
+ pushText(parts, formatMediaTag("image", ensureImagePath(attachment)));
8762
+ } catch {
8763
+ parts.push(imagePartForAttachment(attachment));
8764
+ }
8765
+ imageAttachmentIds.push(id);
8712
8766
  } else {
8713
8767
  parts.push(imagePartForAttachment(attachment));
8714
8768
  imageAttachmentIds.push(id);
@@ -8747,6 +8801,15 @@ function imagePartForAttachment(att) {
8747
8801
  imageUrl: { url: `data:${att.mime};base64,${base64}` }
8748
8802
  };
8749
8803
  }
8804
+ /** Persist bytes to a temp file when path delivery needs a disk path. */
8805
+ function ensureImagePath(att) {
8806
+ if (att.sourcePath !== void 0 && att.sourcePath.length > 0) return att.sourcePath;
8807
+ const ext = att.mime === "image/jpeg" ? "jpg" : att.mime.split("/")[1]?.replace("+xml", "").replace("+json", "") ?? "png";
8808
+ const path = join(tmpdir(), `${TEMP_ATTACHMENT_PREFIX}${randomUUID()}.${ext}`);
8809
+ writeFileSync(path, att.bytes);
8810
+ att.sourcePath = path;
8811
+ return path;
8812
+ }
8750
8813
  function tagTextForVideo(att) {
8751
8814
  return formatMediaTag("video", att.sourcePath);
8752
8815
  }
@@ -8914,7 +8977,8 @@ var InputController = class InputController {
8914
8977
  this.dispatchUserInput(text, session);
8915
8978
  }
8916
8979
  dispatchUserInput(text, session) {
8917
- const extraction = extractMediaAttachments(text, this.host.imageStore);
8980
+ const canInlineImages = this.supportsCurrentModelCapability("image_in");
8981
+ const extraction = extractMediaAttachments(text, this.host.imageStore, { imageMode: canInlineImages ? "inline" : "path" });
8918
8982
  if (!this.validateMediaCapabilities(extraction)) return;
8919
8983
  if (extraction.hasMedia) this.sendMessage(session, text, {
8920
8984
  hasMedia: true,
@@ -9091,10 +9155,6 @@ var InputController = class InputController {
9091
9155
  }
9092
9156
  validateMediaCapabilities(extraction) {
9093
9157
  if (!extraction.hasMedia) return true;
9094
- if (extraction.imageAttachmentIds.length > 0 && !this.supportsCurrentModelCapability("image_in")) {
9095
- this.host.showError(t("error.image_not_supported"));
9096
- return false;
9097
- }
9098
9158
  if (extraction.videoAttachmentIds.length > 0 && !this.supportsCurrentModelCapability("video_in")) {
9099
9159
  this.host.showError(t("error.video_not_supported"));
9100
9160
  return false;
@@ -9926,6 +9986,20 @@ function extractBracketedImagePastePaths(data) {
9926
9986
  if (paths.length === 0 || segmentStart !== pasted.length) return void 0;
9927
9987
  return paths;
9928
9988
  }
9989
+ /**
9990
+ * True only when bracketed-paste markers span the entire input and the
9991
+ * body is strictly empty — Cmd+V with an image-only clipboard (macOS
9992
+ * wraps nothing). Whitespace-only pastes and coalesced keystrokes around
9993
+ * the marker pair are not image pastes.
9994
+ */
9995
+ function isCompleteEmptyBracketedPaste(data) {
9996
+ if (!data.includes(BRACKET_PASTE_START)) return false;
9997
+ const startIndex = data.indexOf(BRACKET_PASTE_START);
9998
+ const endIndex = data.indexOf(BRACKET_PASTE_END, startIndex + 6);
9999
+ if (endIndex === -1) return false;
10000
+ if (startIndex !== 0 || endIndex + 6 !== data.length) return false;
10001
+ return data.slice(startIndex + 6, endIndex).length === 0;
10002
+ }
9929
10003
  const KITTY_CSI_U = /^\[(\d+);(\d+)((?::\d+)*)u$/;
9930
10004
  const CAPS_LOCK_BIT = 64;
9931
10005
  const CTRL_BIT = 4;
@@ -10138,6 +10212,11 @@ var CustomEditor = class extends Editor {
10138
10212
  if (!normalized.includes(BRACKET_PASTE_END)) this.consumingPaste = true;
10139
10213
  return;
10140
10214
  }
10215
+ if (this.onPasteImage !== void 0 && isCompleteEmptyBracketedPaste(normalized)) {
10216
+ const handler = this.onPasteImage;
10217
+ handler();
10218
+ return;
10219
+ }
10141
10220
  if (matchesKey(normalized, process.platform === "win32" ? "alt+v" : Key.ctrl("v"))) {
10142
10221
  if (this.expandPasteMarkerAtCursor()) return;
10143
10222
  if (this.onPasteImage !== void 0) {
@@ -14053,7 +14132,7 @@ var ScreamTUI = class {
14053
14132
  const card = new OnboardingCardComponent({
14054
14133
  colors: this.state.theme.colors,
14055
14134
  onConfigure: () => {
14056
- import("./dispatch-CIGyjBDa.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
14135
+ import("./dispatch-Du8ddILK.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
14057
14136
  this.restoreEditor();
14058
14137
  onDone?.();
14059
14138
  });