@clawos-dev/clawd 0.2.249 → 0.2.250

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
@@ -6213,7 +6213,12 @@ var init_dispatch = __esm({
6213
6213
  })
6214
6214
  ]);
6215
6215
  DispatchRunArgsSchema = external_exports.object({
6216
- targetPersona: external_exports.string().min(1),
6216
+ // 新开必填;续派(dispatchId 非空)时可省略——targetPersonaId 从台账行取。
6217
+ targetPersona: external_exports.string().min(1).optional(),
6218
+ // 续派(spec 2026-07-21,单 dispatchId 语义:一件事一行一 id):非空 = 把 prompt
6219
+ // 作为新 turn 打进该 dispatch 的原 worker session 接着干(台账行复位 running)。
6220
+ // 省略 = 新开一件事,daemon 铸新 id。
6221
+ dispatchId: external_exports.string().min(1).optional(),
6217
6222
  prompt: external_exports.string(),
6218
6223
  // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
6219
6224
  // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
@@ -6223,6 +6228,10 @@ var init_dispatch = __esm({
6223
6228
  // tools/claude.ts CLAUDE_MODEL_IDS);handler 层做二次校验,非法值 fail-fast。
6224
6229
  // protocol 层保持 string 便于未来跨 tool 扩展(届时 union 收合法字面量)。
6225
6230
  model: external_exports.string().min(1).optional()
6231
+ }).refine((v2) => v2.targetPersona !== void 0 || v2.dispatchId !== void 0, {
6232
+ message: "either targetPersona (new dispatch) or dispatchId (follow-up) is required"
6233
+ }).refine((v2) => !(v2.dispatchId !== void 0 && v2.targetDeviceId !== void 0), {
6234
+ message: "cross-device dispatch cannot be continued (dispatchId + targetDeviceId are mutually exclusive)"
6226
6235
  });
6227
6236
  DispatchCompleteArgsSchema = external_exports.object({
6228
6237
  dispatchId: external_exports.string().min(1),
@@ -41926,6 +41935,10 @@ tool \u4F1A**\u7ACB\u5373\u8FD4\u56DE** dispatchId\uFF08\u5F02\u6B65\u59D4\u6D3E
41926
41935
  - \u4E00\u53E5\u8BDD\u91CC\u591A\u4E2A \`@persona/<id>\` \u65F6\u9009\u7B2C\u4E00\u4E2A\uFF08tool \u4E00\u6B21\u53EA\u5904\u7406\u4E00\u4E2A target\uFF09
41927
41936
  - \u59D4\u6D3E\u5931\u8D25\uFF08tool_result.is_error=true\uFF09\u65F6\u5982\u5B9E\u544A\u8BC9\u7528\u6237\u539F\u56E0\uFF0C**\u4E0D\u8981\u91CD\u8BD5**
41928
41937
 
41938
+ ### \u7EED\u6D3E\uFF08\u540C\u4E00\u4EF6\u4E8B\u7684\u540E\u7EED\u6307\u4EE4\uFF09
41939
+
41940
+ \u7528\u6237\u5BF9\u4E00\u4EF6**\u5DF2\u59D4\u6D3E**\u7684\u4E8B\u4E0B\u540E\u7EED\u6307\u4EE4\u65F6\uFF0C\u8C03 \`personaDispatch({ dispatchId: <\u8BE5\u4E8B\u7684 dispatchId>, prompt: <\u65B0\u6307\u4EE4> })\`\uFF08targetPersona \u53EF\u7701\u7565\uFF09\u2014\u2014\u65B0\u6307\u4EE4\u4F1A\u8FDB\u5165\u539F worker session \u63A5\u7740\u5E72\uFF0C\u4E0A\u4E0B\u6587\u4E0D\u4E22\u3002dispatchId \u7528\u4F60\u9996\u6B21\u59D4\u6D3E\u65F6\u62FF\u5230\u7684\u90A3\u4E2A\uFF1B\u4E0D\u786E\u5B9A\u662F\u54EA\u4EF6\u4E8B\u5C31\u5148\u8C03 \`personaDispatchList\` \u67E5\u3002
41941
+
41929
41942
  ### \u8DE8\u8BBE\u5907\u59D4\u6D3E\uFF08remote persona\uFF09
41930
41943
 
41931
41944
  \u5F53 @ token \u5F62\u5982 \`@persona/<id>@<deviceId>\`\uFF08\u7B2C\u4E8C\u6BB5\u662F\u8054\u7CFB\u4EBA**\u8BBE\u5907\u7684 deviceId**\uFF0CUI \u9009\u8054\u7CFB\u4EBA\u65F6\u5DF2\u89E3\u6790\u597D\u586B\u5165\uFF09\u65F6\uFF0C\u662F\u628A\u4EFB\u52A1\u59D4\u6D3E\u7ED9\u90A3\u53F0\u8BBE\u5907\u4E0A\u7684 persona\u3002\u9664 targetPersona / prompt \u5916\u518D\u5E26\u4E00\u4E2A\u53C2\u6570\uFF1A
@@ -44135,9 +44148,25 @@ var SessionManager = class {
44135
44148
  if (pending.length === 0) return;
44136
44149
  const state = this.tryGetState(sourceSessionId);
44137
44150
  if (state && compressStatus(state.status) === "running") return;
44151
+ if (!this.injectIntoSession(sourceSessionId, formatDeliveryText(pending))) return;
44152
+ const nowIso3 = new Date(this.deps.now?.() ?? Date.now()).toISOString();
44153
+ for (const r of pending) store.update(r.dispatchId, { deliveredAt: nowIso3 });
44154
+ this.deps.logger?.info("dispatch.deliver.ok", {
44155
+ sourceSessionId,
44156
+ count: pending.length,
44157
+ dispatchIds: pending.map((r) => r.dispatchId)
44158
+ });
44159
+ }
44160
+ /**
44161
+ * 把一段 plain text 以 owner sender 注入指定 session 当新 turn——dispatch 结果投递
44162
+ * (deliverPending)与续派任务包(handlers/persona-dispatch 经 sendFollowUp 闭包)共用。
44163
+ * session 文件不存在或 send 抛错 → false(记 log,不 throw)。直接 runner.input
44164
+ * (无 collector),frames 经 routeFromRunner 正常广播上 wire。
44165
+ */
44166
+ injectIntoSession(sessionId, text) {
44138
44167
  if (!this.deps.ownerPrincipalId) {
44139
- this.deps.logger?.warn("dispatch.deliver.no-owner-principal", { sourceSessionId });
44140
- return;
44168
+ this.deps.logger?.warn("dispatch.inject.no-owner-principal", { sessionId });
44169
+ return false;
44141
44170
  }
44142
44171
  const sender = principalToSender(
44143
44172
  makeOwnerPrincipal(
@@ -44147,34 +44176,24 @@ var SessionManager = class {
44147
44176
  )
44148
44177
  );
44149
44178
  try {
44150
- const file = this.findOwnedSession(sourceSessionId);
44179
+ const file = this.findOwnedSession(sessionId);
44151
44180
  if (!file) {
44152
- this.deps.logger?.info("dispatch.deliver.source-gone", { sourceSessionId });
44153
- return;
44181
+ this.deps.logger?.info("dispatch.inject.session-gone", { sessionId });
44182
+ return false;
44154
44183
  }
44155
44184
  const runner = this.ensureRunnerForScope(file, this.scopeForFile(file));
44156
44185
  runner.input({
44157
44186
  kind: "command",
44158
- command: {
44159
- kind: "send",
44160
- text: formatDeliveryText(pending),
44161
- senderPrincipal: sender
44162
- }
44187
+ command: { kind: "send", text, senderPrincipal: sender }
44163
44188
  });
44189
+ return true;
44164
44190
  } catch (err) {
44165
- this.deps.logger?.warn("dispatch.deliver.send-failed", {
44166
- sourceSessionId,
44191
+ this.deps.logger?.warn("dispatch.inject.send-failed", {
44192
+ sessionId,
44167
44193
  reason: err instanceof Error ? err.message : String(err)
44168
44194
  });
44169
- return;
44195
+ return false;
44170
44196
  }
44171
- const nowIso3 = new Date(this.deps.now?.() ?? Date.now()).toISOString();
44172
- for (const r of pending) store.update(r.dispatchId, { deliveredAt: nowIso3 });
44173
- this.deps.logger?.info("dispatch.deliver.ok", {
44174
- sourceSessionId,
44175
- count: pending.length,
44176
- dispatchIds: pending.map((r) => r.dispatchId)
44177
- });
44178
44197
  }
44179
44198
  /**
44180
44199
  * 只读拿 SessionState;session 不存在返 null(不 throw)。
@@ -48330,6 +48349,21 @@ var PersonaDispatchManager = class {
48330
48349
  state.waiters.push(d);
48331
48350
  return d.promise;
48332
48351
  }
48352
+ /**
48353
+ * 续派(spec 2026-07-21):complete 已清掉 map 条目,续派前重建——bSessionId 映射
48354
+ * 保证 worker 进程死后 respawn 时 reducer lookupDispatchByBSessionId 仍注回同一
48355
+ * CLAWD_DISPATCH_ID env。台账行的复位由 handler 直接走 store(本方法只管内存态)。
48356
+ */
48357
+ reopen(dispatchId, args) {
48358
+ const existing = this.map.get(dispatchId);
48359
+ this.map.set(dispatchId, {
48360
+ dispatchId,
48361
+ sourceSessionId: args.sourceSessionId,
48362
+ targetPersona: args.targetPersona,
48363
+ bSessionId: args.workerSessionId,
48364
+ waiters: existing?.waiters ?? []
48365
+ });
48366
+ }
48333
48367
  /** SessionManager 在 createDispatchedSession 时调,建立 dispatchId → B sessionId 映射 */
48334
48368
  registerBSession(dispatchId, bSessionId) {
48335
48369
  const state = this.map.get(dispatchId);
@@ -49063,6 +49097,17 @@ function canAccessPersona(grants, personaId, action) {
49063
49097
 
49064
49098
  // src/handlers/persona-dispatch.ts
49065
49099
  init_claude();
49100
+ function buildFollowUpTaskPack(dispatchId, prompt) {
49101
+ return [
49102
+ `[Follow-up instruction for dispatch ${dispatchId}]`,
49103
+ "",
49104
+ `Task: ${prompt}`,
49105
+ "",
49106
+ `This is a follow-up turn in the same dispatched task (dispatchId=${dispatchId}).`,
49107
+ "When you finish this round, report back by calling personaDispatchComplete again,",
49108
+ "exactly as you did before."
49109
+ ].join("\n");
49110
+ }
49066
49111
  function buildPersonaDispatchHandlers(deps) {
49067
49112
  const { personaDispatchManager: mgr, spawnB, logger } = deps;
49068
49113
  const run = async (frame, _client, ctx) => {
@@ -49075,6 +49120,76 @@ function buildPersonaDispatchHandlers(deps) {
49075
49120
  `model '${args.model}' not supported; allowed: ${CLAUDE_MODEL_IDS.join(", ")}`
49076
49121
  );
49077
49122
  }
49123
+ if (args.dispatchId) {
49124
+ if (ctx?.principal.kind === "guest") {
49125
+ throw new ClawdError(
49126
+ ERROR_CODES.UNAUTHORIZED,
49127
+ "follow-up dispatch cannot be initiated by a guest"
49128
+ );
49129
+ }
49130
+ if (!sourceSessionId) {
49131
+ throw new Error(
49132
+ "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49133
+ );
49134
+ }
49135
+ const rec3 = deps.store.get(args.dispatchId);
49136
+ if (!rec3) {
49137
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, `dispatch not found: ${args.dispatchId}`);
49138
+ }
49139
+ if (!rec3.workerSessionId) {
49140
+ throw new ClawdError(
49141
+ ERROR_CODES.INVALID_PARAM,
49142
+ `dispatch ${args.dispatchId} has no worker session and cannot be continued (cross-device or spawn incomplete)`
49143
+ );
49144
+ }
49145
+ if (args.targetPersona && args.targetPersona !== rec3.targetPersonaId) {
49146
+ throw new ClawdError(
49147
+ ERROR_CODES.INVALID_PARAM,
49148
+ `targetPersona mismatch: dispatch ${args.dispatchId} belongs to ${rec3.targetPersonaId}`
49149
+ );
49150
+ }
49151
+ if (!deps.sendFollowUp) {
49152
+ throw new Error("follow-up dispatch not wired (sendFollowUp missing)");
49153
+ }
49154
+ deps.store.update(args.dispatchId, {
49155
+ status: "running",
49156
+ outcome: void 0,
49157
+ completedAt: void 0,
49158
+ deliveredAt: void 0,
49159
+ taskText: args.prompt.slice(0, TASK_TEXT_MAX)
49160
+ });
49161
+ mgr.reopen(args.dispatchId, {
49162
+ sourceSessionId,
49163
+ targetPersona: rec3.targetPersonaId,
49164
+ workerSessionId: rec3.workerSessionId
49165
+ });
49166
+ logger?.info("dispatch.run.follow-up", {
49167
+ dispatchId: args.dispatchId,
49168
+ workerSessionId: rec3.workerSessionId,
49169
+ promptLen: args.prompt.length
49170
+ });
49171
+ const ok = deps.sendFollowUp(
49172
+ rec3.workerSessionId,
49173
+ buildFollowUpTaskPack(args.dispatchId, args.prompt)
49174
+ );
49175
+ if (!ok) {
49176
+ mgr.complete(args.dispatchId, {
49177
+ kind: "failure",
49178
+ reason: "worker session unavailable for follow-up"
49179
+ });
49180
+ throw new ClawdError(
49181
+ ERROR_CODES.SESSION_NOT_FOUND,
49182
+ `worker session unavailable; follow-up failed for dispatch ${args.dispatchId}`
49183
+ );
49184
+ }
49185
+ return {
49186
+ response: { type: "personaDispatch:run:ok", dispatchId: args.dispatchId }
49187
+ };
49188
+ }
49189
+ const targetPersona = args.targetPersona;
49190
+ if (!targetPersona) {
49191
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, "targetPersona is required for a new dispatch");
49192
+ }
49078
49193
  if (args.targetDeviceId) {
49079
49194
  if (ctx?.principal.kind === "guest") {
49080
49195
  throw new ClawdError(
@@ -49092,17 +49207,17 @@ function buildPersonaDispatchHandlers(deps) {
49092
49207
  }
49093
49208
  const { dispatchId: dispatchId2 } = mgr.start({
49094
49209
  sourceSessionId,
49095
- targetPersona: args.targetPersona,
49210
+ targetPersona,
49096
49211
  taskText: args.prompt
49097
49212
  });
49098
49213
  logger?.info("dispatch.run.forward", {
49099
49214
  dispatchId: dispatchId2,
49100
49215
  targetDeviceId: args.targetDeviceId,
49101
- targetPersona: args.targetPersona
49216
+ targetPersona
49102
49217
  });
49103
49218
  void deps.forwardToPeer({
49104
49219
  targetDeviceId: args.targetDeviceId,
49105
- targetPersona: args.targetPersona,
49220
+ targetPersona,
49106
49221
  prompt: args.prompt,
49107
49222
  ...args.model ? { model: args.model } : {}
49108
49223
  }).then((outcome) => mgr.complete(dispatchId2, outcome)).catch((err) => {
@@ -49115,28 +49230,28 @@ function buildPersonaDispatchHandlers(deps) {
49115
49230
  };
49116
49231
  }
49117
49232
  if (ctx?.principal.kind === "guest") {
49118
- const isPublic = deps.getPersonaPublic?.(args.targetPersona) ?? false;
49119
- if (!isPublic || !canAccessPersona(ctx.grants, args.targetPersona, "send")) {
49233
+ const isPublic = deps.getPersonaPublic?.(targetPersona) ?? false;
49234
+ if (!isPublic || !canAccessPersona(ctx.grants, targetPersona, "send")) {
49120
49235
  throw new ClawdError(
49121
49236
  ERROR_CODES.UNAUTHORIZED,
49122
- `persona not dispatchable: ${args.targetPersona}`
49237
+ `persona not dispatchable: ${targetPersona}`
49123
49238
  );
49124
49239
  }
49125
49240
  const { dispatchId: dispatchId2 } = mgr.start({
49126
49241
  sourceSessionId: ctx.principal.id,
49127
- targetPersona: args.targetPersona,
49242
+ targetPersona,
49128
49243
  taskText: args.prompt
49129
49244
  });
49130
49245
  logger?.info("dispatch.run.received.guest", {
49131
49246
  dispatchId: dispatchId2,
49132
49247
  sourcePrincipal: ctx.principal.id,
49133
- targetPersona: args.targetPersona,
49248
+ targetPersona,
49134
49249
  promptLen: args.prompt.length
49135
49250
  });
49136
49251
  void spawnB({
49137
49252
  dispatchId: dispatchId2,
49138
49253
  sourceSessionId: ctx.principal.id,
49139
- targetPersona: args.targetPersona,
49254
+ targetPersona,
49140
49255
  prompt: args.prompt,
49141
49256
  guestPrincipalId: ctx.principal.id,
49142
49257
  guestDisplayName: ctx.principal.displayName,
@@ -49163,19 +49278,19 @@ function buildPersonaDispatchHandlers(deps) {
49163
49278
  }
49164
49279
  const { dispatchId } = mgr.start({
49165
49280
  sourceSessionId,
49166
- targetPersona: args.targetPersona,
49281
+ targetPersona,
49167
49282
  taskText: args.prompt
49168
49283
  });
49169
49284
  logger?.info("dispatch.run.received", {
49170
49285
  dispatchId,
49171
49286
  sourceSessionId,
49172
- targetPersona: args.targetPersona,
49287
+ targetPersona,
49173
49288
  promptLen: args.prompt.length
49174
49289
  });
49175
49290
  void spawnB({
49176
49291
  dispatchId,
49177
49292
  sourceSessionId,
49178
- targetPersona: args.targetPersona,
49293
+ targetPersona,
49179
49294
  prompt: args.prompt,
49180
49295
  ...args.model ? { model: args.model } : {}
49181
49296
  }).then(() => {
@@ -50707,6 +50822,9 @@ var EXPOSED_RPC_METHODS = [
50707
50822
  "peerExec.run",
50708
50823
  "personaDispatch.complete",
50709
50824
  "personaDispatch.list",
50825
+ // persona 清单(spec 2026-07-21):两级路由第一级——发起方 cc(loopback owner 语义)
50826
+ // 用 curl 拿全量 personaId+label 紧凑清单,再按需读候选 CLAUDE.md 首段做终选。
50827
+ "persona.list",
50710
50828
  // clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
50711
50829
  // 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
50712
50830
  // (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
@@ -59610,6 +59728,8 @@ async function startDaemon(config) {
59610
59728
  const dispatchHandlers = buildPersonaDispatchHandlers({
59611
59729
  personaDispatchManager,
59612
59730
  store: dispatchStore,
59731
+ // 续派(spec 2026-07-21):任务包注入 worker session,复用投递同款注入路径。
59732
+ sendFollowUp: (workerSessionId, text) => manager.injectIntoSession(workerSessionId, text),
59613
59733
  logger,
59614
59734
  spawnB: async (args) => {
59615
59735
  logger.info("dispatch.spawnB.start", {
@@ -22708,7 +22708,12 @@ var DispatchOutcomeSchema = external_exports.discriminatedUnion("kind", [
22708
22708
  })
22709
22709
  ]);
22710
22710
  var DispatchRunArgsSchema = external_exports.object({
22711
- targetPersona: external_exports.string().min(1),
22711
+ // 新开必填;续派(dispatchId 非空)时可省略——targetPersonaId 从台账行取。
22712
+ targetPersona: external_exports.string().min(1).optional(),
22713
+ // 续派(spec 2026-07-21,单 dispatchId 语义:一件事一行一 id):非空 = 把 prompt
22714
+ // 作为新 turn 打进该 dispatch 的原 worker session 接着干(台账行复位 running)。
22715
+ // 省略 = 新开一件事,daemon 铸新 id。
22716
+ dispatchId: external_exports.string().min(1).optional(),
22712
22717
  prompt: external_exports.string(),
22713
22718
  // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
22714
22719
  // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
@@ -22718,6 +22723,10 @@ var DispatchRunArgsSchema = external_exports.object({
22718
22723
  // tools/claude.ts CLAUDE_MODEL_IDS);handler 层做二次校验,非法值 fail-fast。
22719
22724
  // protocol 层保持 string 便于未来跨 tool 扩展(届时 union 收合法字面量)。
22720
22725
  model: external_exports.string().min(1).optional()
22726
+ }).refine((v) => v.targetPersona !== void 0 || v.dispatchId !== void 0, {
22727
+ message: "either targetPersona (new dispatch) or dispatchId (follow-up) is required"
22728
+ }).refine((v) => !(v.dispatchId !== void 0 && v.targetDeviceId !== void 0), {
22729
+ message: "cross-device dispatch cannot be continued (dispatchId + targetDeviceId are mutually exclusive)"
22721
22730
  });
22722
22731
  var DispatchCompleteArgsSchema = external_exports.object({
22723
22732
  dispatchId: external_exports.string().min(1),
@@ -23425,6 +23434,7 @@ async function handlePersonaDispatchToolCall(input, ctx) {
23425
23434
  const f = ctx.fetch ?? fetch;
23426
23435
  log("personaDispatch tool entered", {
23427
23436
  targetPersona: input.targetPersona,
23437
+ dispatchId: input.dispatchId,
23428
23438
  promptLen: input.prompt.length,
23429
23439
  sessionId: ctx.sessionId,
23430
23440
  daemonUrl: ctx.daemonUrl
@@ -23452,8 +23462,11 @@ async function handlePersonaDispatchToolCall(input, ctx) {
23452
23462
  "x-clawd-session-id": ctx.sessionId
23453
23463
  },
23454
23464
  body: JSON.stringify({
23455
- targetPersona: input.targetPersona,
23465
+ // 续派时可省 targetPersona(daemon 从台账取)
23466
+ ...input.targetPersona ? { targetPersona: input.targetPersona } : {},
23456
23467
  prompt: input.prompt,
23468
+ // 续派(spec 2026-07-21):非空 = 把 prompt 打进该 dispatch 的原 worker session。
23469
+ ...input.dispatchId ? { dispatchId: input.dispatchId } : {},
23457
23470
  // 跨设备:非空时透传,daemon run handler 据此走 A 角色转发分支。
23458
23471
  ...input.targetDeviceId ? { targetDeviceId: input.targetDeviceId } : {},
23459
23472
  // T-20: 可选 override B session 使用的 claude 模型;MCP tool 层已 enum 校验,
@@ -23487,12 +23500,13 @@ async function handlePersonaDispatchToolCall(input, ctx) {
23487
23500
  };
23488
23501
  }
23489
23502
  const dispatchId = json.result.dispatchId;
23490
- log("personaDispatch submitted", { dispatchId });
23503
+ log("personaDispatch submitted", { dispatchId, followUp: Boolean(input.dispatchId) });
23504
+ const head = input.dispatchId ? `Follow-up sent for dispatch ${dispatchId} \u2014 the new instruction was delivered into the same worker session.` : `Task dispatched to ${input.targetPersona} (dispatchId=${dispatchId}).`;
23491
23505
  return {
23492
23506
  content: [
23493
23507
  {
23494
23508
  type: "text",
23495
- text: `Task dispatched to ${input.targetPersona} (dispatchId=${dispatchId}). The result will be delivered back into this session when the target persona finishes \u2014 you do NOT need to wait or poll. Tell the user the task has been delegated and end your turn. Use personaDispatchList only if the user later asks about progress.`
23509
+ text: `${head} The result will be delivered back into this session when the target persona finishes \u2014 you do NOT need to wait or poll. Tell the user the task has been delegated and end your turn. Use personaDispatchList only if the user later asks about progress.`
23496
23510
  }
23497
23511
  ]
23498
23512
  };
@@ -23619,9 +23633,14 @@ async function main() {
23619
23633
  "personaDispatch",
23620
23634
  {
23621
23635
  title: "Dispatch task to another persona",
23622
- description: "Delegate a task to another clawd persona. Use this when the user message contains `@persona/<id>` to hand the task off to that persona. Returns immediately with a dispatchId; the result will be delivered back into your session automatically when the target persona finishes \u2014 do not wait or poll. Use personaDispatchList to check progress if asked.",
23636
+ description: "Delegate a task to another clawd persona. Use this when the user message contains `@persona/<id>` to hand the task off to that persona. Returns immediately with a dispatchId; the result will be delivered back into your session automatically when the target persona finishes \u2014 do not wait or poll. To continue an existing dispatched task (follow-up instruction into the same worker session), pass its dispatchId and omit targetPersona. Use personaDispatchList to check progress if asked.",
23623
23637
  inputSchema: {
23624
- targetPersona: external_exports.string().describe("persona id (e.g. persona-app-builder)"),
23638
+ targetPersona: external_exports.string().optional().describe(
23639
+ "persona id (e.g. persona-app-builder); omit when continuing an existing dispatch via dispatchId"
23640
+ ),
23641
+ dispatchId: external_exports.string().optional().describe(
23642
+ "continue an existing dispatched task: the prompt is delivered into the same worker session as a new turn (context preserved); model cannot be changed on follow-up (the worker session keeps its original model)"
23643
+ ),
23625
23644
  prompt: external_exports.string().describe(
23626
23645
  "the task to perform, with the @persona/<id> token stripped out"
23627
23646
  ),