scream-code 0.9.3 → 0.9.4

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/README.md CHANGED
@@ -153,15 +153,15 @@ Open scream-code, type `/cc-connect`, and follow the prompts to select your plat
153
153
 
154
154
  Follow the steps to complete setup, then start the background daemon (scream-code can be closed while the daemon runs).
155
155
 
156
- **Remote chat quick commands:**
156
+ **cc-connect remote chat quick commands (only the following are supported):**
157
157
 
158
158
  | Command | Description |
159
159
  |---------|-------------|
160
- | `/new` | Create new session |
161
- | `/bind setup` | Enable file transfer (PDF, images, etc.) |
162
- | `/mode` | View available modes |
163
- | `/mode yolo` | Auto-approve all tools |
164
- | `/mode default` | Ask before each tool call |
160
+ | `/new` | Overwrite and create new session |
161
+ | `/current` | View current session status |
162
+ | `/mode [name]` | Switch mode |
163
+ | `/reasoning [level]` | View or switch reasoning effort |
164
+ | `/stop` | Stop current execution |
165
165
 
166
166
  ---
167
167
 
@@ -344,15 +344,15 @@ npm install -g cc-connect
344
344
 
345
345
  按照步骤完成配置与链接后,输入命令启动后台守护进程(关闭 screamcode 也可在后台聊天)。
346
346
 
347
- **远程聊天快捷指令:**
347
+ **cc-connect远程聊天常用快捷指令(仅支持以下指令):**
348
348
 
349
349
  | 指令 | 说明 |
350
350
  |------|------|
351
- | `/new` | 创建新会话 |
352
- | `/bind setup` | 开启文件传送功能,支持 PDF、图片等 |
353
- | `/mode` | 查看可用模式 |
354
- | `/mode yolo` | 自动批准所有工具 |
355
- | `/mode default` | 每次工具调用前询问 |
351
+ | `/new` | 覆盖并创建新会话 |
352
+ | `/current` | 查看当前会话状态 |
353
+ | `/mode [名称]` | 切换模式 |
354
+ | `/reasoning [等级]` | 查看或切换推理强度 |
355
+ | `/stop` | 停止当前执行 |
356
356
 
357
357
  ---
358
358
 
@@ -119240,25 +119240,6 @@ const SessionSummaryStateSchema = z.object({
119240
119240
  title: z.string().optional(),
119241
119241
  custom: z.record(z.string(), z.unknown()).optional()
119242
119242
  });
