@clawos-dev/clawd 0.2.739 → 0.2.740

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,108 @@ 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
+ /** 任务正文(首条 user 消息) */
32268
+ taskText: external_exports.string(),
32269
+ /** worker 的一句话摘要(statusMessage.brief) */
32270
+ brief: external_exports.string().nullable(),
32271
+ /** worker 最后一条回报正文 */
32272
+ resultText: external_exports.string().nullable(),
32273
+ filePaths: external_exports.array(external_exports.string()),
32274
+ line: external_exports.record(external_exports.unknown()),
32275
+ deliveredAt: external_exports.string().nullable(),
32276
+ createdAt: external_exports.string(),
32277
+ updatedAt: external_exports.string(),
32278
+ finishedAt: external_exports.string().nullable()
32276
32279
  });
32280
+ var A2aCallResultSchema = external_exports.discriminatedUnion("ok", [
32281
+ external_exports.object({ ok: external_exports.literal(true), task: A2aTaskViewSchema }),
32282
+ external_exports.object({ ok: external_exports.literal(false), error: external_exports.object({ code: external_exports.string(), message: external_exports.string() }) })
32283
+ ]);
32284
+ var A2aSendResultSchema = external_exports.object({ results: external_exports.array(A2aCallResultSchema) });
32285
+ var A2aListResultSchema = external_exports.object({ tasks: external_exports.array(A2aTaskViewSchema) });
32286
+ var A2aAwaitResultSchema = external_exports.object({ tasks: external_exports.array(A2aTaskViewSchema), done: external_exports.boolean() });
32277
32287
 
32278
32288
  // ../protocol/src/extension.ts
32279
32289
  var EXT_ID_REGEX = /^[a-z0-9][a-z0-9-]*$/;
@@ -32652,11 +32662,14 @@ var TopicSchema = external_exports.object({
32652
32662
  });
