@clawos-dev/clawd 0.2.739 → 0.2.741

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.
@@ -32182,98 +32182,110 @@ var ContactPinnedFrameSchema = external_exports.object({
32182
32182
  pinnedAt: external_exports.number().int().nullable()
32183
32183
  }).strict();
32184
32184
 
32185
- // ../protocol/src/dispatch.ts
32186
- var DispatchOutcomeSchema = external_exports.discriminatedUnion("kind", [
32187
- external_exports.object({
32188
- kind: external_exports.literal("success"),
32189
- text: external_exports.string(),
32190
- brief: external_exports.string().optional(),
32191
- filePaths: external_exports.array(external_exports.string()).optional()
32185
+ // ../protocol/src/a2a.ts
32186
+ var A2A_MAX_CALLS = 8;
32187
+ var A2A_WAIT_MAX_MS = 2 * 60 * 6e4;
32188
+ var A2A_WAIT_DEFAULT_MS = 30 * 6e4;
32189
+ var A2A_STATES = ["submitted", "working", "input_required", "completed", "failed", "canceled", "rejected"];
32190
+ var A2aStateSchema = external_exports.enum(A2A_STATES);
32191
+ var A2A_TERMINAL_STATES = ["completed", "failed", "canceled", "rejected"];
32192
+ var A2A_DELIVERABLE_STATES = ["input_required", ...A2A_TERMINAL_STATES];
32193
+ var TENANT_DEVICE = /^[A-Za-z0-9_.-]{1,128}$/;
32194
+ var TENANT_PERSONA = /^[a-z0-9-]{1,128}$/;
32195
+ function parseA2aTarget(target) {
32196
+ if (target.startsWith("cloud:")) {
32197
+ const id = target.slice("cloud:".length);
32198
+ return TENANT_PERSONA.test(id) ? { kind: "cloud", personaId: id } : null;
32199
+ }
32200
+ const at = target.indexOf("@");
32201
+ if (at < 0) return TENANT_PERSONA.test(target) ? { kind: "here", personaId: target } : null;
32202
+ const personaId2 = target.slice(0, at);
32203
+ const deviceId = target.slice(at + 1);
32204
+ return TENANT_PERSONA.test(personaId2) && TENANT_DEVICE.test(deviceId) ? { kind: "device", personaId: personaId2, deviceId } : null;
32205
+ }
32206
+ var A2aCallSchema = external_exports.object({
32207
+ target: external_exports.string().refine((v) => parseA2aTarget(v) !== null, {
32208
+ message: "target must be <personaId>, <personaId>@<deviceId> or cloud:<personaId>"
32192
32209
  }),
32193
- external_exports.object({
32194
- kind: external_exports.literal("failure"),
32195
- reason: external_exports.string(),
32196
- brief: external_exports.string().optional()
32197
- })
32198
- ]);
32199
- var DispatchRunArgsSchema = external_exports.object({
32200
- targetPersona: external_exports.string().min(1).optional(),
32201
- // 续派(spec 2026-07-21,单 dispatchId 语义:一件事一行一 id):非空 = 把 prompt
32202
- // 作为新 turn 打进该 dispatch 的原 worker session 接着干(台账行复位 running)。
32203
- // 省略 = 新开一件事,daemon 铸新 id。
32204
- dispatchId: external_exports.string().min(1).optional(),
32205
- prompt: external_exports.string(),
32210
+ text: external_exports.string().min(1),
32206
32211
  brief: external_exports.string().optional(),
32207
- // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
32208
- // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
32209
- targetDeviceId: external_exports.string().min(1).optional(),
32210
- // Model values depend on the selected engine and are validated against its runtime catalog. Overrides apply only to new dispatches.
32212
+ contextId: external_exports.string().min(1).optional(),
32213
+ taskId: external_exports.string().min(1).optional(),
32214
+ sessionLabel: external_exports.string().min(1).max(60).optional(),
32211
32215
  model: external_exports.string().min(1).optional(),
32212
- // One-time engine override for the worker session; it does not change the persona binding. Validate against registered adapters at runtime.
32216
+ /** 一次性换 worker 的引擎;只对本人设备上的本机目标有效,执行方领活时校验 */
32213
32217
  tool: external_exports.string().min(1).optional(),
32214
- // Worker label applies to new dispatches only. Follow-ups tolerate this field but do not update the running session label.
32215
- sessionLabel: external_exports.string().min(1).max(60).optional(),
32216
- // 上层消费者(Master)挂载话题/线语义:topicId / lineId / 验收标准 / rejectCount 等。
32217
- // daemon **不解释**内容,只透传进台账行 DispatchRecord.meta(新开)或与既有 meta
32218
- // merge(续派,见 handlers/persona-dispatch.ts)。与台账行 meta 同款自由 JSON 契约。
32218
+ /** 线的自由字段(topicId / lineId / acceptance …),中心与 daemon 都不解释,原样进台账 */
32219
32219
  meta: external_exports.record(external_exports.unknown()).optional()
32220
- }).refine((v) => v.targetPersona !== void 0 || v.dispatchId !== void 0, {
32221
- message: "either targetPersona (new dispatch) or dispatchId (follow-up) is required"
32222
- }).refine((v) => !(v.dispatchId !== void 0 && v.targetDeviceId !== void 0), {
32223
- message: "follow-ups take only dispatchId; the target device comes from the ledger row (dispatchId + targetDeviceId are mutually exclusive)"
32224
- }).refine((v) => !(v.tool !== void 0 && v.targetDeviceId !== void 0), {
32225
- message: "engine override is not supported for cross-device dispatch (tool + targetDeviceId are mutually exclusive)"
32226
- }).refine((v) => !(v.dispatchId !== void 0 && (v.tool !== void 0 || v.model !== void 0)), {
32227
- message: "a follow-up dispatch cannot change engine or model (dispatchId excludes tool / model); start a new dispatch instead"
32228
- });
32229
- var DispatchCompleteArgsSchema = external_exports.object({
32230
- dispatchId: external_exports.string().min(1),
32231
- outcome: DispatchOutcomeSchema
32232
- });
32233
- var DISPATCH_STATUSES = ["running", "completed", "failed"];
32234
- var DispatchStatusSchema = external_exports.enum(DISPATCH_STATUSES);
32235
- var DispatchRecordSchema = external_exports.object({
32236
- dispatchId: external_exports.string().min(1),
32237
- sourceSessionId: external_exports.string().min(1),
32238
- sourcePersonaId: external_exports.string().min(1).optional(),
32239
- targetPersonaId: external_exports.string().min(1),
32240
- /** A-side target device attribution; absence means local for existing records. Forwarding strips it from the B-side local ledger. */
32241
- targetDeviceId: external_exports.string().min(1).optional(),
32242
- /** A-side cross-device rows: the B-side ledger id, used for follow-ups and for resuming polls after an A restart. */
32243
- remoteDispatchId: external_exports.string().min(1).optional(),
32244
- /** The A-side round B last accepted; a follow-up reopened but not yet accepted has remoteRound < round, so a restart must not resume polling it. */
32245
- remoteRound: external_exports.number().int().positive().optional(),
32246
- /** B 的 sessionId;registerBSession 后回填 */
32247
- workerSessionId: external_exports.string().min(1).optional(),
32248
- taskText: external_exports.string(),
32249
- /** Explicit task summary for list/cards instead of truncated taskText; outcome owns the result summary. */
32250
- taskBrief: external_exports.string().optional(),
32251
- /** One ledger row across follow-up rounds. Missing on old rows is consumed as round one. */
32252
- round: external_exports.number().int().positive().optional(),
32253
- status: DispatchStatusSchema,
32254
- outcome: DispatchOutcomeSchema.optional(),
32255
- meta: external_exports.record(external_exports.unknown()).optional(),
32256
- createdAt: external_exports.string().min(1),
32257
- completedAt: external_exports.string().min(1).optional(),
32258
- /** 结果注入 source session 成功时戳,兼当投递 dedup 标记 */
32259
- deliveredAt: external_exports.string().min(1).optional()
32260
- });
32261
- var DispatchListArgsSchema = external_exports.object({
32262
- sourceSessionId: external_exports.string().min(1).optional(),
32263
- /** Include rows where this session is either source or worker. If sourceSessionId is also provided, both filters must match. */
32264
- relatedSessionId: external_exports.string().min(1).optional()
32220
+ }).refine((v) => !(v.contextId && v.taskId), { message: "contextId (new round) and taskId (answer) are mutually exclusive" }).refine((v) => !((v.contextId || v.taskId) && (v.model || v.tool)), {
32221
+ message: "a follow-up cannot change engine or model (contextId / taskId exclude model / tool); start a new line instead"
32265
32222
  });
32266
- var DispatchListResultSchema = external_exports.object({
32267
- records: external_exports.array(DispatchRecordSchema)
32223
+ var A2aSendArgsSchema = external_exports.object({
32224
+ calls: external_exports.array(A2aCallSchema).min(1).max(A2A_MAX_CALLS),
32225
+ sessionId: external_exports.string().min(1).optional()
32268
32226
  });
32269
- var DispatchGetArgsSchema = external_exports.object({
32270
- dispatchId: external_exports.string().min(1)
32227
+ var A2A_AWAIT_SLICE_MAX_MS = 6e4;
32228
+ var A2aAwaitArgsSchema = external_exports.object({
32229
+ taskIds: external_exports.array(external_exports.string().min(1)).min(1).max(A2A_MAX_CALLS),
32230
+ timeoutMs: external_exports.number().int().min(0).max(A2A_AWAIT_SLICE_MAX_MS)
32231
+ });
32232
+ var A2aAnswerArgsSchema = external_exports.object({ taskId: external_exports.string().min(1), text: external_exports.string().trim().min(1) });
32233
+ var A2aTaskIdArgsSchema = external_exports.object({ taskId: external_exports.string().min(1) });
32234
+ var A2aListArgsSchema = external_exports.object({
32235
+ contextId: external_exports.string().min(1).optional(),
32236
+ state: A2aStateSchema.optional(),
32237
+ /** 发起会话(Master 按自己的会话列线);agent 调用时 MCP 用调用者会话填 */
32238
+ sessionId: external_exports.string().min(1).optional(),
32239
+ /** UI:这个会话是发起方或 worker 的任务 */
32240
+ relatedSessionId: external_exports.string().min(1).optional()
32271
32241
  });
32272
- var DispatchGetOkSchema = external_exports.object({
32273
- type: external_exports.literal("personaDispatch:get:ok"),
32274
- status: DispatchStatusSchema,
32275
- outcome: DispatchOutcomeSchema.optional()
32242
+ var A2aReportArgsSchema = external_exports.object({
32243
+ contextId: external_exports.string().min(1),
32244
+ state: external_exports.enum(["completed", "failed", "input_required"]),
32245
+ text: external_exports.string(),
32246
+ brief: external_exports.string().optional(),
32247
+ filePaths: external_exports.array(external_exports.string().min(1)).optional()
32248
+ });
32249
+ var A2aTaskViewSchema = external_exports.object({
32250
+ taskId: external_exports.string().min(1),
32251
+ contextId: external_exports.string().min(1),
32252
+ round: external_exports.number().int().positive(),
32253
+ state: A2aStateSchema,
32254
+ /** 本机执行方失联(租约过期但不判失败) */
32255
+ stale: external_exports.boolean(),
32256
+ /** 系统原因:no_executor / executor_lost / executor_restarted / not_authorized … */
32257
+ reason: external_exports.string().nullable(),
32258
+ depth: external_exports.number().int().min(0),
32259
+ tenant: external_exports.string(),
32260
+ targetPersonaId: external_exports.string(),
32261
+ targetDeviceId: external_exports.string().nullable(),
32262
+ initiator: external_exports.object({ host: external_exports.string(), personaId: external_exports.string(), sessionRef: external_exports.string().nullable() }),
32263
+ executorSessionRef: external_exports.string().nullable(),
32264
+ executorThreadId: external_exports.string().nullable(),
32265
+ /** 这条线在本机的 worker 会话:本机执行的,或发起方 daemon 挂出的云 worker 会话;本机没有就缺省 */
32266
+ workerSessionId: external_exports.string().optional(),
32267
+ /** 派活方给 worker 会话起的名字(首条 user 消息的 sessionLabel) */
32268
+ sessionLabel: external_exports.string().optional(),
32269
+ /** 任务正文(首条 user 消息) */
32270
+ taskText: external_exports.string(),
32271
+ /** worker 的一句话摘要(statusMessage.brief) */
32272
+ brief: external_exports.string().nullable(),
32273
+ /** worker 最后一条回报正文 */
32274
+ resultText: external_exports.string().nullable(),
32275
+ filePaths: external_exports.array(external_exports.string()),
32276
+ line: external_exports.record(external_exports.unknown()),
32277
+ deliveredAt: external_exports.string().nullable(),
32278
+ createdAt: external_exports.string(),
32279
+ updatedAt: external_exports.string(),
32280
+ finishedAt: external_exports.string().nullable()
32276
32281
  });
32282
+ var A2aCallResultSchema = external_exports.discriminatedUnion("ok", [
32283
+ external_exports.object({ ok: external_exports.literal(true), task: A2aTaskViewSchema }),
32284
+ external_exports.object({ ok: external_exports.literal(false), error: external_exports.object({ code: external_exports.string(), message: external_exports.string() }) })
32285
+ ]);
32286
+ var A2aSendResultSchema = external_exports.object({ results: external_exports.array(A2aCallResultSchema) });
32287
+ var A2aListResultSchema = external_exports.object({ tasks: external_exports.array(A2aTaskViewSchema) });
32288
+ var A2aAwaitResultSchema = external_exports.object({ tasks: external_exports.array(A2aTaskViewSchema), done: external_exports.boolean() });
32277
32289
 
32278
32290
  // ../protocol/src/extension.ts
32279
32291
  var EXT_ID_REGEX = /^[a-z0-9][a-z0-9-]*$/;
@@ -32652,11 +32664,14 @@ var TopicSchema = external_exports.object({
32652
32664
  });
32653
32665
  var TopicLineViewSchema = external_exports.object({
32654
32666
  lineId: external_exports.string().min(1),
32655
- dispatchId: external_exports.string().min(1),
32667
+ /** 这条线(A2A context);打回在同一 context 下开新任务 */
32668
+ contextId: external_exports.string().min(1),
32669
+ /** 最新一轮的任务 */
32670
+ taskId: external_exports.string().min(1),
32656
32671
  workerSessionId: external_exports.string().optional(),
32657
32672
  assignee: external_exports.string().min(1),
32658
32673
  focus: external_exports.string(),
32659
- status: DispatchStatusSchema,
32674
+ status: A2aStateSchema,
32660
32675
  acceptance: external_exports.array(external_exports.string()),
32661
32676
  rejectCount: external_exports.number().int().min(0),
32662
32677
  accepted: external_exports.boolean(),
@@ -32887,6 +32902,37 @@ var OnboardingIosInterestResultSchema = external_exports.object({
32887
32902
  registered: external_exports.boolean()
32888
32903
  });
32889
32904
 
32905
+ // ../protocol/src/faq-schemas.ts
32906
+ var FaqEvidenceSchema = external_exports.object({
32907
+ kind: external_exports.enum(["rpc", "file"]),
32908
+ /** RPC 方法名,或 `path:line` */
32909
+ ref: external_exports.string().min(1).max(300)
32910
+ });
32911
+ var FaqReportArgs = external_exports.object({
32912
+ /** 管家归纳成一句话的问题,不贴用户原话 */
32913
+ question: external_exports.string().min(1).max(500),
32914
+ status: external_exports.enum(["unanswered", "answered_unverified"]),
32915
+ /** answered_unverified 必填:给用户的答案 */
32916
+ answer: external_exports.string().max(4e3).optional(),
32917
+ /** answered_unverified 必填 */
32918
+ resolvedBy: external_exports.enum(["rpc", "source"]).optional(),
32919
+ /** answered_unverified 至少一项 */
32920
+ evidence: external_exports.array(FaqEvidenceSchema).max(20).optional(),
32921
+ /** 管家先看过的托管文档路径 */
32922
+ docsChecked: external_exports.array(external_exports.string().max(200)).max(20).optional(),
32923
+ /** unanswered 必填 */
32924
+ unansweredReason: external_exports.enum(["no_source_access", "user_declined", "not_found"]).optional()
32925
+ }).superRefine((v, ctx) => {
32926
+ if (v.status === "answered_unverified") {
32927
+ if (!v.answer) ctx.addIssue({ code: "custom", path: ["answer"], message: "answered_unverified \u9700\u8981 answer" });
32928
+ if (!v.resolvedBy) ctx.addIssue({ code: "custom", path: ["resolvedBy"], message: "answered_unverified \u9700\u8981 resolvedBy" });
32929
+ if (!v.evidence?.length) ctx.addIssue({ code: "custom", path: ["evidence"], message: "answered_unverified \u9700\u8981\u81F3\u5C11\u4E00\u9879 evidence" });
32930
+ } else if (!v.unansweredReason) {
32931
+ ctx.addIssue({ code: "custom", path: ["unansweredReason"], message: "unanswered \u9700\u8981 unansweredReason" });
32932
+ }
32933
+ });
32934
+ var FaqReportResultSchema = external_exports.object({ id: external_exports.string() });
32935
+
32890
32936
  // ../protocol/src/engine-schemas.ts
32891
32937
  var EngineStatusArgs = external_exports.object({
32892
32938
  tool: external_exports.enum(PROBE_TOOL_IDS)
@@ -32987,11 +33033,14 @@ var HISTORY_USER_META_VALUES = [
32987
33033
  "meta-text",
32988
33034
  "attachment-skills",
32989
33035
  "attachment-deferred-tools",
32990
- // persona dispatch 来源分型(信封契约见 dispatch-format.ts):
32991
- // dispatch-task 派出去的任务包(首发 + 续派,续派靠信封 round 属性区分,不单开分型)
32992
- // dispatch-result worker 干完回报注入发起方 session 的结果包
32993
- // 这两类跟其它 meta 的关键差别:**它们是一轮真实交互**,参与 turn 切分与分页计数
32994
- // (见 isDispatchUserMeta 的消费点),不是 CC 内部注入的辅助消息。
33036
+ // persona 间调用来源分型(信封契约见 a2a-format.ts):
33037
+ // a2a-task 派给 worker 的任务包(首发 + 打回,打回靠信封 round 属性区分,不单开分型)
33038
+ // a2a-result 结果投回发起方 session 的结果包
33039
+ // dispatch-* 旧版信封,只在旧历史里出现,照同样语义展示
33040
+ // 这几类跟其它 meta 的关键差别:**它们是一轮真实交互**,参与 turn 切分与分页计数
33041
+ // (见 isA2aUserMeta 的消费点),不是 CC 内部注入的辅助消息。
33042
+ "a2a-task",
33043
+ "a2a-result",
32995
33044
  "dispatch-task",
32996
33045
  "dispatch-result"
32997
33046
  ];
@@ -33168,6 +33217,8 @@ var SessionFileSchema = external_exports.object({
33168
33217
  originOwnerPersonaId: external_exports.string().min(1).optional(),
33169
33218
  /** Dispatch source session for local tree grouping. Strip on mirrors because remote session ids cannot link into the local tree. */
33170
33219
  dispatchedFromSessionId: external_exports.string().min(1).optional(),
33220
+ /** A2A line (contextId) this worker session serves; the spawn env carries it as CLAWD_A2A_CONTEXT_ID so a2aReport finds the current round. Strip on mirrors. */
33221
+ a2aContextId: external_exports.string().min(1).optional(),
33171
33222
  /** Scheduler source session, distinct from dispatch/fork ancestry. Strip on mirrors because this linkage is local-only. */
33172
33223
  shiftFiredFromSessionId: external_exports.string().min(1).optional(),
33173
33224
  /** Bot session key, potentially virtual topic/speaker or document key, not necessarily a real IM chat id. UI hides these sessions from ordinary recent-session lists. */
@@ -34248,6 +34299,15 @@ var METHOD_DOCS = {
34248
34299
  summary: "\u767B\u8BB0 owner \u60F3\u8981 iOS TestFlight\uFF1Adaemon \u8F6C\u53D1\u4E2D\u5FC3 API\uFF08\u643A\u5E26 unionId/\u98DE\u4E66\u540D\uFF09\uFF0Cserver \u843D\u8868\u53BB\u91CD\u5E76\u7528\u5B98\u65B9 bot \u901A\u77E5 David \u8D70\u98DE\u4E66\u8054\u7CFB\uFF1B\u9700\u98DE\u4E66\u5DF2\u767B\u5F55\uFF08FEISHU_GATED\uFF09",
34249
34300
  args: OnboardingIosInterestArgs
34250
34301
  },
34302
+ "faq:report": {
34303
+ summary: "\u7BA1\u5BB6\u4E0A\u62A5\u7B54\u4E0D\u4E0A\u6765 / \u6CA1\u7ECF\u6587\u6863\u6838\u5BF9\u7684\u95EE\u9898\uFF1Adaemon \u5148\u8131\u654F\uFF08\u4E3B\u76EE\u5F55\u8DEF\u5F84\u3001token \u5F62\u72B6\u7684\u4E32\uFF09\uFF0C\u518D\u5E26\u767B\u5F55\u7968\u8F6C\u53D1\u4E2D\u5FC3 faq_inbox\uFF1B\u5931\u8D25\u7531\u8C03\u7528\u65B9\u9759\u9ED8\u5FFD\u7565\uFF1B\u9700\u98DE\u4E66\u5DF2\u767B\u5F55\uFF08FEISHU_GATED\uFF09",
34304
+ args: FaqReportArgs,
34305
+ argsConstraints: {
34306
+ if: { properties: { status: { const: "answered_unverified" } } },
34307
+ then: { required: ["answer", "resolvedBy", "evidence"], properties: { evidence: { minItems: 1 } } },
34308
+ else: { required: ["unansweredReason"] }
34309
+ }
34310
+ },
34251
34311
  "skills:list": {
34252
34312
  summary: "\u626B\u63CF\u5E76\u5217\u51FA\u8BE5\u76EE\u5F55\u53EF\u7528\u7684 skill",
34253
34313
  args: SkillsListArgs
@@ -34321,25 +34381,33 @@ var METHOD_DOCS = {
34321
34381
  summary: "\u5220 persona\u3002owner-only",
34322
34382
  args: PersonaIdArgsSchema
34323
34383
  },
34324
- "personaDispatch:run": {
34325
- summary: "\u628A\u4EFB\u52A1\u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08\u53EF\u8DE8\u8BBE\u5907\uFF09\uFF0C\u5F02\u6B65\u79D2\u56DE { dispatchId }\u3002targetPersona \u4E0E dispatchId **\u81F3\u5C11\u7ED9\u4E00\u4E2A**\uFF08\u524D\u8005\u65B0\u5F00\u3001\u540E\u8005\u7EED\u6D3E\uFF09\uFF1BdispatchId \u4E0E targetDeviceId **\u4E92\u65A5**\uFF08\u8DE8\u8BBE\u5907\u7EED\u6D3E\u4E5F\u53EA\u4F20 dispatchId\uFF0C\u8BBE\u5907\u4ECE\u53F0\u8D26\u53D6\uFF09",
34326
- args: DispatchRunArgsSchema,
34327
- argsConstraints: {
34328
- anyOf: [{ required: ["targetPersona"] }, { required: ["dispatchId"] }],
34329
- not: { required: ["dispatchId", "targetDeviceId"] }
34330
- }
34384
+ "a2a:send": {
34385
+ summary: "\u6D3E\u6D3B\u7ED9\u53E6\u4E00\u4E2A persona\uFF08\u672C\u673A / \u8054\u7CFB\u4EBA\u8BBE\u5907 / \u4E91\u4E0A\uFF09\uFF0C\u4E00\u6B21 1 \u5230 8 \u4E2A call\uFF0C\u6BCF\u4E2A call \u5355\u72EC\u6210\u8D25\uFF0C\u53D1\u51FA\u5373\u8FD4\u56DE\u4EFB\u52A1\u89C6\u56FE\u3002target \u5199\u6CD5\u540C @ token\uFF1A<personaId> / <personaId>@<deviceId> / cloud:<personaId>\uFF1B\u5E26 contextId = \u540C\u4E00\u6761\u7EBF\u65B0\u4E00\u8F6E\uFF08\u6253\u56DE\uFF09\uFF0C\u5E26 taskId = \u56DE\u7B54 input_required",
34386
+ args: A2aSendArgsSchema
34387
+ },
34388
+ "a2a:await": {
34389
+ summary: "\u7B49\u8FD9\u4E9B\u4EFB\u52A1\u5168\u90E8\u8FDB\u5165\u7EC8\u6001\u6216 input_required\uFF08\u5355\u6B21\u6700\u591A 60 \u79D2\uFF09\u3002done=true \u65F6 daemon \u5F53\u573A\u786E\u8BA4\u6295\u9012\uFF0C\u7ED3\u679C\u4E0D\u518D\u6CE8\u5165\u56DE\u4F1A\u8BDD\uFF1Bdone=false \u4EC0\u4E48\u90FD\u4E0D\u786E\u8BA4",
34390
+ args: A2aAwaitArgsSchema
34391
+ },
34392
+ "a2a:answer": {
34393
+ summary: "\u56DE\u7B54\u4E00\u4E2A\u505C\u5728 input_required \u7684\u4EFB\u52A1\uFF08\u53D1\u8D77\u65B9\u672C\u4EBA\uFF09\uFF1A\u56DE\u7B54\u8FDB worker \u4F1A\u8BDD\uFF0Cworker \u63A5\u7740\u5E72\u3002\u4EFB\u52A1\u5DF2\u4E0D\u5728\u7B49\u56DE\u7B54\u65F6\u62A5\u9519",
34394
+ args: A2aAnswerArgsSchema
34395
+ },
34396
+ "a2a:get": {
34397
+ summary: "\u67E5\u4E00\u4E2A A2A \u4EFB\u52A1\u7684\u72B6\u6001\u4E0E\u7ED3\u679C\uFF08\u53D1\u8D77\u65B9\u672C\u4EBA\uFF0C\u6216\u672C\u673A\u76EE\u6807\u8BBE\u5907\uFF09",
34398
+ args: A2aTaskIdArgsSchema
34331
34399
  },
34332
- "personaDispatch:complete": {
34333
- summary: '\u88AB\u59D4\u6D3E\u65B9\u56DE\u62A5\u7ED3\u679C\uFF1A\u5199\u53F0\u8D26\u5E76\u628A\u7ED3\u679C\u6295\u9012\u56DE\u53D1\u8D77\u65B9\u4F1A\u8BDD\u3002outcome \u662F { kind:"success", text, filePaths? } \u6216 { kind:"failure", reason } \u4E8C\u9009\u4E00',
34334
- args: DispatchCompleteArgsSchema
34400
+ "a2a:list": {
34401
+ summary: "\u5217 A2A \u4EFB\u52A1\uFF1A\u6309 contextId / state \u8FC7\u6EE4\uFF1BsessionId = \u8FD9\u4E2A\u4F1A\u8BDD\u53D1\u51FA\u7684\uFF1BrelatedSessionId = \u8FD9\u4E2A\u4F1A\u8BDD\u662F\u53D1\u8D77\u65B9\u6216 worker \u7684\uFF08UI \u7528\uFF09",
34402
+ args: A2aListArgsSchema
34335
34403
  },
34336
- "personaDispatch:list": {
34337
- summary: "\u67E5 dispatch \u53F0\u8D26\uFF08\u72B6\u6001 / \u7ED3\u679C / \u65F6\u95F4\u6233\uFF09\u3002\u7701\u7565 sourceSessionId = \u8FD4\u5168\u91CF",
34338
- args: DispatchListArgsSchema
34404
+ "a2a:cancel": {
34405
+ summary: "\u53D6\u6D88\u4E00\u4E2A\u8FD8\u6CA1\u7ED3\u675F\u7684 A2A \u4EFB\u52A1\uFF1Bworker \u4F1A\u88AB\u4E2D\u65AD",
34406
+ args: A2aTaskIdArgsSchema
34339
34407
  },
34340
- "personaDispatch:get": {
34341
- summary: "\u67E5\u5355\u6761 dispatch \u53F0\u8D26\u7EC8\u6001\uFF1B\u8DE8\u8BBE\u5907\u65F6 A \u7AEF\u51ED connect token \u8F6E\u8BE2 B \u7AEF\u7528",
34342
- args: DispatchGetArgsSchema
34408
+ "a2a:report": {
34409
+ summary: "worker \u56DE\u62A5\u8FD9\u4E00\u8F6E\u7684\u7ED3\u679C\uFF1Acompleted / failed / input_required\uFF08\u505C\u4E0B\u6765\u8981\u53D1\u8D77\u65B9\u56DE\u7B54\uFF09\u3002contextId \u7531\u5BBF\u4E3B\u6CE8\u5165\uFF0Cdaemon \u636E\u5B83\u627E\u5F53\u524D\u8FD9\u4E00\u8F6E",
34410
+ args: A2aReportArgsSchema
34343
34411
  },
34344
34412
  "peerExec:run": {
34345
34413
  summary: "\u5728\u8054\u7CFB\u4EBA\u8BBE\u5907\u4E0A\u8DD1\u4E00\u6761 bash \u547D\u4EE4\uFF08\u4E00\u53D1\u4E00\u6536\uFF0C\u65E0\u8DE8\u547D\u4EE4\u4F1A\u8BDD\u72B6\u6001\uFF09\u3002**\u672C\u673A\u53D1\u8D77\u5FC5\u586B peerDeviceId**\u2014\u2014\u7701\u7565\u4EC5\u7528\u4E8E\u5BF9\u7AEF guest \u53CD\u5411\u5165\u53E3\uFF0Cowner ctx \u7701\u7565\u4F1A\u88AB\u62D2\uFF1B\u9700\u5BF9\u65B9\u5F00\u4E86 remoteAccess",
@@ -34645,8 +34713,8 @@ var WebSessionExchangeResultSchema = external_exports.object({
34645
34713
  expiresAt: external_exports.number()
34646
34714
  });
34647
34715
 
34648
- // ../protocol/src/dispatch-format.ts
34649
- var INNER_TAG_NAMES = "clawd-dispatch-task|clawd-dispatch-result|brief|detail|files";
34716
+ // ../protocol/src/a2a-format.ts
34717
+ var INNER_TAG_NAMES = "clawd-a2a-task|clawd-a2a-result|clawd-dispatch-task|clawd-dispatch-result|brief|detail|files";
34650
34718
  var INNER_TAG_ESCAPE_RE = new RegExp(`<(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`, "g");
34651
34719
  var INNER_TAG_UNESCAPE_RE = new RegExp(`<\\\\(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`, "g");
34652
34720
 
@@ -51,9 +51,9 @@ watcher 的工具、文件权限与引擎历史能力以当前会话为准;给
51
51
 
52
52
  线间尽量无依赖;有依赖的串行编排,**不做假并行**。按 3-5 条线设计,不追求更多。
53
53
 
54
- ### 3. 派活:走 personaDispatch,把话题语义挂在线上
54
+ ### 3. 派活:走 a2aAsync,把话题语义挂在线上
55
55
 
56
- 调 `personaDispatch` tool 时**必须**带 `meta`:
56
+ 调 `clawd-a2a` 的 `a2aAsync` 时,每个 call **必须**带 `meta`:
57
57
 
58
58
  meta: { topicId, lineId, acceptance: [该线的验收标准] }
59
59
 
@@ -66,7 +66,7 @@ watcher 的工具、文件权限与引擎历史能力以当前会话为准;给
66
66
 
67
67
  - `memberPersonas` —— 可派活的 persona **全集**。每项形如 `{ personaId, deviceId? }`:没有
68
68
  `deviceId` 就是本机 persona,有 `deviceId` 就是那台联系人设备上公开给老板的 persona。
69
- 派它时把这两个值原样填进 `personaDispatch` 的 `targetPersona` / `targetDeviceId`
69
+ 派它时 `target` 写 `<personaId>`(本机)或 `<personaId>@<deviceId>`(联系人设备),值原样照抄
70
70
  - `memberContactDeviceIds` —— **人类**成员,只表示「可以给这个人发 DM」
71
71
 
72
72
  **联系人在名单里不等于他家的 persona 能派。** 要派某台设备上的 persona,那个 persona 必须
@@ -75,8 +75,8 @@ watcher 的工具、文件权限与引擎历史能力以当前会话为准;给
75
75
  缺人手就**停下问老板**要不要把某个 persona 加进频道,不要绕道派给名单外的 persona。
76
76
 
77
77
  打回次数**不要**自己往 `meta` 里记 —— daemon 在 `topic:reject` / `topic:accept` 里数,
78
- 你手记一份只会跟它漂。拿到 `dispatchId` 后立刻落账(见「落账纪律」)。
79
- **一件事全程一个 dispatchId**,打回时复用它。
78
+ 你手记一份只会跟它漂。拿到 `contextId` 后立刻落账(见「落账纪律」)。
79
+ **一件事全程一个 contextId**,打回时复用它。
80
80
 
81
81
  ### 4. 验收:不信自述,自己核一遍
82
82
 
@@ -127,11 +127,11 @@ daemon 会单独落一条「推翻 watcher」的流水并计数。**`needsOwnerA
127
127
  用 `grep -n "^import" <文件>` 展示依赖、用 `node --test ... | grep '^ok'` 贴全部用例名、
128
128
  工具调用记录是否保留、是否能读取与引擎和权限有关;不能承诺某种写法必定留下可见痕迹。最终提供产物、测试输出与必要的过程证据,让 watcher 可复查。
129
129
 
130
- ### 5. 打回:同一个 dispatchId 续派
130
+ ### 5. 打回:同一个 contextId 续派
131
131
 
132
- 不合格 → 再调 `personaDispatch`,**带原 dispatchId**(worker 会话接着干,上下文不丢):
132
+ 不合格 → 再调 `a2aAsync`,**带原 contextId**(同一条线的下一轮,worker 会话接着干,上下文不丢):
133
133
 
134
- personaDispatch({ dispatchId: <原 id>, prompt: "第 N 条没达标:<具体>,补一下" })
134
+ a2aAsync({ calls: [{ target: <原 target>, contextId: <原 id>, text: "第 N 条没达标:<具体>,补一下" }] })
135
135
 
136
136
  打回前先跑:
137
137
 
@@ -143,13 +143,12 @@ daemon 数打回次数、同时把理由写进流水(一次调用两件事,
143
143
  返回 `{ rejectCount, needsOwnerAttention }`。**`needsOwnerAttention` 为 true
144
144
  (同一条线累计两次打回)→ 停下上报老板,不要再自行打回。**
145
145
 
146
- > 跨设备派出去的线(`targetDeviceId`)续派 / 打回与本机线一样:只传 `dispatchId`,**不要**再带
147
- > `targetDeviceId`(协议层禁止两者同传,设备从台账取)。
146
+ > 跨设备派出去的线续派 / 打回与本机线一样:`target` 照旧、带上 `contextId`。
148
147
 
149
148
  ### 6. 线中途上报问题
150
149
 
151
- worker 干到一半上报「答不了的问题」(它会以回报形式带出来,标明是问题不是完成)时:
152
- 先自己判断——你是任务源头,多数问题你能答,答了就续派回注。答不了 → 停下上报老板拍板。
150
+ worker 干到一半上报「答不了的问题」(回报状态是 `input_required`,问题在结果正文里)时:
151
+ 先自己判断——你是任务源头,多数问题你能答,答了就带那一轮的 `taskId` 把回答发回去(`a2aAsync` 的 call 带 `taskId`)。答不了 → 停下上报老板拍板。
153
152
  **不许用未经确认的假设擅自解锁一条挂起的线**——除非该假设来自设定里的预授权或老板临场拍板。
154
153
 
155
154
  ### 6.1 决策留痕:代老板确认与向老板升级(硬性)
@@ -186,7 +185,7 @@ worker 的问题不能只在对话里一闪而过。你每次替老板作出判
186
185
  }})
187
186
 
188
187
  `session:interrupt` 只结束当前 worker turn,不杀会话、不丢上下文;恢复时仍复用原
189
- `dispatchId`。如果该调用失败,不能假定 worker 已停下;仍要追加下面的 `owner-needed`
188
+ `contextId`。如果该调用失败,不能假定 worker 已停下;仍要追加下面的 `owner-needed`
190
189
  记录,并在 `reason` 里写明中断失败,继续保持不续派。
191
190
 
192
191
  mcp__clawd-rpc__call({ method: 'topic:append', args: {
@@ -200,8 +199,8 @@ worker 的问题不能只在对话里一闪而过。你每次替老板作出判
200
199
  }})
201
200
 
202
201
  然后向老板报告这条待决策项,并等待明确答复;不要用普通 `topic:append` 假装已经解决。
203
- 如果 worker 已经 idle,则不需要额外 RPC;只是不再给该 `dispatchId` 续派,等老板答复。
204
- 老板答复后,先把答复原样记录成同一 `target` 的已解决决策,再用**同一个 `dispatchId`**
202
+ 如果 worker 已经 idle,则不需要额外 RPC;只是不再给该 `contextId` 续派,等老板答复。
203
+ 老板答复后,先把答复原样记录成同一 `target` 的已解决决策,再用**同一个 `contextId`**
205
204
  续派可执行指令;不要新建第二条线。`session:interrupt` 失败时不能当作已经停住。
206
205
  记录已解决决策的调用仍走 `topic:append`:
207
206
 
@@ -214,7 +213,7 @@ worker 的问题不能只在对话里一闪而过。你每次替老板作出判
214
213
  },
215
214
  }})
216
215
 
217
- 随后才用原 `dispatchId` 续派。决策记录落账失败就不要继续派活,先上报失败原因。
216
+ 随后才用原 `contextId` 续派。决策记录落账失败就不要继续派活,先上报失败原因。
218
217
  决策 tab 会据此保留完整的「提出问题 → 得到结论」轨迹。
219
218
 
220
219
  这两类记录是老板在话题「决策」tab 里唯一关注的内容:没有 `decision` 的旧流水不会出现在
@@ -247,7 +246,7 @@ worker 的问题不能只在对话里一闪而过。你每次替老板作出判
247
246
  老板事后要看的不是"你做了什么",而是"你凭什么这么判断"——后者才能拿来纠正你。
248
247
 
249
248
  **话题记录是你的外部大脑,不是事后日志**:做决策时当场写。你的上下文随时可能被清空,
250
- 清空后你要能只靠 `topic:get`(三要素 + 流水 + 线)+ `personaDispatch:list` 接着干。
249
+ 清空后你要能只靠 `topic:get`(三要素 + 流水 + 线)+ `a2aListTasks` 接着干。
251
250
 
252
251
  ## 授权纪律(硬性)
253
252
 
@@ -16383,6 +16383,10 @@ function loadMcpServerDefs(args) {
16383
16383
  return out;
16384
16384
  }
16385
16385
 
16386
+ // src/a2a/limits.ts
16387
+ var A2A_MCP_SERVER_NAME = "clawd-a2a";
16388
+ var A2A_MCP_CALL_TIMEOUT_MS = 2 * 60 * 6e4 + 6e4;
16389
+
16386
16390
  // src/pi-mcp-bridge/bridge.ts
16387
16391
  var PI_MCP_CONFIGS_ENV = "CLAWD_PI_MCP_CONFIGS";
16388
16392
  function piToolName(server, tool) {
@@ -16540,7 +16544,8 @@ var ServerLink = class {
16540
16544
  const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
16541
16545
  const result = await client.callTool({ name, arguments: argsObj }, void 0, {
16542
16546
  signal,
16543
- timeout: this.opts.callTimeoutMs,
16547
+ // a2aWait 一次调用可以等到 2 小时(a2a/limits.ts)
16548
+ timeout: this.def.name === A2A_MCP_SERVER_NAME ? Math.max(this.opts.callTimeoutMs, A2A_MCP_CALL_TIMEOUT_MS) : this.opts.callTimeoutMs,
16544
16549
  resetTimeoutOnProgress: true
16545
16550
  });
16546
16551
  return result;