119243
- const CcConnectHistoryEntrySchema = z.object({
119244
- role: z.string(),
119245
- content: z.string(),
119246
- timestamp: z.string().optional()
119247
- });
119248
- const CcConnectSessionSchema = z.object({
119249
- id: z.string(),
119250
- name: z.string().optional(),
119251
- agent_session_id: z.string().optional(),
119252
- agent_type: z.string().optional(),
119253
- history: z.array(CcConnectHistoryEntrySchema).nullable().optional(),
119254
- created_at: z.string().optional(),
119255
- updated_at: z.string().optional()
119256
- });
119257
- const CcConnectSnapshotSchema = z.object({
119258
- sessions: z.record(z.string(), CcConnectSessionSchema).optional(),
119259
- active_session: z.record(z.string(), z.string()).optional(),
119260
- version: z.number().optional()
119261
- });
119262
119243
  var SessionStore = class {
119263
119244
  homeDir;
119264
119245
  sessionsDir;
@@ -119403,17 +119384,11 @@ var SessionStore = class {
119403
119384
  async listAll() {
119404
119385
  const index = await readSessionIndex(this.homeDir, this.sessionsDir);
119405
119386
  const sessions = [];
119406
- const seenAgentIds = /* @__PURE__ */ new Set();
119407
119387
  for (const entry of index.values()) {
119408
119388
  if (!await isDirectory(entry.sessionDir)) continue;
119409
119389
  const summary = await this.summaryFromDir(entry.sessionId, entry.sessionDir, entry.workDir);
119410
119390
  sessions.push(summary);
119411
- seenAgentIds.add(entry.sessionId);
119412
119391
  }
119413
- try {
119414
- const ccSessions = await listCcConnectSessions(seenAgentIds);
119415
- sessions.push(...ccSessions);
119416
- } catch {}
119417
119392
  sessions.sort(compareSessionSummary);
119418
119393
  return sessions;
119419
119394
  }
@@ -119587,90 +119562,6 @@ function compareSessionSummary(a, b) {
119587
119562
  if (a.id > b.id) return 1;
119588
119563
  return 0;
119589
119564
  }
119590
- const CC_CONNECT_SESSIONS_DIR = join$1(homedir(), ".cc-connect", "sessions");
119591
- /**
119592
- * Scan `~/.cc-connect/sessions/*.json` and return ScreamCode-compatible
119593
- * SessionSummary entries for every cc-connect session that has a
119594
- * non-empty agent_session_id (i.e. can actually be resumed).
119595
- *
119596
- * Sessions whose `agent_session_id` already appears in `seenAgentIds` are
119597
- * skipped — the native ScreamCode session takes precedence.
119598
- */
119599
- async function listCcConnectSessions(seenAgentIds) {
119600
- let entries;
119601
- try {
119602
- entries = await readdir(CC_CONNECT_SESSIONS_DIR);
119603
- } catch {
119604
- return [];
119605
- }
119606
- const results = [];
119607
- for (const name of entries) {
119608
- if (!name.endsWith(".json")) continue;
119609
- const projectName = name.slice(0, -5);
119610
- const parsed = await readCcConnectSnapshot(join$1(CC_CONNECT_SESSIONS_DIR, name));
119611
- if (parsed === void 0) continue;
119612
- for (const [, ccSession] of Object.entries(parsed.sessions ?? {})) {
119613
- const agentId = ccSession.agent_session_id?.trim();
119614
- if (!agentId || agentId.length === 0) continue;
119615
- if (seenAgentIds.has(agentId)) continue;
119616
- const summary = ccSessionToSummary(ccSession, agentId, projectName);
119617
- if (summary !== void 0) {
119618
- results.push(summary);
119619
- seenAgentIds.add(agentId);
119620
- }
119621
- }
119622
- }
119623
- return results;
119624
- }
119625
- async function readCcConnectSnapshot(filePath) {
119626
- try {
119627
- const raw = await readFile(filePath, "utf-8");
119628
- const parsed = JSON.parse(raw);
119629
- const result = CcConnectSnapshotSchema.safeParse(parsed);
119630
- return result.success ? result.data : void 0;
119631
- } catch {
119632
- return;
119633
- }
119634
- }
119635
- /** Extract the last user-message content for a preview in the picker. */
119636
- function lastPromptFromCcHistory(history) {
119637
- if (!history || history.length === 0) return void 0;
119638
- for (let i = history.length - 1; i >= 0; i--) {
119639
- const entry = history[i];
119640
- if (entry && entry.role === "user" && entry.content?.trim().length > 0) return entry.content.trim();
119641
- }
119642
- }
119643
- function ccSessionToSummary(cc, agentSessionId, projectName) {
119644
- const id = `cc:${projectName}/${cc.id}`;
119645
- const createdAt = parseCcTimestamp(cc.created_at);
119646
- const updatedAt = parseCcTimestamp(cc.updated_at) ?? createdAt;
119647
- if (createdAt === void 0 && updatedAt === void 0) return void 0;
119648
- const title = cc.name?.trim() ?? cc.id;
119649
- const lastPrompt = lastPromptFromCcHistory(cc.history);
119650
- const metadata = {
119651
- source: "cc-connect",
119652
- agentSessionId,
119653
- ccProject: projectName,
119654
- ccSessionId: cc.id
119655
- };
119656
- if (cc.agent_type) metadata["agentType"] = cc.agent_type;
119657
- return {
119658
- id,
119659
- workDir: homedir(),
119660
- sessionDir: "",
119661
- createdAt: createdAt ?? updatedAt,
119662
- updatedAt: updatedAt ?? createdAt,
119663
- title: title.length > 0 ? title : void 0,
119664
- lastPrompt,
119665
- metadata
119666
- };
119667
- }
119668
- /** Parse an ISO-8601 timestamp with optional timezone offset to epoch ms. */
119669
- function parseCcTimestamp(value) {
119670
- if (!value) return void 0;
119671
- const ms = Date.parse(value);
119672
- return Number.isFinite(ms) ? ms : void 0;
119673
- }
119674
119565
  //#endregion
119675
119566
  //#region ../../packages/jian/src/errors.ts
119676
119567
  /**
@@ -121652,6 +121543,7 @@ const dictionaries = {
121652
121543
  "session.title": "会话",
121653
121544
  "session.loading": "正在加载会话...",
121654
121545
  "session_picker.empty": "未找到会话。按 Escape 关闭。",
121546
+ "session_picker.cc_restricted": "CC专属会话不支持切换或删除,请点击或复制下方文件路径进入手动管理",
121655
121547
  "session.picker_title": "会话 ",
121656
121548
  "session.delete_confirm": "⚠️ 按 Enter 确认删除,Esc 取消",
121657
121549
  "session.picker_hint": "(↑↓ 导航,Enter 选择,d 删除,Esc 取消)",
@@ -122726,6 +122618,7 @@ const dictionaries = {
122726
122618
  "session.title": "Sessions",
122727
122619
  "session.loading": "Loading sessions...",
122728
122620
  "session_picker.empty": "No sessions found. Press Escape to close.",
122621
+ "session_picker.cc_restricted": "CC sessions cannot be switched or deleted. Use the file path below to manage manually",
122729
122622
  "session.picker_title": "Sessions ",
122730
122623
  "session.delete_confirm": "⚠️ Press Enter to confirm delete, Esc to cancel",
122731
122624
  "session.picker_hint": "(↑↓ Navigate, Enter Select, d Delete, Esc Cancel)",
@@ -124810,7 +124703,7 @@ function optionalBuildString(value) {
124810
124703
  return typeof value === "string" && value.length > 0 ? value : void 0;
124811
124704
  }
124812
124705
  const SCREAM_BUILD_INFO = {
124813
- version: optionalBuildString("0.9.3"),
124706
+ version: optionalBuildString("0.9.4"),
124814
124707
  channel: optionalBuildString(""),
124815
124708
  commit: optionalBuildString(""),
124816
124709
  buildTarget: optionalBuildString("darwin-arm64")
@@ -124971,14 +124864,20 @@ function createProgram(version, onMain, onPluginNodeRunner = () => {}, onStreamJ
124971
124864
  const program = new Command(CLI_COMMAND_NAME).description("下一代智能体的起点").version(version, "-V, --version").allowUnknownOption(false).configureHelp({ helpWidth: 100 }).helpOption("-h, --help", "显示帮助。").addHelpText("after", "\n文档: https://scream-cli.github.io/scream-code/\n");
124972
124865
  program.addOption(new Option("-S, --session [id]", "恢复会话。带 ID:恢复该会话。不带 ID:交互式选择。").argParser((val) => val === true ? "" : val)).addOption(new Option("-r, --resume [id]").hideHelp().argParser((val) => val === true ? "" : val)).option("-C, --continue", "继续当前工作目录的上一个会话。", false).option("-y, --yolo", "自动批准所有操作。", false).option("--auto", "以自动权限模式启动。", false).addOption(new Option("-m, --model <model>", "本次调用使用的 LLM 模型别名。默认使用 config.toml 中的 default_model。")).addOption(new Option("-p, --prompt <prompt>", "非交互式运行一条提示并打印响应。")).addOption(new Option("--output-format <format>", "提示模式的输出格式。默认为 text。").choices(["text", "stream-json"])).addOption(new Option("--skills-dir <dir>", "从该目录加载技能,而不是自动发现的用户和项目目录。可多次指定。").argParser((value, previous) => [...previous ?? [], value]).default([])).addOption(new Option("--yes").hideHelp().default(false)).addOption(new Option("--auto-approve").hideHelp().default(false)).option("--plan", "以计划模式启动。", false).option("--wolfpack", "启动时默认开启 WolfPack 批量并发模式。", false);
124973
124866
  registerExportCommand(program);
124974
- program.command("stream-json", { hidden: true }).option("--input-format <fmt>", "stream-json").option("--output-format <fmt>", "stream-json").option("--resume <id>", "resume a previous session").option("--model <model>", "model to use").option("--permission-mode <mode>", "permission mode").option("--permission-prompt-tool <mode>", "(ignored, cc-connect compat)").option("--replay-user-messages", "(ignored, cc-connect compat)").option("--verbose", "(ignored, cc-connect compat)").option("--system-prompt <text>", "(ignored, cc-connect compat)").option("--append-system-prompt <text>", "(passed through to agent)").option("--append-system-prompt-file <path>", "(passed through to agent; file contents are read and merged)").option("--allowedTools <list>", "(ignored, cc-connect compat)").option("--disallowedTools <list>", "(ignored, cc-connect compat)").option("--effort <value>", "(ignored, cc-connect compat)").option("--max-context-tokens <N>", "(ignored, cc-connect compat)").option("--skills-dir <dir>", "additional skills directory (repeatable)", (value, previous) => [...previous ?? [], value], []).option("--plugin-dir <dir>", "(ignored, cc-connect compat; repeatable)", (value, previous) => [...previous ?? [], value], []).action((subOpts) => {
124867
+ program.command("stream-json", { hidden: true }).option("--input-format <fmt>", "stream-json").option("--output-format <fmt>", "stream-json").option("--resume <id>", "resume a previous session").option("--model <model>", "model to use").option("--permission-mode <mode>", "permission mode").option("--permission-prompt-tool <mode>", "(ignored, cc-connect compat)").option("--replay-user-messages", "(ignored, cc-connect compat)").option("--verbose", "(ignored, cc-connect compat)").option("--system-prompt <text>", "(passed through to agent system prompt)").option("--append-system-prompt <text>", "(passed through to agent)").option("--append-system-prompt-file <path>", "(passed through to agent; file contents are read and merged)").option("--allowedTools <list>", "(comma-separated tool whitelist)").option("--disallowedTools <list>", "(comma-separated tool blacklist)").option("--effort <value>", "(reasoning effort: low/medium/high/max)").option("--max-context-tokens <N>", "(ignored, cc-connect compat)").option("--skills-dir <dir>", "additional skills directory (repeatable)", (value, previous) => [...previous ?? [], value], []).option("--plugin-dir <dir>", "(ignored, cc-connect compat; repeatable)", (value, previous) => [...previous ?? [], value], []).action((subOpts) => {
124975
124868
  onStreamJson({
124976
124869
  resume: subOpts["resume"],
124977
124870
  model: subOpts["model"],
124978
124871
  permissionMode: subOpts["permissionMode"],
124979
124872
  skillsDirs: subOpts["skillsDir"] ?? [],
124980
124873
  appendSystemPrompt: subOpts["appendSystemPrompt"],
124981
- appendSystemPromptFile: subOpts["appendSystemPromptFile"]
124874
+ appendSystemPromptFile: subOpts["appendSystemPromptFile"],
124875
+ systemPrompt: subOpts["systemPrompt"],
124876
+ allowedTools: subOpts["allowedTools"],
124877
+ disallowedTools: subOpts["disallowedTools"],
124878
+ effort: subOpts["effort"],
124879
+ maxContextTokens: subOpts["maxContextTokens"],
124880
+ pluginDirs: subOpts["pluginDir"] ?? []
124982
124881
  });
124983
124882
  });
124984
124883
  program.command("channel").description("管理 cc-connect 消息平台通道").command("setup").description("配置 cc-connect 并选择要连接的平台").action(() => {
@@ -135170,7 +135069,7 @@ function generateConfig$1(platform) {
135170
135069
  "type = \"claudecode\"",
135171
135070
  "",
135172
135071
  "[projects.agent.options]",
135173
- `cli_path = '${escapeSingleQuotes(detectScreamPath$1())}'`,
135072
+ `cmd = '${escapeSingleQuotes(detectScreamPath$1())}'`,
135174
135073
  `work_dir = '${escapeSingleQuotes(process.cwd())}'`,
135175
135074
  "mode = \"default\"",
135176
135075
  "",
@@ -146239,6 +146138,7 @@ function sessionRowsForPicker(sessions, currentSessionId, currentSessionHasConte
146239
146138
  title: session.title ?? null,
146240
146139
  last_prompt: session.lastPrompt ?? null,
146241
146140
  work_dir: session.workDir,
146141
+ session_dir: session.sessionDir ?? "",
146242
146142
  updated_at: session.updatedAt ?? session.createdAt ?? 0,
146243
146143
  metadata: session.metadata
146244
146144
  }));
@@ -147847,6 +147747,7 @@ var SessionPickerComponent = class extends Container {
147847
147747
  onSelect;
147848
147748
  onCancel;
147849
147749
  onDelete;
147750
+ onStatus;
147850
147751
  maxVisibleSessions;
147851
147752
  loading;
147852
147753
  focused = false;
@@ -147861,6 +147762,7 @@ var SessionPickerComponent = class extends Container {
147861
147762
  this.onSelect = opts.onSelect;
147862
147763
  this.onCancel = opts.onCancel;
147863
147764
  this.onDelete = opts.onDelete;
147765
+ this.onStatus = opts.onStatus;
147864
147766
  this.maxVisibleSessions = opts.maxVisibleSessions ?? 4;
147865
147767
  }
147866
147768
  handleInput(data) {
@@ -147877,9 +147779,17 @@ var SessionPickerComponent = class extends Container {
147877
147779
  if (!session) return;
147878
147780
  if (this.confirmingDelete) {
147879
147781
  this.confirmingDelete = false;
147782
+ if (session.metadata?.["source"] === "cc-connect") {
147783
+ this.onStatus(t("session_picker.cc_restricted"));
147784
+ return;
147785
+ }
147880
147786
  this.onDelete?.(session.id);
147881
147787
  return;
147882
147788
  }
147789
+ if (session.metadata?.["source"] === "cc-connect") {
147790
+ this.onStatus(t("session_picker.cc_restricted"));
147791
+ return;
147792
+ }
147883
147793
  this.onSelect(session.id);
147884
147794
  return;
147885
147795
  }
@@ -147896,7 +147806,12 @@ var SessionPickerComponent = class extends Container {
147896
147806
  const k = printableChar(data);
147897
147807
  if ((k === "d" || k === "D") && !this.confirmingDelete && this.sessions.length > 0) {
147898
147808
  const session = this.sessions[this.selectedIndex];
147899
- if (session && session.id !== this.currentSessionId) this.confirmingDelete = true;
147809
+ if (!session) return;
147810
+ if (session.metadata?.["source"] === "cc-connect") {
147811
+ this.onStatus(t("session_picker.cc_restricted"));
147812
+ return;
147813
+ }
147814
+ if (session.id !== this.currentSessionId) this.confirmingDelete = true;
147900
147815
  }
147901
147816
  }
147902
147817
  render(width) {
@@ -147967,12 +147882,12 @@ var SessionPickerComponent = class extends Container {
147967
147882
  const metaGap = " ";
147968
147883
  const metaGapWidth = visibleWidth(metaGap);
147969
147884
  const idLineWidth = indentWidth + idWidth;
147970
- const aliasedDir = homeAlias(session.work_dir);
147971
- const dirWidth = visibleWidth(aliasedDir);
147972
- if (idLineWidth + metaGapWidth + dirWidth <= width) card.push(indent + chalk.hex(colors.textMuted)(fullId) + chalk.hex(colors.textDim)(metaGap) + chalk.hex(colors.textMuted)(aliasedDir));
147885
+ const displayDir = session.session_dir ? homeAlias(session.session_dir) : homeAlias(session.work_dir);
147886
+ const dirWidth = visibleWidth(displayDir);
147887
+ if (idLineWidth + metaGapWidth + dirWidth <= width) card.push(indent + chalk.hex(colors.textMuted)(fullId) + chalk.hex(colors.textDim)(metaGap) + chalk.hex(colors.textMuted)(displayDir));
147973
147888
  else {
147974
147889
  card.push(indent + chalk.hex(colors.textMuted)(truncateToWidth(fullId, Math.max(idWidth, width - indentWidth), ELLIPSIS)));
147975
- const dir = truncatePathLeft(aliasedDir, Math.max(8, width - indentWidth));
147890
+ const dir = truncatePathLeft(displayDir, Math.max(8, width - indentWidth));
147976
147891
  card.push(indent + chalk.hex(colors.textMuted)(dir));
147977
147892
  }
147978
147893
  const rawPrompt = session.last_prompt?.trim();
@@ -148634,6 +148549,9 @@ var DialogManager = class {
148634
148549
  loading: this.host.getIsLoadingSessions(),
148635
148550
  currentSessionId: this.host.getCurrentSessionId(),
148636
148551
  colors: this.host.state.theme.colors,
148552
+ onStatus: (message) => {
148553
+ this.host.showStatus(message);
148554
+ },
148637
148555
  onSelect: (pickerId) => {
148638
148556
  const row = this.host.getSessions().find((s) => s.id === pickerId);
148639
148557
  const isCc = row?.metadata?.["source"] === "cc-connect";
@@ -149881,7 +149799,7 @@ function generateConfig(cliPath, platformType) {
149881
149799
  "type = \"claudecode\"",
149882
149800
  "",
149883
149801
  "[projects.agent.options]",
149884
- `cli_path = '${cliPath}'`,
149802
+ `cmd = '${cliPath}'`,
149885
149803
  `work_dir = '${workDir}'`,
149886
149804
  "mode = \"default\"",
149887
149805
  "",
@@ -150028,13 +149946,15 @@ var ClaudeStreamJsonWriter = class {
150028
149946
  this.currentModel = model;
150029
149947
  }
150030
149948
  /** Emit the `system` / `init` event. Called once after session creation. */
150031
- emitSystem(sessionId) {
149949
+ emitSystem(sessionId, model) {
150032
149950
  this.sessionId = sessionId;
150033
- this.writeJson({
149951
+ const event = {
150034
149952
  type: "system",
150035
149953
  subtype: "init",
150036
149954
  session_id: sessionId
150037
- });
149955
+ };
149956
+ if (model) event["model"] = model;
149957
+ this.writeLine(JSON.stringify(event));
150038
149958
  }
150039
149959
  /** Accumulate assistant text delta. Flush buffered thinking first. */
150040
149960
  writeAssistantDelta(delta) {
@@ -150145,14 +150065,16 @@ var ClaudeStreamJsonWriter = class {
150145
150065
  content: `Resume this session: scream -r ${sessionId}`
150146
150066
  });
150147
150067
  }
150148
- /** Emit a `control_request` event for cc-connect permission flow. */
150068
+ /** Emit a `control_request` event for cc-connect permission flow.
150069
+ * Format matches cc-connect's handleControlRequest which expects
150070
+ * `request.subtype = "can_use_tool"`, `request.tool_name`, `request.input`. */
150149
150071
  emitControlRequest(requestId, toolCallId, toolName, input) {
150150
150072
  this.writeJson({
150151
150073
  type: "control_request",
150152
150074
  request_id: requestId,
150153
- tool_use: {
150154
- id: toolCallId,
150155
- name: toolName,
150075
+ request: {
150076
+ subtype: "can_use_tool",
150077
+ tool_name: toolName,
150156
150078
  input
150157
150079
  }
150158
150080
  });
@@ -150204,7 +150126,7 @@ async function* readStdinMessages() {
150204
150126
  log.warn("stream-json: failed to parse stdin line", { line: trimmed.slice(0, 200) });
150205
150127
  continue;
150206
150128
  }
150207
- if (msg.type === "user" || msg.type === "control_response") yield msg;
150129
+ if (msg.type === "user" || msg.type === "control_response" || msg.type === "control_cancel_request") yield msg;
150208
150130
  else log.debug("stream-json: ignoring unknown stdin message type", { type: msg.type });
150209
150131
  }
150210
150132
  }
@@ -150292,7 +150214,8 @@ async function runStreamJson(opts) {
150292
150214
  error: String(error)
150293
150215
  });
150294
150216
  }
