weacpx 0.4.6 → 0.4.9

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
@@ -180,7 +180,7 @@ weacpx restart
180
180
  | `weacpx agent add <name>` | 从内置模板添加 agent;已存在且配置不同的同名 agent 不会被覆盖 |
181
181
  | `weacpx agent rm <name>` | 删除 agent |
182
182
  | `weacpx workspace list` | 查看本机已注册的 workspace |
183
- | `weacpx workspace add [name]` | 把当前目录注册成 workspace;不传 `name` 时使用当前目录名 |
183
+ | `weacpx workspace add [name] [--raw]` | 把当前目录注册成 workspace;不传 `name` 时使用当前目录名,含特殊字符的名称会被自动规范化 |
184
184
  | `weacpx workspace rm <name>` | 删除 workspace |
185
185
 
186
186
  首次运行 `weacpx start` 或 `weacpx run` 时,如果没有会话、workspace 和插件,CLI 会询问是否把当前目录创建为 workspace,并选择一个内置 agent 模板;服务启动后会通过正常会话创建流程创建初始 acpx 会话。
@@ -200,8 +200,9 @@ weacpx ws rm backend
200
200
  | 命令 | 说明 |
201
201
  |------|------|
202
202
  | `weacpx workspace list` | 列出已注册的 workspace 及其路径 |
203
- | `weacpx workspace add` | 把当前目录注册为 workspace,名称默认取当前目录名 |
204
- | `weacpx workspace add <name>` | 把当前目录注册为指定名称 |
203
+ | `weacpx workspace add` | 把当前目录注册为 workspace,名称默认取当前目录名(自动规范化) |
204
+ | `weacpx workspace add <name>` | 把当前目录注册为指定名称(含特殊字符时自动规范化) |
205
+ | `weacpx workspace add [name] --raw` | 保留原始名称(含空格等),后续命令需要用引号引用 |
205
206
  | `weacpx workspace rm <name>` | 删除指定 workspace |
206
207
 
207
208
  常见用法:
@@ -224,7 +225,7 @@ weacpx ws rm frontend
224
225
  /ss new claude --ws frontend
225
226
  ```
226
227
 
227
- 注意:`workspace add` 总是注册**当前终端所在目录**;如果不传名称,会用当前目录名作为 workspace 名称。
228
+ 注意:`workspace add` 总是注册**当前终端所在目录**。如果不传名称,会用当前目录名作为 workspace 名称。含空格、中文等字符的名称会被自动规范化为 `[a-zA-Z0-9._-]+`(例如目录 `My Project` 会保存为 `My-Project`),重名时追加 `-2`、`-3`。如需保留原始名称,加 `--raw`;之后 `weacpx workspace rm`、`/ws rm`、`--ws <name>` 都需要用引号引用,例如 `weacpx workspace rm "My Project"`。
228
229
 
229
230
  ### `agent` CLI 怎么用
230
231
 
@@ -292,7 +293,7 @@ opencode, qoder, qwen, trae
292
293
  | 命令 | 说明 |
293
294
  |------|------|
294
295
  | `/workspaces` / `/workspace` / `/ws` | 查看 workspace 列表 |
295
- | `/ws new <name> -d <path>` | 添加 workspace,`path` 是电脑上的绝对路径,Windows 不用区分正反斜杠 |
296
+ | `/ws new <name> -d <path> [--raw]` | 添加 workspace,`path` 是电脑上的绝对路径,Windows 不用区分正反斜杠;含空格/中文等特殊字符的名称会被自动规范化,--raw 保留原名 |
296
297
  | `/workspace rm <name>` | 删除 workspace |
297
298
 
298
299
  ### Session 会话
@@ -60,6 +60,10 @@ function encodeBridgePromptToolEvent(event) {
60
60
  return `${JSON.stringify(event)}
61
61
  `;
62
62
  }
63
+ function encodeBridgePromptThoughtEvent(event) {
64
+ return `${JSON.stringify(event)}
65
+ `;
66
+ }
63
67
  function encodeBridgeSessionProgressEvent(event) {
64
68
  return `${JSON.stringify(event)}
