@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
|
-
|
|
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.
|
|
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(
|
|
44179
|
+
const file = this.findOwnedSession(sessionId);
|
|
44151
44180
|
if (!file) {
|
|
44152
|
-
this.deps.logger?.info("dispatch.
|
|
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.
|
|
44166
|
-
|
|
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
|
|
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
|
|
49216
|
+
targetPersona
|
|
49102
49217
|
});
|
|
49103
49218
|
void deps.forwardToPeer({
|
|
49104
49219
|
targetDeviceId: args.targetDeviceId,
|
|
49105
|
-
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?.(
|
|
49119
|
-
if (!isPublic || !canAccessPersona(ctx.grants,
|
|
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: ${
|
|
49237
|
+
`persona not dispatchable: ${targetPersona}`
|
|
49123
49238
|
);
|
|
49124
49239
|
}
|
|
49125
49240
|
const { dispatchId: dispatchId2 } = mgr.start({
|
|
49126
49241
|
sourceSessionId: ctx.principal.id,
|
|
49127
|
-
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
|
|
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
|
|
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
|
|
49281
|
+
targetPersona,
|
|
49167
49282
|
taskText: args.prompt
|
|
49168
49283
|
});
|
|
49169
49284
|
logger?.info("dispatch.run.received", {
|
|
49170
49285
|
dispatchId,
|
|
49171
49286
|
sourceSessionId,
|
|
49172
|
-
targetPersona
|
|
49287
|
+
targetPersona,
|
|
49173
49288
|
promptLen: args.prompt.length
|
|
49174
49289
|
});
|
|
49175
49290
|
void spawnB({
|
|
49176
49291
|
dispatchId,
|
|
49177
49292
|
sourceSessionId,
|
|
49178
|
-
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
|
-
|
|
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
|
|
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:
|
|
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(
|
|
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
|
),
|