150295
- if (appendPrompt) {
150217
+ const hasSystemPrompt = opts.systemPrompt && opts.systemPrompt.trim().length > 0;
150218
+ if (hasSystemPrompt || appendPrompt) {
150296
150219
  try {
150297
150220
  originalAgentsMd = await readFile(agentsMdPath, "utf-8");
150298
150221
  } catch {}
@@ -150301,10 +150224,13 @@ async function runStreamJson(opts) {
150301
150224
  cc-connect send --image /absolute/path/to/image.png
150302
150225
  cc-connect send --file /absolute/path/to/file.pdf
150303
150226
  当用户要求你发送文件、截图、生成的图片时,使用 Bash 工具执行上述命令即可。
150304
- \n${appendPrompt}`;
150227
+ \n${hasSystemPrompt ? `# System Prompt (from --system-prompt)\n\n${opts.systemPrompt}\n` : ""}\n${appendPrompt ? appendPrompt : ""}`;
150305
150228
  await writeFile(agentsMdPath, originalAgentsMd ? `${ccPrompt}\n\n${originalAgentsMd}` : ccPrompt, "utf-8");
150306
150229
  injectedAgentsMd = true;
150307
- log.info("stream-json: injected cc-connect system prompt into AGENTS.md");
150230
+ log.info("stream-json: injected cc-connect system prompt into AGENTS.md", {
150231
+ hasSystemPrompt,
150232
+ hasAppendPrompt: appendPrompt.length > 0
150233
+ });
150308
150234
  }
150309
150235
  try {
150310
150236
  await harness.ensureConfigFile();
@@ -150323,6 +150249,17 @@ async function runStreamJson(opts) {
150323
150249
  } else log.warn("stream-json: control_response for unknown request_id", { requestId: msg.response.request_id });
150324
150250
  continue;
150325
150251
  }
150252
+ if (msg.type === "control_cancel_request") {
150253
+ const pending = pendingApprovals.get(msg.request_id);
150254
+ if (pending) {
150255
+ pendingApprovals.delete(msg.request_id);
150256
+ pending.resolve({
150257
+ decision: "rejected",
150258
+ feedback: "Permission request cancelled"
150259
+ });
150260
+ }
150261
+ continue;
150262
+ }
150326
150263
  const userText = extractUserText(msg);
150327
150264
  if (!userText) {
150328
150265
  log.warn("stream-json: empty user message, skipping");
@@ -150355,7 +150292,12 @@ async function runStreamJson(opts) {
150355
150292
  workDir,
150356
150293
  model,
150357
150294
  permission: mappedPermission,
150358
- planMode: mappedPlanMode
150295
+ planMode: mappedPlanMode,
150296
+ thinking: opts.effort,
150297
+ metadata: {
150298
+ source: "cc-connect",
150299
+ agentType: "scream-code"
150300
+ }
150359
150301
  });
150360
150302
  log.info("stream-json: recreated session", { sessionId: session.id });
150361
150303
  }
@@ -150366,14 +150308,19 @@ async function runStreamJson(opts) {
150366
150308
  workDir,
150367
150309
  model,
150368
150310
  permission: mappedPermission,
150369
- planMode: mappedPlanMode
150311
+ planMode: mappedPlanMode,
150312
+ thinking: opts.effort,
150313
+ metadata: {
150314
+ source: "cc-connect",
150315
+ agentType: "scream-code"
150316
+ }
150370
150317
  });