65
69
  `;
@@ -366,6 +370,7 @@ var init_tool_kind_emoji = __esm(() => {
366
370
  function createStreamingPromptState(formatToolCalls = false, options) {
367
371
  let toolEventMode;
368
372
  let onToolEvent;
373
+ let onThought;
369
374
  if (options === undefined) {
370
375
  toolEventMode = "text";
371
376
  onToolEvent = undefined;
@@ -374,6 +379,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
374
379
  toolEventMode = "structured";
375
380
  } else {
376
381
  onToolEvent = options.onToolEvent;
382
+ onThought = options.onThought;
377
383
  toolEventMode = resolveToolEventMode({
378
384
  toolEventMode: options.mode,
379
385
  onToolEvent
@@ -388,6 +394,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
388
394
  emittedToolCallIds: new Set,
389
395
  toolEventMode,
390
396
  onToolEvent,
397
+ onThought,
391
398
  finalize() {
392
399
  if (this.pendingLine.trim().length > 0) {
393
400
  parseStreamingChunks(this, this.pendingLine);
@@ -446,6 +453,14 @@ function parseStreamingChunks(state, line) {
446
453
  }
447
454
  return;
448
455
  }
456
+ const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
457
+ if (isThoughtChunk) {
458
+ const chunk2 = update.content.text;
459
+ if (chunk2.length > 0) {
460
+ state.onThought?.(chunk2);
461
+ }
462
+ return;
463
+ }
449
464
  const isMessageChunk = update.sessionUpdate === "agent_message_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
450
465
  if (!isMessageChunk)
451
466
  return;
@@ -473,7 +488,7 @@ function formatToolCallEvent(update, sessionUpdate) {
473
488
  if (title.length === 0)
474
489
  return null;
475
490
  const emoji = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
476
- const inputSummary = summarizeToolInput(update.rawInput, title);
491
+ const inputSummary = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
477
492
  const status = readString(update, "status");
478
493
  if (!inputSummary && status === "pending")
479
494
  return null;
@@ -504,15 +519,23 @@ function buildToolUseEvent(update) {
504
519
  })();
505
520
  const title = (update.title ?? "").trim();
506
521
  const toolName = title || "Tool";
507
- const summaryRaw = summarizeToolInput(update.rawInput, title);
522
+ const summaryRaw = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
508
523
  const summary = summaryRaw && summaryRaw !== title ? summaryRaw : undefined;
509
524
  const statusRaw = readString(update, "status");
510
525
  const status = statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : "running";
526
+ const rawInput = update.rawInput;
527
+ const content = update.content;
528
+ const rawOutput = update.rawOutput;
529
+ const locations = update.locations;
511
530
  return {
512
531
  toolCallId,
513
532
  toolName,
514
533
  kind,
515
534
  ...summary ? { summary } : {},
535
+ ...rawInput !== undefined ? { rawInput } : {},
536
+ ...content !== undefined ? { content } : {},
537
+ ...rawOutput !== undefined ? { rawOutput } : {},
538
+ ...locations !== undefined ? { locations } : {},
516
539
  status
517
540
  };
518
541
  }
@@ -831,6 +854,8 @@ function buildQueueOwnerPayload(input) {
831
854
  nonInteractivePermissions: input.nonInteractivePermissions,
832
855
  ttlMs: input.ttlMs ?? 300000,
833
856
  maxQueueDepth: input.maxQueueDepth ?? 16,
857
+ ...Number.isFinite(input.promptRetries) ? { promptRetries: input.promptRetries } : {},
858
+ ...input.sessionOptions ? { sessionOptions: input.sessionOptions } : {},
834
859
  mcpServers: input.mcpServers
835
860
  };
836
861
  }
@@ -1113,6 +1138,7 @@ class BridgeRuntime {
1113
1138
  async updatePermissionPolicy(policy) {
1114
1139
  this.options.permissionMode = policy.permissionMode;
1115
1140
  this.options.nonInteractivePermissions = policy.nonInteractivePermissions;
1141
+ this.options.permissionPolicy = policy.permissionPolicy;
1116
1142
  return {};
1117
1143
  }
1118
1144
  async hasSession(input) {
@@ -1124,6 +1150,26 @@ class BridgeRuntime {
1124
1150
  const result = await this.run(spawnSpec.command, spawnSpec.args);
1125
1151
  return { exists: result.code === 0 };
1126
1152
  }
1153
+ async tailSessionHistory(input) {
1154
+ const candidates = [
1155
+ ["sessions", "history", "quiet", "-s", input.name, String(input.lines)],
1156
+ ["sessions", "history", "quiet", input.name, String(input.lines)],
1157
+ ["sessions", "history", "-s", input.name, "--tail", String(input.lines)],
1158
+ ["sessions", "history", input.name, "--tail", String(input.lines)],
1159
+ ["sessions", "history", "--name", input.name, "--tail", String(input.lines)]
1160
+ ];
1161
+ let lastResult;
1162
+ for (const tailArgs of candidates) {
1163
+ const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, tailArgs));
1164
+ const result = await this.run(spawnSpec.command, spawnSpec.args);
1165
+ if (result.code === 0) {
1166
+ return { text: result.stdout.trimEnd() };
1167
+ }
1168
+ lastResult = result;
1169
+ }
1170
+ const message = lastResult?.stderr || lastResult?.stdout || "sessions history failed";
1171
+ throw new Error(message);
1172
+ }
1127
1173
  async ensureSession(input, onProgress) {
1128
1174
  onProgress?.("spawn");
1129
1175
  const onStderrLine = onProgress ? (line) => {
@@ -1346,7 +1392,11 @@ class BridgeRuntime {
1346
1392
  const permissionMode = this.options.permissionMode ?? "approve-all";
1347
1393
  const nonInteractivePermissions = this.options.nonInteractivePermissions ?? "deny";
1348
1394
  const modeFlag = permissionModeToFlag(permissionMode);
1349
- return [modeFlag, "--non-interactive-permissions", nonInteractivePermissions];
1395
+ const args = [modeFlag, "--non-interactive-permissions", nonInteractivePermissions];
1396
+ if (typeof this.options.permissionPolicy === "string" && this.options.permissionPolicy.trim().length > 0) {
1397
+ args.push("--permission-policy", this.options.permissionPolicy);
1398
+ }
1399
+ return args;
1350
1400
  }
1351
1401
  }
1352
1402
  function spawnCapture(command, args, options) {
@@ -1398,7 +1448,8 @@ async function runStreamingPrompt(command, args, onEvent, options = {}) {
1398
1448
  const toolEventMode = options.toolEventMode ?? "text";
1399
1449
  const state = createStreamingPromptState(options.formatToolCalls ?? false, {
1400
1450
  mode: toolEventMode,
1401
- ...onEvent && (toolEventMode === "structured" || toolEventMode === "both") ? { onToolEvent: (toolEvent) => onEvent({ type: "prompt.tool_event", event: toolEvent }) } : {}
1451
+ ...onEvent && (toolEventMode === "structured" || toolEventMode === "both") ? { onToolEvent: (toolEvent) => onEvent({ type: "prompt.tool_event", event: toolEvent }) } : {},
1452
+ ...onEvent ? { onThought: (chunk) => onEvent({ type: "prompt.thought", text: chunk }) } : {}
1402
1453
  });
1403
1454
  let lastReplyAt = now();
1404
1455
  const flushBuffer = () => {
@@ -1514,6 +1565,7 @@ var BRIDGE_METHODS = new Set([
1514
1565
  "updatePermissionPolicy",
1515
1566
  "hasSession",
1516
1567
  "ensureSession",
1568
+ "tailSessionHistory",
1517
1569
  "prompt",
1518
1570
  "setMode",
1519
1571
  "cancel",
@@ -1522,6 +1574,7 @@ var BRIDGE_METHODS = new Set([
1522
1574
  var SESSION_SCOPED_METHODS = new Set([
1523
1575
  "hasSession",
1524
1576
  "ensureSession",
1577
+ "tailSessionHistory",
1525
1578
  "prompt",
1526
1579
  "setMode",
1527
1580
  "cancel",
@@ -1596,6 +1649,14 @@ class BridgeServer {
1596
1649
  cwd: requireString(params, "cwd"),
1597
1650
  name: requireString(params, "name")
1598
1651
  });
1652
+ case "tailSessionHistory":
1653
+ return await this.runtime.tailSessionHistory({
1654
+ agent: requireString(params, "agent"),
1655
+ agentCommand: asOptionalString(params.agentCommand),
1656
+ cwd: requireString(params, "cwd"),
1657
+ name: requireString(params, "name"),
1658
+ lines: requirePositiveInt(params, "lines")
1659
+ });
1599
1660
  case "ensureSession":
1600
1661
  return await this.runtime.ensureSession({
1601
1662
  agent: requireString(params, "agent"),
@@ -1647,6 +1708,12 @@ class BridgeServer {
1647
1708
  event: "prompt.tool_event",
1648
1709
  toolEvent: event.event
1649
1710
  }));
1711
+ } else if (event.type === "prompt.thought") {
1712
+ writeLine?.(encodeBridgePromptThoughtEvent({
1713
+ id: requestId,
1714
+ event: "prompt.thought",
1715
+ text: event.text
1716
+ }));
1650
1717
  }
1651
1718
  });
1652
1719
  case "setMode":
@@ -1745,6 +1812,13 @@ function requireString(params, key) {
1745
1812
  }
1746
1813
  return value;
1747
1814
  }
1815
+ function requirePositiveInt(params, key) {
1816
+ const value = params[key];
1817
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
1818
+ throw new BridgeInvalidRequestError(`${key} must be a positive integer`);
1819
+ }
1820
+ return value;
1821
+ }
1748
1822
  function requirePromptText(params, media) {
1749
1823
  const value = params.text;
1750
1824
  if (typeof value !== "string") {
@@ -71,6 +71,10 @@ export interface ToolUseEvent {
71
71
  kind: ToolUseKind;
72
72
  /** Best-effort one-line summary derived from `rawInput`. */
73
73
  summary?: string;
74
+ rawInput?: unknown;
75
+ content?: unknown;
76
+ rawOutput?: unknown;
77
+ locations?: unknown;
74
78
  status: ToolUseStatus;
75
79
  /** Set when status transitions out of "running". */
76
80
  durationMs?: number;