@clawos-dev/clawd 0.2.428 → 0.2.430

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/dist/cli.cjs CHANGED
@@ -7260,7 +7260,7 @@ var init_principal = __esm({
7260
7260
  });
7261
7261
 
7262
7262
  // ../protocol/src/schemas.ts
7263
- var SessionStatusSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, SelectOptionSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, ToolFeaturesSchema, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, TURN_END_REASONS, ParsedEventSchema, SessionCreateArgs, ProjectEntry, ProjectPatchArgs, ProjectRevealArgs, ProjectCreateArgs, SessionImportExternalArgs, SessionListArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionCurrentTurnSenderArgs, SessionSubscribeArgs, SessionPinArgs, SessionArchiveArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, ClientInfoSchema, AuthRequestFrameSchema, AuthOkFrameSchema, DeviceConnectionEntrySchema, DeviceConnectionsResponseSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, DEFAULT_DEV_COMMAND, ProjectMetadataSchema, PROJECT_STAGE_VALUES, ProjectStageSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, GetProjectArgsSchema, GetProjectResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema, SetProdUrlArgsSchema, SetProdUrlResultSchema, StartDevServerArgsSchema, StartDevServerResultSchema, ASSISTANT_REPORTABLE_STAGES, ReportStageArgsSchema, ReportStageResultSchema, StopDevServerArgsSchema, StopDevServerResultSchema, AppBuilderProjectUpdatedEventSchema, PublishArgsSchema, PublishResultSchema, DismissPublishJobArgsSchema, DismissPublishJobResultSchema, AppBuilderPublishProgressEventSchema, AppBuilderPublishFailedEventSchema;
7263
+ var SessionStatusSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, SelectOptionSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, ToolFeaturesSchema, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, TURN_END_REASONS, ParsedEventSchema, SessionCreateArgs, ProjectEntry, ProjectPatchArgs, ProjectRevealArgs, ProjectCreateArgs, SessionImportExternalArgs, SessionListArgs, SessionIdArgs, SessionUpdateArgs, QueuedMessage, SessionSendArgs, SessionDequeueArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionCurrentTurnSenderArgs, SessionSubscribeArgs, SessionPinArgs, SessionArchiveArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, ClientInfoSchema, AuthRequestFrameSchema, AuthOkFrameSchema, DeviceConnectionEntrySchema, DeviceConnectionsResponseSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, DEFAULT_DEV_COMMAND, ProjectMetadataSchema, PROJECT_STAGE_VALUES, ProjectStageSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, GetProjectArgsSchema, GetProjectResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema, SetProdUrlArgsSchema, SetProdUrlResultSchema, StartDevServerArgsSchema, StartDevServerResultSchema, ASSISTANT_REPORTABLE_STAGES, ReportStageArgsSchema, ReportStageResultSchema, StopDevServerArgsSchema, StopDevServerResultSchema, AppBuilderProjectUpdatedEventSchema, PublishArgsSchema, PublishResultSchema, DismissPublishJobArgsSchema, DismissPublishJobResultSchema, AppBuilderPublishProgressEventSchema, AppBuilderPublishFailedEventSchema;
7264
7264
  var init_schemas = __esm({
7265
7265
  "../protocol/src/schemas.ts"() {
7266
7266
  "use strict";
@@ -7831,9 +7831,25 @@ var init_schemas = __esm({
7831
7831
  // 通过 sessionUpdate 修改这个字段。SessionFile 仍保留 appBuilderProject 字段供 daemon 内部 mutate。
7832
7832
  })
7833
7833
  });
7834
+ QueuedMessage = external_exports.object({
7835
+ id: external_exports.string().min(1),
7836
+ text: external_exports.string(),
7837
+ queuedAt: external_exports.number(),
7838
+ senderDisplayName: external_exports.string().optional()
7839
+ });
7834
7840
  SessionSendArgs = external_exports.object({
7835
7841
  sessionId: external_exports.string().min(1),
7836
- text: external_exports.string()
7842
+ text: external_exports.string(),
7843
+ /**
7844
+ * turn 在跑时是入队还是直发。**入队与否由调用方显式声明,daemon 不按来源猜**:
7845
+ * UI 输入框传 true;IM / 飞书 / dispatch 注入不传 = 维持原状直发(老板 2026-08-31:
7846
+ * 其他地方先不加队列)。缺省 false → 行为与本字段引入前逐字一致。
7847
+ */
7848
+ queueWhileRunning: external_exports.boolean().optional()
7849
+ });
7850
+ SessionDequeueArgs = external_exports.object({
7851
+ sessionId: external_exports.string().min(1),
7852
+ id: external_exports.string().min(1).optional()
7837
7853
  });
7838
7854
  SessionRewindArgs = external_exports.object({
7839
7855
  sessionId: external_exports.string().min(1),
@@ -8407,9 +8423,13 @@ var init_methods = __esm({
8407
8423
  args: SessionIdArgs
8408
8424
  },
8409
8425
  "session:send": {
8410
- summary: "\u5411\u4F1A\u8BDD\u53D1\u4E00\u6761\u7528\u6237\u6D88\u606F\uFF08\u89E6\u53D1\u65B0\u4E00\u8F6E turn\uFF09",
8426
+ summary: "\u5411\u4F1A\u8BDD\u53D1\u4E00\u6761\u7528\u6237\u6D88\u606F\uFF08\u89E6\u53D1\u65B0\u4E00\u8F6E turn\uFF09\uFF1BqueueWhileRunning=true \u4E14 turn \u5728\u8DD1\u65F6\u6539\u4E3A\u8FDB\u961F\u5217",
8411
8427
  args: SessionSendArgs
8412
8428
  },
8429
+ "session:dequeue": {
8430
+ summary: "\u64A4\u9500\u6392\u961F\u4E2D\u7684\u6D88\u606F\uFF1A\u5E26 id \u5220\u4E00\u6761\uFF0C\u4E0D\u5E26 id \u6E05\u7A7A\u6574\u961F\uFF08spec 2026-08-31-chat-input-queue-design\uFF09",
8431
+ args: SessionDequeueArgs
8432
+ },
8413
8433
  "session:currentTurnSender": {
8414
8434
  summary: "\u67E5\u5F53\u524D turn \u7684\u9A71\u52A8\u8005\uFF08owner / guest \u7684 SenderInfo\uFF09\u3002\u7701\u7565 sessionId \u6216\u4F20\u7A7A\u4E32 \u2192 \u76F4\u63A5\u8FD4 sender:null\uFF0C\u4E0D\u62A5\u9519",
8415
8435
  args: SessionCurrentTurnSenderArgs
@@ -45116,6 +45136,8 @@ function cloneState(s) {
45116
45136
  // reduce 内 push/shift 在调用方原 state 上副作用(reducer 纯函数契约)
45117
45137
  readyForSend: s.readyForSend,
45118
45138
  pendingSend: s.pendingSend.slice(),
45139
+ // 同理浅拷贝:入队 / 出队都在 next 上原地改
45140
+ queuedSends: s.queuedSends.slice(),
45119
45141
  // per-turn sender (spec §4.3):SenderInfo 只在 case 'send' 覆盖,其它 kind 直接复用引用
45120
45142
  currentTurnSender: s.currentTurnSender
45121
45143
  };
@@ -45150,6 +45172,48 @@ function resetReadyGate(next) {
45150
45172
  next.pendingSend = [];
45151
45173
  next.readyForSend = true;
45152
45174
  }
45175
+ function queueFrame(state) {
45176
+ return {
45177
+ kind: "emit-frame",
45178
+ frame: {
45179
+ type: "session:queue",
45180
+ sessionId: state.file.sessionId,
45181
+ items: state.queuedSends.map((q) => ({
45182
+ id: q.id,
45183
+ text: q.text,
45184
+ queuedAt: q.queuedAt,
45185
+ ...q.sender.displayName ? { senderDisplayName: q.sender.displayName } : {}
45186
+ }))
45187
+ }
45188
+ };
45189
+ }
45190
+ function clearQueuedSends(next, effects) {
45191
+ if (next.queuedSends.length === 0) return;
45192
+ next.queuedSends = [];
45193
+ effects.push(queueFrame(next));
45194
+ }
45195
+ function deliverUserText(next, args, deps) {
45196
+ const effects = [];
45197
+ next.currentTurnSender = args.sender;
45198
+ const userEvent = {
45199
+ kind: "user_text",
45200
+ text: args.text,
45201
+ uuid: deps.genUuid?.(),
45202
+ sender: args.sender,
45203
+ ...args.meta ? { meta: args.meta } : {}
45204
+ };
45205
+ effects.push(...pushEventToBufferInPlace(next, userEvent, deps));
45206
+ if (deps.mode === "tui" && (next.file.tool ?? "claude") === "claude") {
45207
+ next.pendingSend.push(args.text);
45208
+ effects.push(...tryFlushPending(next, deps));
45209
+ } else {
45210
+ effects.push({
45211
+ kind: "write-stdin",
45212
+ payload: deps.encodeStdin(args.text, { sessionId: next.file.sessionId })
45213
+ });
45214
+ }
45215
+ return effects;
45216
+ }
45153
45217
  var SEEN_UUID_CAP = 2e3;
45154
45218
  function applyEventBatch(state, events, deps) {
45155
45219
  if (events.length === 0) return { state, effects: [] };
@@ -45454,6 +45518,14 @@ function pushEventToBufferInPlace(next, event, deps) {
45454
45518
  effects.push({ kind: "persist-file", file: next.file });
45455
45519
  effects.push(sessionInfoFrame(next.file));
45456
45520
  }
45521
+ if (isTurnEnd && next.procAlive && (next.status === "running" || next.status === "spawning") && next.queuedSends.length > 0) {
45522
+ const items = next.queuedSends;
45523
+ next.queuedSends = [];
45524
+ const merged = items.map((q) => q.text).join("\n\n");
45525
+ effects.push(queueFrame(next));
45526
+ effects.push(...deliverUserText(next, { text: merged, sender: items[0].sender }, deps));
45527
+ return effects;
45528
+ }
45457
45529
  if (isTurnEnd && next.procAlive && (next.status === "running" || next.status === "spawning")) {
45458
45530
  next.status = "running-idle";
45459
45531
  effects.push({
@@ -45504,6 +45576,19 @@ function applyCommand(state, command, deps) {
45504
45576
  const sessionId = next.file.sessionId;
45505
45577
  switch (command.kind) {
45506
45578
  case "send": {
45579
+ if (command.queueWhileRunning && (next.status === "running" || next.status === "spawning")) {
45580
+ next.queuedSends = [
45581
+ ...next.queuedSends,
45582
+ {
45583
+ id: deps.genUuid?.() ?? `q-${deps.now()}-${next.queuedSends.length}`,
45584
+ text: command.text,
45585
+ queuedAt: deps.now(),
45586
+ sender: command.senderPrincipal
45587
+ }
45588
+ ];
45589
+ effects.push(queueFrame(next));
45590
+ return { state: next, effects };
45591
+ }
45507
45592
  if (next.status === "running-idle") {
45508
45593
  effects.push({ kind: "cancel-idle-kill", sessionId });
45509
45594
  next.status = next.procAlive ? "running" : "idle";
@@ -45540,28 +45625,20 @@ function applyCommand(state, command, deps) {
45540
45625
  }
45541
45626
  });
45542
45627
  }
45543
- next.currentTurnSender = command.senderPrincipal;
45544
- const userEvent = {
45545
- kind: "user_text",
45546
- text: command.text,
45547
- uuid: deps.genUuid?.(),
45548
- sender: command.senderPrincipal,
45549
- // dispatch 来源分型(L2):daemon 自注入的消息在 command 上显式带分型值。
45550
- // history 侧同一条消息由 classifyUserMessage 从信封解出同一个值 —— 两路必须一致。
45551
- ...command.meta ? { meta: command.meta } : {}
45552
- };
45553
- const pushed = pushEventToBuffer(next, userEvent, deps);
45554
- effects.push(...pushed.effects);
45555
45628
  const procAliveBefore = state.procAlive;
45556
45629
  const readyBefore = state.readyForSend;
45557
45630
  const queueBefore = state.pendingSend.length;
45558
- if (deps.mode === "tui" && (next.file.tool ?? "claude") === "claude") {
45559
- pushed.state.pendingSend.push(command.text);
45560
- effects.push(...tryFlushPending(pushed.state, deps));
45561
- } else {
45562
- const payload = deps.encodeStdin(command.text, { sessionId });
45563
- effects.push({ kind: "write-stdin", payload });
45564
- }
45631
+ effects.push(
45632
+ ...deliverUserText(
45633
+ next,
45634
+ {
45635
+ text: command.text,
45636
+ sender: command.senderPrincipal,
45637
+ ...command.meta ? { meta: command.meta } : {}
45638
+ },
45639
+ deps
45640
+ )
45641
+ );
45565
45642
  rgDbg(deps, "send", {
45566
45643
  sid: sessionId,
45567
45644
  mode: deps.mode ?? "sdk",
@@ -45572,10 +45649,18 @@ function applyCommand(state, command, deps) {
45572
45649
  textLen: command.text.length,
45573
45650
  preview: command.text.slice(0, 40),
45574
45651
  wroteStdin: effects.some((e) => e.kind === "write-stdin"),
45575
- readyAfter: pushed.state.readyForSend,
45576
- queueAfter: pushed.state.pendingSend.length
45652
+ readyAfter: next.readyForSend,
45653
+ queueAfter: next.pendingSend.length
45577
45654
  });
45578
- return { state: pushed.state, effects };
45655
+ return { state: next, effects };
45656
+ }
45657
+ // 撤队列(spec 2026-08-31-chat-input-queue-design):带 id 删一条,不带 id 清空整队。
45658
+ // 中断也走这条(manager.interrupt 转发),"中断 = 队列作废"是老板拍的语义。
45659
+ case "dequeue": {
45660
+ if (next.queuedSends.length === 0) return { state: next, effects };
45661
+ next.queuedSends = command.id ? next.queuedSends.filter((q) => q.id !== command.id) : [];
45662
+ if (next.queuedSends.length !== state.queuedSends.length) effects.push(queueFrame(next));
45663
+ return { state: next, effects };
45579
45664
  }
45580
45665
  case "stop": {
45581
45666
  for (const toolUseId of Object.keys(state.pendingQuestions ?? {})) {
@@ -45591,6 +45676,7 @@ function applyCommand(state, command, deps) {
45591
45676
  next.pendingPermissions = {};
45592
45677
  next.pendingQuestions = {};
45593
45678
  resetReadyGate(next);
45679
+ clearQueuedSends(next, effects);
45594
45680
  if (next.procAlive) {
45595
45681
  next.status = "stopping";
45596
45682
  effects.push({ kind: "kill", signal: "SIGKILL" });
@@ -45636,6 +45722,7 @@ function applyCommand(state, command, deps) {
45636
45722
  next.pendingPermissions = {};
45637
45723
  next.pendingQuestions = {};
45638
45724
  resetReadyGate(next);
45725
+ clearQueuedSends(next, effects);
45639
45726
  if (next.procAlive) {
45640
45727
  effects.push({ kind: "kill", signal: "SIGKILL" });
45641
45728
  }
@@ -45700,6 +45787,7 @@ function applyCommand(state, command, deps) {
45700
45787
  });
45701
45788
  }
45702
45789
  resetReadyGate(next);
45790
+ clearQueuedSends(next, effects);
45703
45791
  if (next.procAlive) {
45704
45792
  effects.push({ kind: "kill", signal: "SIGKILL" });
45705
45793
  }
@@ -45737,6 +45825,7 @@ function applyCommand(state, command, deps) {
45737
45825
  next.freshSpawn = false;
45738
45826
  next.seenUuids = [];
45739
45827
  resetReadyGate(next);
45828
+ clearQueuedSends(next, effects);
45740
45829
  effects.push({ kind: "persist-file", file: nextFile });
45741
45830
  effects.push(sessionInfoFrame(nextFile));
45742
45831
  effects.push({
@@ -45793,6 +45882,8 @@ function reduceSessionInner(state, input, deps) {
45793
45882
  next.pendingBgTasks = [];
45794
45883
  const interruptEffects = next.turnOpen ? pushEventToBufferInPlace(next, { kind: "turn_end", reason: "interrupted" }, deps) : [];
45795
45884
  resetReadyGate(next);
45885
+ const queueEffects = [];
45886
+ clearQueuedSends(next, queueEffects);
45796
45887
  const sessionId = next.file.sessionId;
45797
45888
  const clearedEffects = Object.keys(state.pendingQuestions ?? {}).map(
45798
45889
  (toolUseId) => ({
@@ -45818,7 +45909,8 @@ function reduceSessionInner(state, input, deps) {
45818
45909
  exitCode: input.code
45819
45910
  }
45820
45911
  },
45821
- ...clearedEffects
45912
+ ...clearedEffects,
45913
+ ...queueEffects
45822
45914
  ]
45823
45915
  };
45824
45916
  }
@@ -45826,6 +45918,8 @@ function reduceSessionInner(state, input, deps) {
45826
45918
  const next = cloneState(state);
45827
45919
  next.status = "error";
45828
45920
  resetReadyGate(next);
45921
+ const queueEffects = [];
45922
+ clearQueuedSends(next, queueEffects);
45829
45923
  return {
45830
45924
  state: next,
45831
45925
  effects: [
@@ -45837,7 +45931,8 @@ function reduceSessionInner(state, input, deps) {
45837
45931
  status: next.status,
45838
45932
  message: input.message
45839
45933
  }
45840
- }
45934
+ },
45935
+ ...queueEffects
45841
45936
  ]
45842
45937
  };
45843
45938
  }
@@ -46640,6 +46735,8 @@ function makeInitialState(file, subSessionMeta) {
46640
46735
  // TUI 模式由 reducer send 分支按 deps.mode='tui' 主动翻 readyForSend=false
46641
46736
  readyForSend: true,
46642
46737
  pendingSend: [],
46738
+ // 输入框排队队列(spec 2026-08-31-chat-input-queue-design):初始空
46739
+ queuedSends: [],
46643
46740
  // per-turn sender 2026-07-08 spec §4.3:初始 null;case 'send' 首次覆盖
46644
46741
  currentTurnSender: null
46645
46742
  };
@@ -47488,12 +47585,34 @@ var SessionManager = class {
47488
47585
  const { broadcast } = this.withCollector(() => {
47489
47586
  runner.input({
47490
47587
  kind: "command",
47491
- command: { kind: "send", text: args.text, senderPrincipal }
47588
+ command: {
47589
+ kind: "send",
47590
+ text: args.text,
47591
+ senderPrincipal,
47592
+ // 排队与否由调用方显式声明(UI 输入框传 true;IM / dispatch 不传 = 直发)
47593
+ ...args.queueWhileRunning ? { queueWhileRunning: true } : {}
47594
+ }
47492
47595
  });
47493
47596
  });
47494
47597
  this.maybeGenerateTitle(existing, args.text);
47495
47598
  return { response: { ok: true }, broadcast };
47496
47599
  }
47600
+ /**
47601
+ * 撤排队消息(spec 2026-08-31-chat-input-queue-design):带 id 删一条,不带 id 清空整队。
47602
+ * session 没起过 runner = 没有队列,直接返 ok(幂等,不抛)。
47603
+ */
47604
+ dequeue(args) {
47605
+ this.getFile(args.sessionId);
47606
+ const runner = this.runners.get(args.sessionId);
47607
+ if (!runner) return { response: { ok: true }, broadcast: [] };
47608
+ const { broadcast } = this.withCollector(() => {
47609
+ runner.input({
47610
+ kind: "command",
47611
+ command: { kind: "dequeue", ...args.id ? { id: args.id } : {} }
47612
+ });
47613
+ });
47614
+ return { response: { ok: true }, broadcast };
47615
+ }
47497
47616
  /**
47498
47617
  * 首条 send 触发的智能标题自产(spec:PR #1196 追加拍板 2026-08-17)。
47499
47618
  * 背景见 tools/title-generator.ts 头注释——CC/codex 两条上游标题数据源都等不来
@@ -47639,17 +47758,27 @@ var SessionManager = class {
47639
47758
  `session not running: ${args.sessionId}`
47640
47759
  );
47641
47760
  }
47761
+ const queued = runner.getState().queuedSends.map((q) => ({
47762
+ id: q.id,
47763
+ text: q.text,
47764
+ queuedAt: q.queuedAt,
47765
+ ...q.sender.displayName ? { senderDisplayName: q.sender.displayName } : {}
47766
+ }));
47642
47767
  const tsid = runner.getState().file.toolSessionId;
47643
47768
  const pty = tsid ? this.ptyTransports.get(tsid) : void 0;
47644
47769
  if (pty) {
47645
- return this.stop({ sessionId: args.sessionId });
47770
+ const stopped = await this.stop({ sessionId: args.sessionId });
47771
+ return { ...stopped, response: { ...stopped.response, queued } };
47646
47772
  }
47773
+ const { broadcast } = this.withCollector(() => {
47774
+ runner.input({ kind: "command", command: { kind: "dequeue" } });
47775
+ });
47647
47776
  try {
47648
47777
  await runner.interrupt();
47649
47778
  } catch (err) {
47650
47779
  throw new ClawdError(ERROR_CODES.INTERNAL, err.message);
47651
47780
  }
47652
- return { response: { ok: true }, broadcast: [] };
47781
+ return { response: { ok: true, queued }, broadcast };
47653
47782
  }
47654
47783
  // 批量版本:UI 打开 session 时一次性拿到"磁盘和快照有差异"的 user message id 集合,
47655
47784
  // 用来在消息流里精准控制 rewind 按钮的显示。session 没 toolSessionId 时返回空
@@ -60908,8 +61037,15 @@ ${attribution}` } : args;
60908
61037
  const interrupt = async (frame, _client, ctx) => {
60909
61038
  const args = SessionIdArgs.parse(frame);
60910
61039
  ensureSessionAccess(ctx, args.sessionId, "send");
60911
- const { broadcast } = await manager.interrupt(args);
60912
- return { response: { type: "session:interrupt", ok: true }, broadcast };
61040
+ const { response, broadcast } = await manager.interrupt(args);
61041
+ const queued = response.queued ?? [];
61042
+ return { response: { type: "session:interrupt", ok: true, queued }, broadcast };
61043
+ };
61044
+ const dequeue = async (frame, _client, ctx) => {
61045
+ const args = SessionDequeueArgs.parse(frame);
61046
+ ensureSessionAccess(ctx, args.sessionId, "send");
61047
+ const { broadcast } = manager.dequeue(args);
61048
+ return { response: { type: "session:dequeue", ok: true }, broadcast };
60913
61049
  };
60914
61050
  const ensureRewindSupported = async (sessionId) => {
60915
61051
  const file = manager.get({ sessionId }).response;
@@ -61146,6 +61282,7 @@ ${attribution}` } : args;
61146
61282
  "session:update": update,
61147
61283
  "session:delete": del,
61148
61284
  "session:send": send,
61285
+ "session:dequeue": dequeue,
61149
61286
  "session:currentTurnSender": currentTurnSender,
61150
61287
  "session:stop": stop,
61151
61288
  "session:interrupt": interrupt,
@@ -62266,6 +62403,8 @@ var METHOD_GRANT_MAP = {
62266
62403
  "session:update": CAPABILITY_SCOPED,
62267
62404
  "session:delete": CAPABILITY_SCOPED,
62268
62405
  "session:send": CAPABILITY_SCOPED,
62406
+ // 撤排队消息:能发就能撤(spec 2026-08-31-chat-input-queue-design)
62407
+ "session:dequeue": CAPABILITY_SCOPED,
62269
62408
  // per-turn sender 2026-07-08 spec §4.6:dispatcher 层走 'public'(HTTP RPC path 需 dispatcher 放行);
62270
62409
  // WS path 的隐私边界(防 guest 猜 sid 枚举 owner feishuUnionId)由 handler 内 ensureSessionAccess
62271
62410
  // 显式兜底——见 handlers/session.ts:currentTurnSender。dispatcher 'public' ≠ handler 免检。
@@ -62495,7 +62634,7 @@ function computeMethodAccess(args) {
62495
62634
  }
62496
62635
 
62497
62636
  // src/version.ts
62498
- var version = "0.2.428".length > 0 ? "0.2.428" : "dev";
62637
+ var version = "0.2.430".length > 0 ? "0.2.430" : "dev";
62499
62638
 
62500
62639
  // src/cli-probe/probe.ts
62501
62640
  var fs65 = __toESM(require("fs"), 1);
@@ -22934,9 +22934,25 @@ var SessionUpdateArgs = external_exports.object({
22934
22934
  // 通过 sessionUpdate 修改这个字段。SessionFile 仍保留 appBuilderProject 字段供 daemon 内部 mutate。
22935
22935
  })
22936
22936
  });
22937
+ var QueuedMessage = external_exports.object({
22938
+ id: external_exports.string().min(1),
22939
+ text: external_exports.string(),
22940
+ queuedAt: external_exports.number(),
22941
+ senderDisplayName: external_exports.string().optional()
22942
+ });
22937
22943
  var SessionSendArgs = external_exports.object({
22938
22944
  sessionId: external_exports.string().min(1),
22939
- text: external_exports.string()
22945
+ text: external_exports.string(),
22946
+ /**
22947
+ * turn 在跑时是入队还是直发。**入队与否由调用方显式声明,daemon 不按来源猜**:
22948
+ * UI 输入框传 true;IM / 飞书 / dispatch 注入不传 = 维持原状直发(老板 2026-08-31:
22949
+ * 其他地方先不加队列)。缺省 false → 行为与本字段引入前逐字一致。
22950
+ */
22951
+ queueWhileRunning: external_exports.boolean().optional()
22952
+ });
22953
+ var SessionDequeueArgs = external_exports.object({
22954
+ sessionId: external_exports.string().min(1),
22955
+ id: external_exports.string().min(1).optional()
22940
22956
  });
22941
22957
  var SessionRewindArgs = external_exports.object({
22942
22958
  sessionId: external_exports.string().min(1),
@@ -23467,9 +23483,13 @@ var METHOD_DOCS = {
23467
23483
  args: SessionIdArgs
23468
23484
  },
23469
23485
  "session:send": {
23470
- summary: "\u5411\u4F1A\u8BDD\u53D1\u4E00\u6761\u7528\u6237\u6D88\u606F\uFF08\u89E6\u53D1\u65B0\u4E00\u8F6E turn\uFF09",
23486
+ summary: "\u5411\u4F1A\u8BDD\u53D1\u4E00\u6761\u7528\u6237\u6D88\u606F\uFF08\u89E6\u53D1\u65B0\u4E00\u8F6E turn\uFF09\uFF1BqueueWhileRunning=true \u4E14 turn \u5728\u8DD1\u65F6\u6539\u4E3A\u8FDB\u961F\u5217",
23471
23487
  args: SessionSendArgs
23472
23488
  },
23489
+ "session:dequeue": {
23490
+ summary: "\u64A4\u9500\u6392\u961F\u4E2D\u7684\u6D88\u606F\uFF1A\u5E26 id \u5220\u4E00\u6761\uFF0C\u4E0D\u5E26 id \u6E05\u7A7A\u6574\u961F\uFF08spec 2026-08-31-chat-input-queue-design\uFF09",
23491
+ args: SessionDequeueArgs
23492
+ },
23473
23493
  "session:currentTurnSender": {
23474
23494
  summary: "\u67E5\u5F53\u524D turn \u7684\u9A71\u52A8\u8005\uFF08owner / guest \u7684 SenderInfo\uFF09\u3002\u7701\u7565 sessionId \u6216\u4F20\u7A7A\u4E32 \u2192 \u76F4\u63A5\u8FD4 sender:null\uFF0C\u4E0D\u62A5\u9519",
23475
23495
  args: SessionCurrentTurnSenderArgs