150371
150318
  log.info("stream-json: created session", { sessionId: session.id });
150372
150319
  }
150373
150320
  currentSessionId = session.id;
150374
150321
  writer.setSessionId(session.id);
150375
150322
  writer.setModel(opts.model ?? config.defaultModel ?? "");
150376
- writer.emitSystem(sessionKey);
150323
+ writer.emitSystem(sessionKey, opts.model ?? config.defaultModel);
150377
150324
  function sendControlRequest(request) {
150378
150325
  let resolve;
150379
150326
  const promise = new Promise((res) => {
@@ -150384,25 +150331,55 @@ async function runStreamJson(opts) {
150384
150331
  writer.emitControlRequest(reqId, request.toolCallId, request.toolName, request.display);
150385
150332
  return promise;
150386
150333
  }
150334
+ const allowedSet = opts.allowedTools ? new Set(opts.allowedTools.split(",").map((t) => t.trim()).filter(Boolean)) : void 0;
150335
+ const disallowedSet = opts.disallowedTools ? new Set(opts.disallowedTools.split(",").map((t) => t.trim()).filter(Boolean)) : void 0;
150336
+ function isToolBlocked(toolName) {
150337
+ if (disallowedSet?.has(toolName)) return true;
150338
+ if (allowedSet && allowedSet.size > 0 && !allowedSet.has(toolName)) return true;
150339
+ return false;
150340
+ }
150387
150341
  switch (opts.permissionMode) {
150388
150342
  case "yolo":
150389
150343
  case "bypassPermissions":
150390
- session.setApprovalHandler(() => ({ decision: "approved" }));
150344
+ session.setApprovalHandler((request) => {
150345
+ if (isToolBlocked(request.toolName)) return {
150346
+ decision: "rejected",
150347
+ feedback: `Tool ${request.toolName} is disallowed`
150348
+ };
150349
+ return { decision: "approved" };
150350
+ });
150391
150351
  break;
150392
150352
  case "dontAsk":
150393
- session.setApprovalHandler(() => ({
150394
- decision: "rejected",
150395
- feedback: "dontAsk 模式下工具调用被自动拒绝"
150396
- }));
150353
+ session.setApprovalHandler((request) => {
150354
+ if (disallowedSet?.has(request.toolName)) return {
150355
+ decision: "rejected",
150356
+ feedback: `Tool ${request.toolName} is disallowed`
150357
+ };
150358
+ if (allowedSet?.has(request.toolName)) return { decision: "approved" };
150359
+ return {
150360
+ decision: "rejected",
150361
+ feedback: "dontAsk mode: tool call auto-denied"
150362
+ };
150363
+ });
150397
150364
  break;
150398
150365
  case "acceptEdits":
150399
150366
  session.setApprovalHandler((request) => {
150367
+ if (isToolBlocked(request.toolName)) return {
150368
+ decision: "rejected",
150369
+ feedback: `Tool ${request.toolName} is disallowed`
150370
+ };
150400
150371
  if (["Edit", "Write"].includes(request.toolName)) return { decision: "approved" };
150401
150372
  return sendControlRequest(request);
150402
150373
  });
150403
150374
  break;
150404
150375
  default:
150405
- session.setApprovalHandler((request) => sendControlRequest(request));
150376
+ session.setApprovalHandler((request) => {
150377
+ if (isToolBlocked(request.toolName)) return {
150378
+ decision: "rejected",
150379
+ feedback: `Tool ${request.toolName} is disallowed`
150380
+ };
150381
+ return sendControlRequest(request);
150382
+ });
150406
150383
  break;
150407
150384
  }
150408
150385
  session.setQuestionHandler(() => null);
@@ -150467,6 +150444,10 @@ async function runStreamJson(opts) {
150467
150444
  if (type === "turn.step.started" || type === "turn.step.interrupted") writer.flushAssistant();
150468
150445
  else if (type === "turn.step.retrying") writer.discardAssistant();
150469
150446
  else if (type === "assistant.delta") writer.writeAssistantDelta(event.delta);
150447
+ else if (type === "thinking.delta") writer.writeThinkingDelta(event.delta);
150448
+ else if (type === "tool.call.started") writer.writeToolCall(event.toolCallId, event.name, event.args);
150449
+ else if (type === "tool.call.delta") writer.writeToolCallDelta(event.toolCallId, event.name, event.argumentsPart);
150450
+ else if (type === "tool.result") writer.writeToolResult(event.toolCallId, event.output, event.isError);
150470
150451
  else if (type === "turn.step.completed") {
150471
150452
  if (event.usage) {
150472
150453
  const inputTotal = (event.usage.inputOther ?? 0) + (event.usage.inputCacheRead ?? 0) + (event.usage.inputCacheCreation ?? 0);
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-BE3SBnF0.mjs")).main();
9
+ (await import("./app-D7S2yEaM.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.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",