32653
32663
  var TopicLineViewSchema = external_exports.object({
32654
32664
  lineId: external_exports.string().min(1),
32655
- dispatchId: external_exports.string().min(1),
32665
+ /** 这条线(A2A context);打回在同一 context 下开新任务 */
32666
+ contextId: external_exports.string().min(1),
32667
+ /** 最新一轮的任务 */
32668
+ taskId: external_exports.string().min(1),
32656
32669
  workerSessionId: external_exports.string().optional(),
32657
32670
  assignee: external_exports.string().min(1),
32658
32671
  focus: external_exports.string(),
32659
- status: DispatchStatusSchema,
32672
+ status: A2aStateSchema,
32660
32673
  acceptance: external_exports.array(external_exports.string()),
32661
32674
  rejectCount: external_exports.number().int().min(0),
32662
32675
  accepted: external_exports.boolean(),
@@ -32887,6 +32900,37 @@ var OnboardingIosInterestResultSchema = external_exports.object({
32887
32900
  registered: external_exports.boolean()
32888
32901
  });
32889
32902
 
32903
+ // ../protocol/src/faq-schemas.ts
32904
+ var FaqEvidenceSchema = external_exports.object({
32905
+ kind: external_exports.enum(["rpc", "file"]),
32906
+ /** RPC 方法名,或 `path:line` */
32907
+ ref: external_exports.string().min(1).max(300)
32908
+ });
32909
+ var FaqReportArgs = external_exports.object({
32910
+ /** 管家归纳成一句话的问题,不贴用户原话 */
32911
+ question: external_exports.string().min(1).max(500),
32912
+ status: external_exports.enum(["unanswered", "answered_unverified"]),
32913
+ /** answered_unverified 必填:给用户的答案 */
32914
+ answer: external_exports.string().max(4e3).optional(),
32915
+ /** answered_unverified 必填 */
32916
+ resolvedBy: external_exports.enum(["rpc", "source"]).optional(),
32917
+ /** answered_unverified 至少一项 */
32918
+ evidence: external_exports.array(FaqEvidenceSchema).max(20).optional(),
32919
+ /** 管家先看过的托管文档路径 */
32920
+ docsChecked: external_exports.array(external_exports.string().max(200)).max(20).optional(),
32921
+ /** unanswered 必填 */
32922
+ unansweredReason: external_exports.enum(["no_source_access", "user_declined", "not_found"]).optional()
32923
+ }).superRefine((v, ctx) => {
32924
+ if (v.status === "answered_unverified") {
32925
+ if (!v.answer) ctx.addIssue({ code: "custom", path: ["answer"], message: "answered_unverified \u9700\u8981 answer" });
32926
+ if (!v.resolvedBy) ctx.addIssue({ code: "custom", path: ["resolvedBy"], message: "answered_unverified \u9700\u8981 resolvedBy" });
32927
+ if (!v.evidence?.length) ctx.addIssue({ code: "custom", path: ["evidence"], message: "answered_unverified \u9700\u8981\u81F3\u5C11\u4E00\u9879 evidence" });
32928
+ } else if (!v.unansweredReason) {
32929
+ ctx.addIssue({ code: "custom", path: ["unansweredReason"], message: "unanswered \u9700\u8981 unansweredReason" });
32930
+ }
32931
+ });
32932
+ var FaqReportResultSchema = external_exports.object({ id: external_exports.string() });
32933
+
32890
32934
  // ../protocol/src/engine-schemas.ts
32891
32935
  var EngineStatusArgs = external_exports.object({
32892
32936
  tool: external_exports.enum(PROBE_TOOL_IDS)
@@ -32987,11 +33031,14 @@ var HISTORY_USER_META_VALUES = [
32987
33031
  "meta-text",
32988
33032
  "attachment-skills",
32989
33033
  "attachment-deferred-tools",
32990
- // persona dispatch 来源分型(信封契约见 dispatch-format.ts):
32991
- // dispatch-task 派出去的任务包(首发 + 续派,续派靠信封 round 属性区分,不单开分型)
32992
- // dispatch-result worker 干完回报注入发起方 session 的结果包
32993
- // 这两类跟其它 meta 的关键差别:**它们是一轮真实交互**,参与 turn 切分与分页计数
32994
- // (见 isDispatchUserMeta 的消费点),不是 CC 内部注入的辅助消息。
33034
+ // persona 间调用来源分型(信封契约见 a2a-format.ts):
33035
+ // a2a-task 派给 worker 的任务包(首发 + 打回,打回靠信封 round 属性区分,不单开分型)
33036
+ // a2a-result 结果投回发起方 session 的结果包
33037
+ // dispatch-* 旧版信封,只在旧历史里出现,照同样语义展示
33038
+ // 这几类跟其它 meta 的关键差别:**它们是一轮真实交互**,参与 turn 切分与分页计数
33039
+ // (见 isA2aUserMeta 的消费点),不是 CC 内部注入的辅助消息。
33040
+ "a2a-task",
33041
+ "a2a-result",
32995
33042
  "dispatch-task",
32996
33043
  "dispatch-result"
32997
33044
  ];
@@ -33168,6 +33215,8 @@ var SessionFileSchema = external_exports.object({
33168
33215
  originOwnerPersonaId: external_exports.string().min(1).optional(),
33169
33216
  /** Dispatch source session for local tree grouping. Strip on mirrors because remote session ids cannot link into the local tree. */
33170
33217
  dispatchedFromSessionId: external_exports.string().min(1).optional(),
33218
+ /** 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. */
33219
+ a2aContextId: external_exports.string().min(1).optional(),
33171
33220
  /** Scheduler source session, distinct from dispatch/fork ancestry. Strip on mirrors because this linkage is local-only. */
33172
33221
  shiftFiredFromSessionId: external_exports.string().min(1).optional(),
33173
33222
  /** 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 +34297,15 @@ var METHOD_DOCS = {
34248
34297
  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
34298
  args: OnboardingIosInterestArgs
34250
34299
  },
34300
+ "faq:report": {
34301
+ 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",
34302
+ args: FaqReportArgs,
34303
+ argsConstraints: {
34304
+ if: { properties: { status: { const: "answered_unverified" } } },
34305
+ then: { required: ["answer", "resolvedBy", "evidence"], properties: { evidence: { minItems: 1 } } },
34306
+ else: { required: ["unansweredReason"] }
34307
+ }
34308
+ },
34251
34309
  "skills:list": {
34252
34310
  summary: "\u626B\u63CF\u5E76\u5217\u51FA\u8BE5\u76EE\u5F55\u53EF\u7528\u7684 skill",
34253
34311
  args: SkillsListArgs
@@ -34321,25 +34379,33 @@ var METHOD_DOCS = {
34321
34379
  summary: "\u5220 persona\u3002owner-only",
34322
34380
  args: PersonaIdArgsSchema
34323
34381
  },
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
- }
34382
+ "a2a:send": {
34383
+ 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",
34384
+ args: A2aSendArgsSchema
34385
+ },
34386
+ "a2a:await": {
34387
+ 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",
34388
+ args: A2aAwaitArgsSchema
34389
+ },
34390
+ "a2a:answer": {
34391
+ 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",
34392
+ args: A2aAnswerArgsSchema
34393
+ },
34394
+ "a2a:get": {
34395
+ 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",
34396
+ args: A2aTaskIdArgsSchema
34331
34397
  },
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
34398
+ "a2a:list": {
34399
+ 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",
34400
+ args: A2aListArgsSchema
34335
34401
  },
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
34402
+ "a2a:cancel": {
34403
+ summary: "\u53D6\u6D88\u4E00\u4E2A\u8FD8\u6CA1\u7ED3\u675F\u7684 A2A \u4EFB\u52A1\uFF1Bworker \u4F1A\u88AB\u4E2D\u65AD",
34404
+ args: A2aTaskIdArgsSchema
34339
34405
  },
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
34406
+ "a2a:report": {
34407
+ 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",
34408
+ args: A2aReportArgsSchema
34343
34409
  },
34344
34410
  "peerExec:run": {
34345
34411
  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 +34711,8 @@ var WebSessionExchangeResultSchema = external_exports.object({
34645
34711
  expiresAt: external_exports.number()
34646
34712
  });
34647
34713
 
34648
- // ../protocol/src/dispatch-format.ts
34649
- var INNER_TAG_NAMES = "clawd-dispatch-task|clawd-dispatch-result|brief|detail|files";
34714
+ // ../protocol/src/a2a-format.ts
34715
+ var INNER_TAG_NAMES = "clawd-a2a-task|clawd-a2a-result|clawd-dispatch-task|clawd-dispatch-result|brief|detail|files";
34650
34716
  var INNER_TAG_ESCAPE_RE = new RegExp(`<(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`, "g");
34651
34717
  var INNER_TAG_UNESCAPE_RE = new RegExp(`<\\\\(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`, "g");
34652
34718
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: clawd-orchestration
3
- description: 老板要 dispatch 任务给别的 persona、排定时任务(shift/routine)、管授权联系人,或问频道 / 话题 / 拆线 / 验收 / 收敛 / Master 相关的事(「这个话题结了没」「第 2 条线怎么样」「把这几个分身编成一组干一件事」)时触发
3
+ description: 老板要委派任务给别的 persona、排定时任务(shift/routine)、管授权联系人,或问频道 / 话题 / 拆线 / 验收 / 收敛 / Master 相关的事(「这个话题结了没」「第 2 条线怎么样」「把这几个分身编成一组干一件事」)时触发
4
4
  ---
5
5
 
6
6
  # 任务调度 & 定时 & 联系人管理
@@ -13,14 +13,14 @@ description: 老板要 dispatch 任务给别的 persona、排定时任务(shif
13
13
 
14
14
  | 老板要 | fetch |
15
15
  |---|---|
16
- | dispatch 任务(本机 persona / 跨设备 / 续派 / attachment) | `${CLAWOS_API}/api/docs/orchestration/dispatch-guide.md` |
16
+ | 委派任务(本机 / 跨设备 / 云上 persona、续派、回答对方提问、attachment) | `${CLAWOS_API}/api/docs/orchestration/dispatch-guide.md` |
17
17
  | 排定时任务(`/schedule`、`/loop`、shift) | `${CLAWOS_API}/api/docs/orchestration/shift-guide.md` |
18
18
  | 授权联系人 / 撤销授权 / 看谁授过我啥 | `${CLAWOS_API}/api/docs/orchestration/contacts-guide.md` |
19
19
  | 频道 / 话题(多分身拆线并行 + 逐条验收) | `${CLAWOS_API}/api/docs/orchestration/channel-topic-guide.md` |
20
20
 
21
21
  **能调的 MCP 工具**(以当前会话实际工具清单为准):
22
22
 
23
- - `mcp__clawd-dispatch__personaDispatch` / `personaDispatchList` / `personaDispatchComplete`
23
+ - `mcp__clawd-a2a__a2aAsync` / `a2aWait` / `a2aListTasks` / `a2aGetTask` / `a2aCancelTask`(被委派时还有 `a2aReport`)
24
24
  - `mcp__clawd-shift__addShift` / `listShifts` / `updateShift` / `cancelShift` / `getShiftHistory`
25
25
  - `mcp__peer-ops__exec`(跨设备执行)
26
26
 
@@ -5,9 +5,9 @@ description: 老板问 clawd 是什么、能干什么、desktop 有啥功能、p
5
5
 
6
6
  # clawd 产品答疑
7
7
 
8
- 老板问"clawd 是啥、有啥功能、persona 咋用、远程接入咋弄"之类的产品问题就用这个 skill。
8
+ 老板问"clawd 是啥、有啥功能、persona 咋用、远程接入咋弄"之类的产品问题就用这个 skill。回答分三层,**按顺序来,上一层答不了才进下一层**,并且凡是没在第一层得到答案的问题都要上报(见"上报")。
9
9
 
10
- ## 怎么答
10
+ ## 第一层:托管文档
11
11
 
12
12
  1. `$CLAWOS_API` 已在 env 里,直接用(daemon 注的;这只是 api 域名,跟产品命名无关)
13
13
  2. 按老板问题挑对应 md 拉最新版:
@@ -22,11 +22,54 @@ description: 老板问 clawd 是什么、能干什么、desktop 有啥功能、p
22
22
  | 分享分身、隐私、删联系人后还能不能用 | `${CLAWOS_API}/api/docs/product/sharing.md` |
23
23
  | 不确定看哪篇 | `${CLAWOS_API}/api/docs/product/index.md`(按问题分组的目录) |
24
24
 
25
- 3. WebFetch 拉回来照着答
25
+ 3. WebFetch 拉回来,文档里有明确说法就照着答。**这一层答出来的不上报。** 记下你读过哪几篇(路径,如 `product/environments.md`),上报时要用
26
+ 4. 文档只写了相关内容、但没有直接回答老板的问题,算"文档没答案",进第二层,**别自己推断着答**
26
27
 
27
- ## fetch 失败了
28
+ fetch 失败了:直接告诉老板"产品文档暂时拉不到,稍后再问一次或让 David 看一眼",**别**凭印象编,也不用上报。
28
29
 
29
- 直接告诉老板"产品文档暂时拉不到,稍后再问一次或让 David 看一眼"。**别**凭印象编——记不准的功能不猜。
30
+ ## 第二层:本机状态
31
+
32
+ 老板问的是**他这台机器当下的情况**("我有几个 persona""这个分身挂了几个定时任务"),文档不会写,用 `clawd-rpc` MCP 的 `list` + `call` 查。**只调只读方法**,不调任何写操作;**不调 `contact:list`(返回连接票),也不去读任何凭证文件**,需要联系人信息先问老板。
33
+
34
+ 答完后上报:`status = answered_unverified`、`resolvedBy = rpc`,`evidence` 写用到的 RPC 方法名。**上报的 `answer` 只写通用结论**("本机有若干 persona,用 persona:list 查得"),**不要写查到的具体名字**——persona 名、联系人名、会话标题、路径都不进 `answer`。具体数字和名字只说给老板听。
35
+
36
+ ## 第三层:源码(必须先问老板)
37
+
38
+ 前两层都没有答案时:
39
+
40
+ 1. 先判断能不能看源码:用 `clawd-rpc` 调 `project:list`,在返回的项目路径里找一个目录,其下存在 `clawd/protocol/src/methods.ts`(这就是 clawos 仓库)。找不到或读不了 = 不能看
41
+ 2. **能看**:问老板一句"文档和本机状态里都没有答案,要我去源码里找吗?"
42
+ - 老板同意:只在那个仓库里找(别全盘搜)。找到了就答,并上报 `answered_unverified`、`resolvedBy = source`,`evidence` 写 `文件路径:行号`;没找到上报 `unanswered`、`unansweredReason = not_found`
43
+ - 老板不同意:上报 `unanswered`、`unansweredReason = user_declined`
44
+ 3. **不能看**:不用问,直接上报 `unanswered`、`unansweredReason = no_source_access`
45
+
46
+ ## 上报
47
+
48
+ 用 `clawd-rpc` 的 `call` 调 `faq:report`:
49
+
50
+ ```
51
+ call({ method: "faq:report", args: {
52
+ question: "int 环境的云 persona 和线上用的是同一个 key 吗", // 你归纳的一句话,≤ 500 字,不贴老板的原话
53
+ status: "answered_unverified", // 或 "unanswered"
54
+ answer: "……", // answered_unverified 必填:你给老板的答案
55
+ resolvedBy: "rpc", // answered_unverified 必填:"rpc" | "source"
56
+ evidence: [{ kind: "rpc", ref: "persona:list" }], // answered_unverified 至少一项;kind 是 "rpc" 或 "file"
57
+ docsChecked: ["product/environments.md"], // 你读过的文档
58
+ unansweredReason: "no_source_access" // unanswered 必填:"no_source_access" | "user_declined" | "not_found"
59
+ }})
60
+ ```
61
+
62
+ 规则:
63
+
64
+ - `question` 和 `answer` 都要写成**通用的话**,不带老板的人名、实例名字、路径、密钥。daemon 会再脱敏一遍,但它只认路径和密钥形状,别指望它兜底
65
+ - **上报失败就当没发生**:没登录、网络不通、被限频,都不要告诉老板,也不要重试,更不要因此改变你给老板的回答。同时**失败了就不要说"已记录"**(见下)
66
+ - 同一个问题在一次对话里只报一次
67
+
68
+ ## 回答里要告诉老板的话
69
+
70
+ - `faq:report` **返回成功**才在答案末尾加一行:"这个问题我已记录下来,会请产品团队补进文档。"失败了就不加、也不提,别说没做成的事
71
+ - `answered_unverified` 的答案,正文里要明说"这条没有经过文档核对,仅供参考"
72
+ - `unanswered`:直接说"这个我现在答不上来"(上报成功的话再加上面那一行)。**别编**
30
73
 
31
74
  ## 回答风格
32
75