@clawos-dev/clawd 0.2.385 → 0.2.386
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 +105 -217
- package/dist/dispatch/mcp-server.cjs +41 -20
- package/dist/persona-defaults/persona-master/.claude/agents/line-watcher.md +35 -19
- package/dist/persona-defaults/persona-master/CLAUDE.md +49 -7
- package/dist/share-ui/assets/{guest-BxzDJJJn.js → guest-D6nU3ygB.js} +25 -25
- package/dist/share-ui/guest.html +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -6613,17 +6613,19 @@ var init_shift_internal = __esm({
|
|
|
6613
6613
|
});
|
|
6614
6614
|
|
|
6615
6615
|
// ../protocol/src/channel.ts
|
|
6616
|
-
var ChannelSchema, nonBlank, ChannelCreateArgsSchema, ChannelUpdateArgsSchema, ChannelDeleteArgsSchema, ChannelListResultSchema, ChannelResultSchema, ChannelDeleteResultSchema;
|
|
6616
|
+
var MASTER_PERSONA_ID, ChannelSchema, nonBlank, ChannelCreateArgsSchema, ChannelUpdateArgsSchema, ChannelDeleteArgsSchema, ChannelListResultSchema, ChannelResultSchema, ChannelDeleteResultSchema;
|
|
6617
6617
|
var init_channel = __esm({
|
|
6618
6618
|
"../protocol/src/channel.ts"() {
|
|
6619
6619
|
"use strict";
|
|
6620
6620
|
init_zod();
|
|
6621
|
+
MASTER_PERSONA_ID = "persona-master";
|
|
6621
6622
|
ChannelSchema = external_exports.object({
|
|
6622
6623
|
id: external_exports.string().min(1),
|
|
6623
6624
|
name: external_exports.string().min(1),
|
|
6624
6625
|
/** 该频道里可被派活的 persona */
|
|
6625
6626
|
memberPersonaIds: external_exports.array(external_exports.string().min(1)),
|
|
6626
|
-
/** 人类成员(contact deviceId
|
|
6627
|
+
/** 人类成员(contact deviceId)。跨设备派活的成员边界认这张名单(personaDispatch 校验),
|
|
6628
|
+
* P1 人类异步询问也用它 */
|
|
6627
6629
|
memberContactDeviceIds: external_exports.array(external_exports.string().min(1)),
|
|
6628
6630
|
createdAt: external_exports.string().min(1),
|
|
6629
6631
|
updatedAt: external_exports.string().min(1)
|
|
@@ -6672,7 +6674,18 @@ var init_topic = __esm({
|
|
|
6672
6674
|
});
|
|
6673
6675
|
TopicLineMetaSchema = external_exports.object({
|
|
6674
6676
|
rejectCount: external_exports.number().int().min(0),
|
|
6675
|
-
accepted: external_exports.boolean().default(false)
|
|
6677
|
+
accepted: external_exports.boolean().default(false),
|
|
6678
|
+
/**
|
|
6679
|
+
* Master 在 watcher 判 fail 的情况下仍判合格的次数。
|
|
6680
|
+
*
|
|
6681
|
+
* 为什么要单独数:watcher 的 fail 若能被一句「这是可观测性缺口」无声绕过,核验就形同
|
|
6682
|
+
* 虚设——实测一个话题三条线的 watcher 全报 fail、无一打回,Master 自己下场补证后判了合格。
|
|
6683
|
+
* 覆盖本身不禁止(watcher 确实会误判),但必须留痕、必须计数、反复覆盖必须惊动老板。
|
|
6684
|
+
*
|
|
6685
|
+
* optional 而非 default(0):store 不按 schema parse(见 topic-store),存量 lineMeta
|
|
6686
|
+
* 里本来就没有这个字段,读侧一律 `?? 0`。
|
|
6687
|
+
*/
|
|
6688
|
+
overrideCount: external_exports.number().int().min(0).optional()
|
|
6676
6689
|
});
|
|
6677
6690
|
TopicSchema = external_exports.object({
|
|
6678
6691
|
id: external_exports.string().min(1),
|
|
@@ -6742,7 +6755,17 @@ var init_topic = __esm({
|
|
|
6742
6755
|
/** 凭什么判合格(逐条标准对应到什么证据)——与打回同样必填 */
|
|
6743
6756
|
reason: nonBlank2,
|
|
6744
6757
|
/** watcher 认定的产出物,一并进影响面 */
|
|
6745
|
-
artifacts: external_exports.array(nonBlank2).optional()
|
|
6758
|
+
artifacts: external_exports.array(nonBlank2).optional(),
|
|
6759
|
+
/**
|
|
6760
|
+
* 这条线的 watcher 核验结论。**必填**:不填等于允许「压根没跑 watcher 就发合格证」,
|
|
6761
|
+
* 而合格与打回之所以要对称落账,就是为了让老板事后分得出「判过且过了」和「没judge过」。
|
|
6762
|
+
*/
|
|
6763
|
+
watcherVerdict: external_exports.enum(["pass", "fail"]),
|
|
6764
|
+
/** watcher 判 fail 却仍要判合格时,凭什么推翻它(fail 时必填) */
|
|
6765
|
+
overrideReason: nonBlank2.optional()
|
|
6766
|
+
}).refine((v2) => v2.watcherVerdict !== "fail" || Boolean(v2.overrideReason), {
|
|
6767
|
+
message: "watcherVerdict=fail \u65F6\u5FC5\u987B\u7ED9 overrideReason\uFF08\u63A8\u7FFB watcher \u7684\u7406\u7531\uFF09",
|
|
6768
|
+
path: ["overrideReason"]
|
|
6746
6769
|
});
|
|
6747
6770
|
TopicCloseArgsSchema = external_exports.object({
|
|
6748
6771
|
topicId: external_exports.string().min(1),
|
|
@@ -6769,27 +6792,25 @@ var init_topic = __esm({
|
|
|
6769
6792
|
acceptedLines: external_exports.number().int().min(1),
|
|
6770
6793
|
totalLines: external_exports.number().int().min(1),
|
|
6771
6794
|
/** 全部线都合格了 → 可以收敛上报。由 daemon 数,不让 Master 自己盘 */
|
|
6772
|
-
allAccepted: external_exports.boolean()
|
|
6795
|
+
allAccepted: external_exports.boolean(),
|
|
6796
|
+
/** 本话题累计推翻 watcher 的次数(含这一次) */
|
|
6797
|
+
overrides: external_exports.number().int().min(0),
|
|
6798
|
+
/**
|
|
6799
|
+
* 反复推翻 watcher = 取证链路有系统性问题(如 worker 用 heredoc 写文件导致正文
|
|
6800
|
+
* 写得没法验),该让老板知道并去修验收标准,而不是每条线都靠 Master 补证。
|
|
6801
|
+
*/
|
|
6802
|
+
needsOwnerAttention: external_exports.boolean()
|
|
6773
6803
|
});
|
|
6774
6804
|
TopicResultSchema = external_exports.object({ topic: TopicSchema });
|
|
6775
6805
|
}
|
|
6776
6806
|
});
|
|
6777
6807
|
|
|
6778
6808
|
// ../protocol/src/master.ts
|
|
6779
|
-
var
|
|
6809
|
+
var AUTHORITY_ACTIONS, AuthorityActionSchema, AuthorityRuleSchema, AuthorityTableSchema, AuthorityCheckArgsSchema, AuthorityCheckResultSchema;
|
|
6780
6810
|
var init_master = __esm({
|
|
6781
6811
|
"../protocol/src/master.ts"() {
|
|
6782
6812
|
"use strict";
|
|
6783
6813
|
init_zod();
|
|
6784
|
-
LineDigestArgsSchema = external_exports.object({
|
|
6785
|
-
/** 一条线 = 一条 dispatch;daemon 内部据此解析 worker session 与 transcript */
|
|
6786
|
-
dispatchId: external_exports.string().min(1)
|
|
6787
|
-
});
|
|
6788
|
-
LineDigestResultSchema = external_exports.object({
|
|
6789
|
-
digest: external_exports.string(),
|
|
6790
|
-
/** 抽取时是否因超上限被截断——watcher 据此避免"仅凭没看到就判 fail" */
|
|
6791
|
-
truncated: external_exports.boolean()
|
|
6792
|
-
});
|
|
6793
6814
|
AUTHORITY_ACTIONS = ["ask", "allow", "deny"];
|
|
6794
6815
|
AuthorityActionSchema = external_exports.enum(AUTHORITY_ACTIONS);
|
|
6795
6816
|
AuthorityRuleSchema = external_exports.object({
|
|
@@ -8476,18 +8497,19 @@ var init_methods = __esm({
|
|
|
8476
8497
|
args: TopicRejectArgsSchema
|
|
8477
8498
|
},
|
|
8478
8499
|
"topic:accept": {
|
|
8479
|
-
summary: "\u5224\u67D0\u6761\u7EBF\u5408\u683C\uFF1A\u7F6E accepted \u5E76\u540C\u6B65\u843D\u4E00\u6761\u6D41\u6C34\uFF1Breason \u5FC5\u586B\uFF08\u4E0E\u6253\u56DE\u5BF9\u79F0\uFF0C\u53EA\u8BB0\u6253\u56DE\u7B49\u4E8E\u53EA\u8BB0\u5206\u5B50\uFF09\u3002\u8FD4\u56DE acceptedLines / totalLines / allAccepted\uFF0C\u6536\u655B\u65F6\u673A\u7531 daemon \u6570\u4E0D\u7531 Master \u76D8",
|
|
8480
|
-
args: TopicAcceptArgsSchema
|
|
8500
|
+
summary: "\u5224\u67D0\u6761\u7EBF\u5408\u683C\uFF1A\u7F6E accepted \u5E76\u540C\u6B65\u843D\u4E00\u6761\u6D41\u6C34\uFF1Breason \u4E0E watcherVerdict \u5FC5\u586B\uFF08\u4E0E\u6253\u56DE\u5BF9\u79F0\uFF0C\u53EA\u8BB0\u6253\u56DE\u7B49\u4E8E\u53EA\u8BB0\u5206\u5B50\uFF09\u3002watcherVerdict=fail \u65F6\u5FC5\u987B\u7ED9 overrideReason\uFF0Cdaemon \u5355\u72EC\u8BA1\u63A8\u7FFB\u6B21\u6570\u3002\u8FD4\u56DE acceptedLines / totalLines / allAccepted / overrides / needsOwnerAttention\uFF0C\u6536\u655B\u65F6\u673A\u7531 daemon \u6570\u4E0D\u7531 Master \u76D8",
|
|
8501
|
+
args: TopicAcceptArgsSchema,
|
|
8502
|
+
argsConstraints: {
|
|
8503
|
+
// watcherVerdict=fail 时 overrideReason 必填(.refine 的谓词不可序列化,这里显式声明)
|
|
8504
|
+
if: { properties: { watcherVerdict: { const: "fail" } }, required: ["watcherVerdict"] },
|
|
8505
|
+
then: { required: ["overrideReason"] }
|
|
8506
|
+
}
|
|
8481
8507
|
},
|
|
8482
8508
|
"topic:close": {
|
|
8483
8509
|
summary: "\u6536\u655B\u8BDD\u9898\uFF1Astate \u7F6E done / archived",
|
|
8484
8510
|
args: TopicCloseArgsSchema
|
|
8485
8511
|
},
|
|
8486
|
-
// ----
|
|
8487
|
-
"line:digest": {
|
|
8488
|
-
summary: "\u628A\u4E00\u6761\u7EBF\uFF08dispatch\uFF09\u7684 worker transcript \u62BD\u6210 \u226420KB digest\uFF1A\u6539\u52A8\u8FC7\u7684\u6587\u4EF6 / \u6267\u884C\u8FC7\u7684\u547D\u4EE4 / \u6210\u529F\u547D\u4EE4\u8F93\u51FA\u6458\u5F55 / \u62A5\u9519\u805A\u5408 / \u5C3E\u90E8 assistant \u8F93\u51FA\u3002\u6838\u9A8C subagent \u7528\u5B83\uFF0C\u907F\u514D\u76F4\u63A5\u8BFB\u53EF\u8FBE 10MB \u7684\u539F\u59CB\u6D41",
|
|
8489
|
-
args: LineDigestArgsSchema
|
|
8490
|
-
},
|
|
8512
|
+
// ---- master:*(授权查表,Master 经 clawd-rpc 桥调)----
|
|
8491
8513
|
"master:authorityCheck": {
|
|
8492
8514
|
summary: "\u67E5 Master \u7684\u6388\u6743\u8BBE\u5B9A\uFF08\u5168\u5C40\u4E00\u5957\uFF09\uFF1A\u8FD4\u56DE ask / allow / deny\u3002\u8868\u7F3A\u5931\u3001\u8BFB\u574F\u3001\u7ED3\u6784\u4E0D\u5BF9\u3001action \u62FC\u9519\u3001id \u672A\u767B\u8BB0\uFF0C\u4E00\u5F8B\u88C1\u51B3 ask",
|
|
8493
8515
|
args: AuthorityCheckArgsSchema
|
|
@@ -14597,7 +14619,7 @@ var init_claude_history = __esm({
|
|
|
14597
14619
|
/**
|
|
14598
14620
|
* 定位一条 session 的 transcript 文件;不存在返回 null。
|
|
14599
14621
|
*
|
|
14600
|
-
* 对外开这一口是为了让 adapter
|
|
14622
|
+
* 对外开这一口是为了让 adapter 之外的消费者**不必自己拼路径**:
|
|
14601
14623
|
* 「按 cwd 直取 → 按 tsid 全局兜底」这套 relocation 逻辑曾被抄成第二份,
|
|
14602
14624
|
* 规则一改就静默漂移。路径编码(cwdToHashDir)与兜底顺序全仓只此一处。
|
|
14603
14625
|
*
|
|
@@ -52691,7 +52713,7 @@ function createTopicStore(deps) {
|
|
|
52691
52713
|
const touch = (t) => {
|
|
52692
52714
|
t.updatedAt = new Date(deps.now()).toISOString();
|
|
52693
52715
|
};
|
|
52694
|
-
const lineMetaOf = (t, lineId) => t.lineMeta[lineId] ??= { rejectCount: 0, accepted: false };
|
|
52716
|
+
const lineMetaOf = (t, lineId) => t.lineMeta[lineId] ??= { rejectCount: 0, accepted: false, overrideCount: 0 };
|
|
52695
52717
|
return {
|
|
52696
52718
|
load: file.load,
|
|
52697
52719
|
flushNow: file.flushNow,
|
|
@@ -52748,6 +52770,15 @@ function createTopicStore(deps) {
|
|
|
52748
52770
|
file.scheduleFlush();
|
|
52749
52771
|
return meta;
|
|
52750
52772
|
},
|
|
52773
|
+
bumpOverride(topicId, lineId) {
|
|
52774
|
+
const t = get(topicId);
|
|
52775
|
+
if (!t) return void 0;
|
|
52776
|
+
const meta = lineMetaOf(t, lineId);
|
|
52777
|
+
meta.overrideCount = (meta.overrideCount ?? 0) + 1;
|
|
52778
|
+
touch(t);
|
|
52779
|
+
file.scheduleFlush();
|
|
52780
|
+
return meta;
|
|
52781
|
+
},
|
|
52751
52782
|
markAccepted(topicId, lineId) {
|
|
52752
52783
|
const t = get(topicId);
|
|
52753
52784
|
if (!t) return void 0;
|
|
@@ -52757,7 +52788,7 @@ function createTopicStore(deps) {
|
|
|
52757
52788
|
file.scheduleFlush();
|
|
52758
52789
|
return meta;
|
|
52759
52790
|
},
|
|
52760
|
-
|
|
52791
|
+
setState(topicId, state) {
|
|
52761
52792
|
const t = get(topicId);
|
|
52762
52793
|
if (!t) return void 0;
|
|
52763
52794
|
t.state = state;
|
|
@@ -54387,6 +54418,21 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
54387
54418
|
);
|
|
54388
54419
|
}
|
|
54389
54420
|
}
|
|
54421
|
+
const topicId = args.meta?.topicId;
|
|
54422
|
+
if (typeof topicId === "string" && topicId && deps.getTopicMembership) {
|
|
54423
|
+
const members = deps.getTopicMembership(topicId);
|
|
54424
|
+
if (!members) {
|
|
54425
|
+
throw new ClawdError(ERROR_CODES.INVALID_PARAM, `topic not found: ${topicId}`);
|
|
54426
|
+
}
|
|
54427
|
+
const who = args.targetDeviceId ?? targetPersona;
|
|
54428
|
+
const isMember = args.targetDeviceId ? members.deviceIds.includes(args.targetDeviceId) : members.personaIds.includes(targetPersona);
|
|
54429
|
+
if (!isMember) {
|
|
54430
|
+
throw new ClawdError(
|
|
54431
|
+
ERROR_CODES.INVALID_PARAM,
|
|
54432
|
+
`${who} is not a member of the channel behind topic ${topicId} \u2014\u2014 \u5148\u628A\u5B83\u52A0\u8FDB\u9891\u9053\u518D\u6D3E\u6D3B`
|
|
54433
|
+
);
|
|
54434
|
+
}
|
|
54435
|
+
}
|
|
54390
54436
|
if (args.targetDeviceId) {
|
|
54391
54437
|
if (ctx?.principal.kind === "guest") {
|
|
54392
54438
|
throw new ClawdError(
|
|
@@ -61658,7 +61704,6 @@ var METHOD_GRANT_MAP = {
|
|
|
61658
61704
|
"topic:reject": ADMIN_ANY,
|
|
61659
61705
|
"topic:accept": ADMIN_ANY,
|
|
61660
61706
|
"topic:close": ADMIN_ANY,
|
|
61661
|
-
"line:digest": ADMIN_ANY,
|
|
61662
61707
|
"master:authorityCheck": ADMIN_ANY,
|
|
61663
61708
|
// project:*(sidebar 项目分区,spec 2026-08-17):registry 是 owner 本机资源,owner-only 一刀切
|
|
61664
61709
|
"project:list": ADMIN_ANY,
|
|
@@ -61825,7 +61870,7 @@ function computeMethodAccess(args) {
|
|
|
61825
61870
|
}
|
|
61826
61871
|
|
|
61827
61872
|
// src/version.ts
|
|
61828
|
-
var version = "0.2.
|
|
61873
|
+
var version = "0.2.386".length > 0 ? "0.2.386" : "dev";
|
|
61829
61874
|
|
|
61830
61875
|
// src/cli-probe/probe.ts
|
|
61831
61876
|
var fs66 = __toESM(require("fs"), 1);
|
|
@@ -64017,6 +64062,7 @@ function buildChannelHandlers(deps) {
|
|
|
64017
64062
|
// src/handlers/topic.ts
|
|
64018
64063
|
init_protocol();
|
|
64019
64064
|
var REJECT_ESCALATE_AT = 2;
|
|
64065
|
+
var OVERRIDE_ESCALATE_AT = 2;
|
|
64020
64066
|
function joinLines(records, lineMeta) {
|
|
64021
64067
|
const out = [];
|
|
64022
64068
|
for (const r of records) {
|
|
@@ -64104,8 +64150,9 @@ function buildTopicHandlers(deps) {
|
|
|
64104
64150
|
},
|
|
64105
64151
|
"topic:reject": async (frame) => {
|
|
64106
64152
|
const args = TopicRejectArgsSchema.parse(frame);
|
|
64107
|
-
mustGet(args.topicId);
|
|
64153
|
+
const topic = mustGet(args.topicId);
|
|
64108
64154
|
const { rejectCount } = deps.topicStore.bumpReject(args.topicId, args.lineId);
|
|
64155
|
+
if (topic.state === "done") deps.topicStore.setState(args.topicId, "running");
|
|
64109
64156
|
deps.topicStore.appendActivity(args.topicId, {
|
|
64110
64157
|
actor: "master",
|
|
64111
64158
|
action: `\u6253\u56DE ${args.lineId}\uFF08\u7B2C ${rejectCount} \u6B21\uFF09`,
|
|
@@ -64129,206 +64176,49 @@ function buildTopicHandlers(deps) {
|
|
|
64129
64176
|
);
|
|
64130
64177
|
}
|
|
64131
64178
|
deps.topicStore.markAccepted(args.topicId, args.lineId);
|
|
64179
|
+
if (args.watcherVerdict === "fail") {
|
|
64180
|
+
deps.topicStore.bumpOverride(args.topicId, args.lineId);
|
|
64181
|
+
deps.topicStore.appendActivity(args.topicId, {
|
|
64182
|
+
actor: "master",
|
|
64183
|
+
action: `\u63A8\u7FFB watcher \u5BF9 ${args.lineId} \u7684 fail \u5224\u5B9A`,
|
|
64184
|
+
target: args.lineId,
|
|
64185
|
+
reason: args.overrideReason
|
|
64186
|
+
});
|
|
64187
|
+
}
|
|
64132
64188
|
deps.topicStore.appendActivity(args.topicId, {
|
|
64133
64189
|
actor: "master",
|
|
64134
|
-
action: `\u5224\u5B9A ${args.lineId} \u5408\u683C`,
|
|
64190
|
+
action: `\u5224\u5B9A ${args.lineId} \u5408\u683C\uFF08watcher: ${args.watcherVerdict}\uFF09`,
|
|
64135
64191
|
target: args.lineId,
|
|
64136
64192
|
reason: args.reason,
|
|
64137
64193
|
...args.artifacts ? { artifacts: args.artifacts } : {}
|
|
64138
64194
|
});
|
|
64139
64195
|
const lines = linesOf(mustGet(args.topicId));
|
|
64140
64196
|
const acceptedLines = lines.filter((l) => l.accepted).length;
|
|
64197
|
+
const allAccepted = lines.length > 0 && acceptedLines === lines.length;
|
|
64198
|
+
if (allAccepted) deps.topicStore.setState(args.topicId, "done");
|
|
64199
|
+
const overrides = Object.values(mustGet(args.topicId).lineMeta).reduce(
|
|
64200
|
+
(n, m2) => n + (m2.overrideCount ?? 0),
|
|
64201
|
+
0
|
|
64202
|
+
);
|
|
64141
64203
|
return {
|
|
64142
64204
|
response: TopicAcceptResultSchema.parse({
|
|
64143
64205
|
acceptedLines,
|
|
64144
64206
|
totalLines: lines.length,
|
|
64145
|
-
allAccepted
|
|
64207
|
+
allAccepted,
|
|
64208
|
+
overrides,
|
|
64209
|
+
needsOwnerAttention: overrides >= OVERRIDE_ESCALATE_AT
|
|
64146
64210
|
})
|
|
64147
64211
|
};
|
|
64148
64212
|
},
|
|
64149
64213
|
"topic:close": async (frame) => {
|
|
64150
64214
|
const args = TopicCloseArgsSchema.parse(frame);
|
|
64151
64215
|
mustGet(args.topicId);
|
|
64152
|
-
const topic = deps.topicStore.
|
|
64216
|
+
const topic = deps.topicStore.setState(args.topicId, args.state);
|
|
64153
64217
|
return { response: TopicResultSchema.parse({ topic }) };
|
|
64154
64218
|
}
|
|
64155
64219
|
};
|
|
64156
64220
|
}
|
|
64157
64221
|
|
|
64158
|
-
// src/handlers/line.ts
|
|
64159
|
-
init_protocol();
|
|
64160
|
-
init_claude_history();
|
|
64161
|
-
|
|
64162
|
-
// src/channel/digest.ts
|
|
64163
|
-
var QUOTA = { files: 0.14, commands: 0.12, outputs: 0.22, errors: 0.24, tail: 0.28 };
|
|
64164
|
-
var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
64165
|
-
var READ_TOOLS = /* @__PURE__ */ new Set(["Read", "NotebookRead"]);
|
|
64166
|
-
var DIGEST_DEFAULTS = { tailAssistant: 12, maxBytes: 2e4 };
|
|
64167
|
-
var firstLine = (s) => String(s ?? "").split("\n")[0].trim();
|
|
64168
|
-
var errSignature = (s) => firstLine(s).replace(/\d+/g, "N").replace(/(^|[\s'"`([])\/[^\s'"`)\]]+/g, "$1<path>").slice(0, 200);
|
|
64169
|
-
var blocksOf = (rec4) => {
|
|
64170
|
-
const c = rec4?.message?.content;
|
|
64171
|
-
return Array.isArray(c) ? c : [];
|
|
64172
|
-
};
|
|
64173
|
-
function takeWithin(lines, budget) {
|
|
64174
|
-
if (!lines.length) return { out: ["- \uFF08\u65E0\uFF09"], clipped: false };
|
|
64175
|
-
const out = [];
|
|
64176
|
-
let used = 0;
|
|
64177
|
-
for (let i = 0; i < lines.length; i++) {
|
|
64178
|
-
const size = Buffer.byteLength(lines[i], "utf8") + 1;
|
|
64179
|
-
if (used + size > budget && out.length) {
|
|
64180
|
-
out.push(`- \u2026\u672C\u8282\u8FD8\u6709 ${lines.length - i} \u6761\u672A\u5217\u51FA\uFF08\u672C\u8282\u914D\u989D ${budget} \u5B57\u8282\u5DF2\u6EE1\uFF09`);
|
|
64181
|
-
return { out, clipped: true };
|
|
64182
|
-
}
|
|
64183
|
-
out.push(lines[i]);
|
|
64184
|
-
used += size;
|
|
64185
|
-
}
|
|
64186
|
-
return { out, clipped: false };
|
|
64187
|
-
}
|
|
64188
|
-
function clampBytes(text, limit) {
|
|
64189
|
-
if (Buffer.byteLength(text, "utf8") <= limit) return text;
|
|
64190
|
-
let lo = 0;
|
|
64191
|
-
let hi = text.length;
|
|
64192
|
-
while (lo < hi) {
|
|
64193
|
-
const mid = Math.ceil((lo + hi) / 2);
|
|
64194
|
-
if (Buffer.byteLength(text.slice(0, mid), "utf8") <= limit) lo = mid;
|
|
64195
|
-
else hi = mid - 1;
|
|
64196
|
-
}
|
|
64197
|
-
return text.slice(0, lo);
|
|
64198
|
-
}
|
|
64199
|
-
function buildDigest(records, opts = {}) {
|
|
64200
|
-
const maxBytes = opts.maxBytes ?? DIGEST_DEFAULTS.maxBytes;
|
|
64201
|
-
const tailAssistant = opts.tailAssistant ?? DIGEST_DEFAULTS.tailAssistant;
|
|
64202
|
-
const filesWritten = /* @__PURE__ */ new Set();
|
|
64203
|
-
const filesRead = /* @__PURE__ */ new Set();
|
|
64204
|
-
const commands = /* @__PURE__ */ new Map();
|
|
64205
|
-
const errors = /* @__PURE__ */ new Map();
|
|
64206
|
-
const outputs = [];
|
|
64207
|
-
const pendingCmd = /* @__PURE__ */ new Map();
|
|
64208
|
-
let lastCmd = null;
|
|
64209
|
-
const tail = [];
|
|
64210
|
-
let toolCalls = 0;
|
|
64211
|
-
for (const rec4 of records) {
|
|
64212
|
-
for (const b2 of blocksOf(rec4)) {
|
|
64213
|
-
if (b2.type === "tool_use") {
|
|
64214
|
-
toolCalls += 1;
|
|
64215
|
-
const fp = b2.input?.file_path ?? b2.input?.notebook_path;
|
|
64216
|
-
if (fp && b2.name && WRITE_TOOLS.has(b2.name)) filesWritten.add(String(fp));
|
|
64217
|
-
else if (fp && b2.name && READ_TOOLS.has(b2.name)) filesRead.add(String(fp));
|
|
64218
|
-
if (b2.name === "Bash" && b2.input?.command) {
|
|
64219
|
-
const c = firstLine(b2.input.command);
|
|
64220
|
-
commands.set(c, (commands.get(c) ?? 0) + 1);
|
|
64221
|
-
if (b2.id) pendingCmd.set(b2.id, c);
|
|
64222
|
-
lastCmd = c;
|
|
64223
|
-
}
|
|
64224
|
-
} else if (b2.type === "tool_result" && !b2.is_error) {
|
|
64225
|
-
const cmd = b2.tool_use_id ? pendingCmd.get(b2.tool_use_id) : lastCmd;
|
|
64226
|
-
if (cmd) {
|
|
64227
|
-
const text2 = typeof b2.content === "string" ? b2.content : JSON.stringify(b2.content ?? "");
|
|
64228
|
-
const flat = text2.replace(/\s+/g, " ").trim();
|
|
64229
|
-
if (flat) {
|
|
64230
|
-
outputs.push(`- ${cmd} \u2192 \u2026${flat.slice(-220)}`);
|
|
64231
|
-
if (outputs.length > 40) outputs.shift();
|
|
64232
|
-
}
|
|
64233
|
-
if (b2.tool_use_id) pendingCmd.delete(b2.tool_use_id);
|
|
64234
|
-
}
|
|
64235
|
-
} else if (b2.type === "tool_result" && b2.is_error) {
|
|
64236
|
-
const text2 = typeof b2.content === "string" ? b2.content : JSON.stringify(b2.content ?? "");
|
|
64237
|
-
const sig = errSignature(text2);
|
|
64238
|
-
const prev = errors.get(sig);
|
|
64239
|
-
if (prev) prev.count += 1;
|
|
64240
|
-
else errors.set(sig, { count: 1, sample: firstLine(text2).slice(0, 300) });
|
|
64241
|
-
} else if (b2.type === "text" && rec4.type === "assistant") {
|
|
64242
|
-
tail.push(b2.text ?? "");
|
|
64243
|
-
if (tail.length > tailAssistant) tail.shift();
|
|
64244
|
-
}
|
|
64245
|
-
}
|
|
64246
|
-
}
|
|
64247
|
-
const budget = (k2) => Math.max(200, Math.floor(maxBytes * QUOTA[k2]));
|
|
64248
|
-
let anyClipped = false;
|
|
64249
|
-
const take = (lines, k2) => {
|
|
64250
|
-
const r = takeWithin(lines, budget(k2));
|
|
64251
|
-
if (r.clipped) anyClipped = true;
|
|
64252
|
-
return r.out;
|
|
64253
|
-
};
|
|
64254
|
-
const errLines = [...errors.entries()].sort((x, y2) => y2[1].count - x[1].count).map(([, v2]) => `- \xD7${v2.count} ${v2.sample}`);
|
|
64255
|
-
const cmdLines = [...commands.entries()].map(([c, n]) => n > 1 ? `- \xD7${n} ${c}` : `- ${c}`);
|
|
64256
|
-
const out = [
|
|
64257
|
-
"# worker transcript digest",
|
|
64258
|
-
...opts.transcriptPath ? [`transcript: ${opts.transcriptPath}`] : [],
|
|
64259
|
-
`\u5DE5\u5177\u8C03\u7528\u603B\u6570: ${toolCalls}\uFF5C\u5199\u6587\u4EF6 ${filesWritten.size} \u4E2A\uFF5C\u8BFB\u6587\u4EF6 ${filesRead.size} \u4E2A\uFF5C\u4E0D\u540C\u547D\u4EE4 ${commands.size} \u6761\uFF5C\u62A5\u9519\u7B7E\u540D ${errors.size} \u79CD`,
|
|
64260
|
-
"",
|
|
64261
|
-
"## \u6539\u52A8\u8FC7\u7684\u6587\u4EF6\uFF08\u5F71\u54CD\u9762\uFF1B\u53EA\u7EDF\u8BA1 Write/Edit/MultiEdit/NotebookEdit\uFF09",
|
|
64262
|
-
...take([...filesWritten].map((f) => `- ${f}`), "files"),
|
|
64263
|
-
"",
|
|
64264
|
-
"## \u6267\u884C\u8FC7\u7684\u547D\u4EE4\uFF08\u91CD\u590D\u7684\u6298\u53E0\u8BA1\u6570\uFF09",
|
|
64265
|
-
...take(cmdLines, "commands"),
|
|
64266
|
-
"",
|
|
64267
|
-
"## \u62A5\u9519\uFF08\u6309\u7B7E\u540D\u805A\u5408\uFF1B\u91CD\u590D\u6B21\u6570\u9AD8 = \u649E\u5899\uFF09",
|
|
64268
|
-
...take(errLines, "errors"),
|
|
64269
|
-
"",
|
|
64270
|
-
"## \u6210\u529F\u547D\u4EE4\u7684\u8F93\u51FA\u6458\u5F55\uFF08\u5404\u53D6\u5C3E\u90E8\uFF1B\u5224\u300C\u6D4B\u8BD5\u771F\u8DD1\u8FC7\u6CA1\u3001\u8FC7\u4E86\u51E0\u6761\u300D\u770B\u8FD9\u91CC\uFF09",
|
|
64271
|
-
...take(outputs, "outputs"),
|
|
64272
|
-
"",
|
|
64273
|
-
`## \u6700\u540E ${tail.length} \u6BB5 assistant \u8F93\u51FA\uFF08\u4FDD\u7559\u539F\u6587\u6362\u884C\uFF0C\u6BCF\u6BB5\u622A\u65AD\u81F3 500 \u5B57\uFF09`,
|
|
64274
|
-
// 只留首行会把 worker 的收尾报告砍成「做完了」这种没信息量的一句,
|
|
64275
|
-
// 而那正是判「假完成」最需要的原文。
|
|
64276
|
-
...take(tail.map((t) => `- ${String(t ?? "").slice(0, 500)}`), "tail")
|
|
64277
|
-
];
|
|
64278
|
-
let text = out.join("\n");
|
|
64279
|
-
let truncated = anyClipped;
|
|
64280
|
-
if (Buffer.byteLength(text, "utf8") > maxBytes) {
|
|
64281
|
-
text = clampBytes(text, maxBytes);
|
|
64282
|
-
text += `
|
|
64283
|
-
|
|
64284
|
-
\u26A0\uFE0F digest \u5DF2\u622A\u65AD\uFF08\u4E0A\u9650 ${maxBytes} \u5B57\u8282\uFF09\u2014\u2014\u672A\u89C1\u5168\u90E8\u5185\u5BB9\uFF0C\u5224\u5B9A"\u6CA1\u505A"\u524D\u5148\u8981\u6C42 worker \u81EA\u8BC1\u3002`;
|
|
64285
|
-
truncated = true;
|
|
64286
|
-
} else if (anyClipped) {
|
|
64287
|
-
text += `
|
|
64288
|
-
|
|
64289
|
-
\u26A0\uFE0F digest \u5DF2\u622A\u65AD\uFF08\u6709\u5C0F\u8282\u8D85\u51FA\u914D\u989D\uFF0C\u89C1\u4E0A\u65B9\u300C\u672A\u5217\u51FA\u300D\u6807\u6CE8\uFF09\u2014\u2014\u672A\u89C1\u5168\u90E8\u5185\u5BB9\uFF0C\u5224\u5B9A"\u6CA1\u505A"\u524D\u5148\u8981\u6C42 worker \u81EA\u8BC1\u3002`;
|
|
64290
|
-
}
|
|
64291
|
-
return { digest: text, truncated };
|
|
64292
|
-
}
|
|
64293
|
-
|
|
64294
|
-
// src/handlers/line.ts
|
|
64295
|
-
function buildLineHandlers(deps) {
|
|
64296
|
-
return {
|
|
64297
|
-
"line:digest": async (frame) => {
|
|
64298
|
-
const args = LineDigestArgsSchema.parse(frame);
|
|
64299
|
-
const rec4 = deps.dispatchStore.get(args.dispatchId);
|
|
64300
|
-
if (!rec4) {
|
|
64301
|
-
throw new ClawdError(ERROR_CODES.INVALID_PARAM, `dispatch not found: ${args.dispatchId}`);
|
|
64302
|
-
}
|
|
64303
|
-
if (!rec4.workerSessionId) {
|
|
64304
|
-
throw new ClawdError(
|
|
64305
|
-
ERROR_CODES.INVALID_PARAM,
|
|
64306
|
-
`\u8BE5\u7EBF\u8FD8\u6CA1\u6709 worker \u4F1A\u8BDD\uFF08spawn \u672A\u5B8C\u6210\uFF09\uFF0C\u6682\u65F6\u65E0\u6CD5\u6838\u9A8C: ${args.dispatchId}`
|
|
64307
|
-
);
|
|
64308
|
-
}
|
|
64309
|
-
const sf = deps.getSessionFile(rec4.workerSessionId);
|
|
64310
|
-
if (!sf?.toolSessionId) {
|
|
64311
|
-
throw new ClawdError(
|
|
64312
|
-
ERROR_CODES.INVALID_PARAM,
|
|
64313
|
-
`worker \u4F1A\u8BDD\u5C1A\u672A\u4EA7\u751F transcript\uFF08\u8FD8\u6CA1\u8DD1\u8D77\u6765\uFF09: ${rec4.workerSessionId}`
|
|
64314
|
-
);
|
|
64315
|
-
}
|
|
64316
|
-
const file = deps.history.resolveTranscriptFile(sf.cwd, sf.toolSessionId);
|
|
64317
|
-
if (!file) {
|
|
64318
|
-
throw new ClawdError(
|
|
64319
|
-
ERROR_CODES.INVALID_PARAM,
|
|
64320
|
-
`transcript \u4E0D\u5B58\u5728\uFF08worker \u53EF\u80FD\u8FD8\u6CA1\u5199\u76D8\uFF09: ${sf.toolSessionId}`
|
|
64321
|
-
);
|
|
64322
|
-
}
|
|
64323
|
-
return {
|
|
64324
|
-
response: LineDigestResultSchema.parse(
|
|
64325
|
-
buildDigest(streamJsonlLines(file), { transcriptPath: file })
|
|
64326
|
-
)
|
|
64327
|
-
};
|
|
64328
|
-
}
|
|
64329
|
-
};
|
|
64330
|
-
}
|
|
64331
|
-
|
|
64332
64222
|
// src/handlers/master.ts
|
|
64333
64223
|
init_protocol();
|
|
64334
64224
|
|
|
@@ -64964,12 +64854,6 @@ function buildMethodHandlers(deps) {
|
|
|
64964
64854
|
...buildMasterHandlers({
|
|
64965
64855
|
authorityTablePath: import_node_path67.default.join(deps.masterDataDir, "master-authority.json")
|
|
64966
64856
|
}),
|
|
64967
|
-
...buildLineHandlers({
|
|
64968
|
-
dispatchStore: deps.dispatchStore,
|
|
64969
|
-
getSessionFile: deps.getWorkerSessionFile,
|
|
64970
|
-
// transcript 定位/读取复用 history:* 那个同一个 reader,不另起一份路径规则
|
|
64971
|
-
history: deps.history
|
|
64972
|
-
}),
|
|
64973
64857
|
...buildTopicHandlers({
|
|
64974
64858
|
topicStore: deps.topicStore,
|
|
64975
64859
|
channelStore: deps.channelStore,
|
|
@@ -66440,17 +66324,12 @@ async function startDaemon(config) {
|
|
|
66440
66324
|
channelStore,
|
|
66441
66325
|
topicStore,
|
|
66442
66326
|
dispatchStore,
|
|
66443
|
-
// line:digest 用:worker session 的 cwd / toolSessionId(transcript 定位的输入)
|
|
66444
|
-
getWorkerSessionFile: (sessionId) => {
|
|
66445
|
-
const f = manager.findOwnedSession(sessionId);
|
|
66446
|
-
return f ? { cwd: f.cwd, ...f.toolSessionId ? { toolSessionId: f.toolSessionId } : {} } : void 0;
|
|
66447
|
-
},
|
|
66448
66327
|
masterDataDir: config.dataDir,
|
|
66449
66328
|
// 建话题即起该话题的 Master session(1:1)。走与 session:create 同一条 manager.create
|
|
66450
66329
|
// 链路,不复制会话创建逻辑;creatorPrincipalId 用本机 deviceId(owner ctx 的 principal.id)。
|
|
66451
66330
|
createMasterSession: ({ label }) => {
|
|
66452
66331
|
const { response } = manager.create(
|
|
66453
|
-
{ ownerPersonaId:
|
|
66332
|
+
{ ownerPersonaId: MASTER_PERSONA_ID, label },
|
|
66454
66333
|
authFile.deviceId,
|
|
66455
66334
|
ownerDisplayName
|
|
66456
66335
|
);
|
|
@@ -66683,6 +66562,15 @@ async function startDaemon(config) {
|
|
|
66683
66562
|
const dispatchHandlers = buildPersonaDispatchHandlers({
|
|
66684
66563
|
personaDispatchManager,
|
|
66685
66564
|
store: dispatchStore,
|
|
66565
|
+
// 话题工作线的派活边界:topicId → 它所属频道的两张成员名单。频道 / 话题任一不存在
|
|
66566
|
+
// 都返回 undefined,由 handler 判成「话题不存在」拒掉(编 topicId 绕不过去)。
|
|
66567
|
+
getTopicMembership: (topicId) => {
|
|
66568
|
+
const t = topicStore.get(topicId);
|
|
66569
|
+
if (!t) return void 0;
|
|
66570
|
+
const ch = channelStore.get(t.channelId);
|
|
66571
|
+
if (!ch) return void 0;
|
|
66572
|
+
return { personaIds: [...ch.memberPersonaIds], deviceIds: [...ch.memberContactDeviceIds] };
|
|
66573
|
+
},
|
|
66686
66574
|
// 续派(spec 2026-07-21):任务包注入 worker session,复用投递同款注入路径。
|
|
66687
66575
|
sendFollowUp: (workerSessionId, text) => manager.injectIntoSession(workerSessionId, text),
|
|
66688
66576
|
logger,
|
|
@@ -21820,7 +21820,8 @@ var ChannelSchema = external_exports.object({
|
|
|
21820
21820
|
name: external_exports.string().min(1),
|
|
21821
21821
|
/** 该频道里可被派活的 persona */
|
|
21822
21822
|
memberPersonaIds: external_exports.array(external_exports.string().min(1)),
|
|
21823
|
-
/** 人类成员(contact deviceId
|
|
21823
|
+
/** 人类成员(contact deviceId)。跨设备派活的成员边界认这张名单(personaDispatch 校验),
|
|
21824
|
+
* P1 人类异步询问也用它 */
|
|
21824
21825
|
memberContactDeviceIds: external_exports.array(external_exports.string().min(1)),
|
|
21825
21826
|
createdAt: external_exports.string().min(1),
|
|
21826
21827
|
updatedAt: external_exports.string().min(1)
|
|
@@ -21861,7 +21862,18 @@ var TopicActivityEntrySchema = external_exports.object({
|
|
|
21861
21862
|
});
|
|
21862
21863
|
var TopicLineMetaSchema = external_exports.object({
|
|
21863
21864
|
rejectCount: external_exports.number().int().min(0),
|
|
21864
|
-
accepted: external_exports.boolean().default(false)
|
|
21865
|
+
accepted: external_exports.boolean().default(false),
|
|
21866
|
+
/**
|
|
21867
|
+
* Master 在 watcher 判 fail 的情况下仍判合格的次数。
|
|
21868
|
+
*
|
|
21869
|
+
* 为什么要单独数:watcher 的 fail 若能被一句「这是可观测性缺口」无声绕过,核验就形同
|
|
21870
|
+
* 虚设——实测一个话题三条线的 watcher 全报 fail、无一打回,Master 自己下场补证后判了合格。
|
|
21871
|
+
* 覆盖本身不禁止(watcher 确实会误判),但必须留痕、必须计数、反复覆盖必须惊动老板。
|
|
21872
|
+
*
|
|
21873
|
+
* optional 而非 default(0):store 不按 schema parse(见 topic-store),存量 lineMeta
|
|
21874
|
+
* 里本来就没有这个字段,读侧一律 `?? 0`。
|
|
21875
|
+
*/
|
|
21876
|
+
overrideCount: external_exports.number().int().min(0).optional()
|
|
21865
21877
|
});
|
|
21866
21878
|
var TopicSchema = external_exports.object({
|
|
21867
21879
|
id: external_exports.string().min(1),
|
|
@@ -21931,7 +21943,17 @@ var TopicAcceptArgsSchema = external_exports.object({
|
|
|
21931
21943
|
/** 凭什么判合格(逐条标准对应到什么证据)——与打回同样必填 */
|
|
21932
21944
|
reason: nonBlank2,
|
|
21933
21945
|
/** watcher 认定的产出物,一并进影响面 */
|
|
21934
|
-
artifacts: external_exports.array(nonBlank2).optional()
|
|
21946
|
+
artifacts: external_exports.array(nonBlank2).optional(),
|
|
21947
|
+
/**
|
|
21948
|
+
* 这条线的 watcher 核验结论。**必填**:不填等于允许「压根没跑 watcher 就发合格证」,
|
|
21949
|
+
* 而合格与打回之所以要对称落账,就是为了让老板事后分得出「判过且过了」和「没judge过」。
|
|
21950
|
+
*/
|
|
21951
|
+
watcherVerdict: external_exports.enum(["pass", "fail"]),
|
|
21952
|
+
/** watcher 判 fail 却仍要判合格时,凭什么推翻它(fail 时必填) */
|
|
21953
|
+
overrideReason: nonBlank2.optional()
|
|
21954
|
+
}).refine((v) => v.watcherVerdict !== "fail" || Boolean(v.overrideReason), {
|
|
21955
|
+
message: "watcherVerdict=fail \u65F6\u5FC5\u987B\u7ED9 overrideReason\uFF08\u63A8\u7FFB watcher \u7684\u7406\u7531\uFF09",
|
|
21956
|
+
path: ["overrideReason"]
|
|
21935
21957
|
});
|
|
21936
21958
|
var TopicCloseArgsSchema = external_exports.object({
|
|
21937
21959
|
topicId: external_exports.string().min(1),
|
|
@@ -21958,20 +21980,18 @@ var TopicAcceptResultSchema = external_exports.object({
|
|
|
21958
21980
|
acceptedLines: external_exports.number().int().min(1),
|
|
21959
21981
|
totalLines: external_exports.number().int().min(1),
|
|
21960
21982
|
/** 全部线都合格了 → 可以收敛上报。由 daemon 数,不让 Master 自己盘 */
|
|
21961
|
-
allAccepted: external_exports.boolean()
|
|
21983
|
+
allAccepted: external_exports.boolean(),
|
|
21984
|
+
/** 本话题累计推翻 watcher 的次数(含这一次) */
|
|
21985
|
+
overrides: external_exports.number().int().min(0),
|
|
21986
|
+
/**
|
|
21987
|
+
* 反复推翻 watcher = 取证链路有系统性问题(如 worker 用 heredoc 写文件导致正文
|
|
21988
|
+
* 写得没法验),该让老板知道并去修验收标准,而不是每条线都靠 Master 补证。
|
|
21989
|
+
*/
|
|
21990
|
+
needsOwnerAttention: external_exports.boolean()
|
|
21962
21991
|
});
|
|
21963
21992
|
var TopicResultSchema = external_exports.object({ topic: TopicSchema });
|
|
21964
21993
|
|
|
21965
21994
|
// ../protocol/src/master.ts
|
|
21966
|
-
var LineDigestArgsSchema = external_exports.object({
|
|
21967
|
-
/** 一条线 = 一条 dispatch;daemon 内部据此解析 worker session 与 transcript */
|
|
21968
|
-
dispatchId: external_exports.string().min(1)
|
|
21969
|
-
});
|
|
21970
|
-
var LineDigestResultSchema = external_exports.object({
|
|
21971
|
-
digest: external_exports.string(),
|
|
21972
|
-
/** 抽取时是否因超上限被截断——watcher 据此避免"仅凭没看到就判 fail" */
|
|
21973
|
-
truncated: external_exports.boolean()
|
|
21974
|
-
});
|
|
21975
21995
|
var AUTHORITY_ACTIONS = ["ask", "allow", "deny"];
|
|
21976
21996
|
var AuthorityActionSchema = external_exports.enum(AUTHORITY_ACTIONS);
|
|
21977
21997
|
var AuthorityRuleSchema = external_exports.object({
|
|
@@ -23537,18 +23557,19 @@ var METHOD_DOCS = {
|
|
|
23537
23557
|
args: TopicRejectArgsSchema
|
|
23538
23558
|
},
|
|
23539
23559
|
"topic:accept": {
|
|
23540
|
-
summary: "\u5224\u67D0\u6761\u7EBF\u5408\u683C\uFF1A\u7F6E accepted \u5E76\u540C\u6B65\u843D\u4E00\u6761\u6D41\u6C34\uFF1Breason \u5FC5\u586B\uFF08\u4E0E\u6253\u56DE\u5BF9\u79F0\uFF0C\u53EA\u8BB0\u6253\u56DE\u7B49\u4E8E\u53EA\u8BB0\u5206\u5B50\uFF09\u3002\u8FD4\u56DE acceptedLines / totalLines / allAccepted\uFF0C\u6536\u655B\u65F6\u673A\u7531 daemon \u6570\u4E0D\u7531 Master \u76D8",
|
|
23541
|
-
args: TopicAcceptArgsSchema
|
|
23560
|
+
summary: "\u5224\u67D0\u6761\u7EBF\u5408\u683C\uFF1A\u7F6E accepted \u5E76\u540C\u6B65\u843D\u4E00\u6761\u6D41\u6C34\uFF1Breason \u4E0E watcherVerdict \u5FC5\u586B\uFF08\u4E0E\u6253\u56DE\u5BF9\u79F0\uFF0C\u53EA\u8BB0\u6253\u56DE\u7B49\u4E8E\u53EA\u8BB0\u5206\u5B50\uFF09\u3002watcherVerdict=fail \u65F6\u5FC5\u987B\u7ED9 overrideReason\uFF0Cdaemon \u5355\u72EC\u8BA1\u63A8\u7FFB\u6B21\u6570\u3002\u8FD4\u56DE acceptedLines / totalLines / allAccepted / overrides / needsOwnerAttention\uFF0C\u6536\u655B\u65F6\u673A\u7531 daemon \u6570\u4E0D\u7531 Master \u76D8",
|
|
23561
|
+
args: TopicAcceptArgsSchema,
|
|
23562
|
+
argsConstraints: {
|
|
23563
|
+
// watcherVerdict=fail 时 overrideReason 必填(.refine 的谓词不可序列化,这里显式声明)
|
|
23564
|
+
if: { properties: { watcherVerdict: { const: "fail" } }, required: ["watcherVerdict"] },
|
|
23565
|
+
then: { required: ["overrideReason"] }
|
|
23566
|
+
}
|
|
23542
23567
|
},
|
|
23543
23568
|
"topic:close": {
|
|
23544
23569
|
summary: "\u6536\u655B\u8BDD\u9898\uFF1Astate \u7F6E done / archived",
|
|
23545
23570
|
args: TopicCloseArgsSchema
|
|
23546
23571
|
},
|
|
23547
|
-
// ----
|
|
23548
|
-
"line:digest": {
|
|
23549
|
-
summary: "\u628A\u4E00\u6761\u7EBF\uFF08dispatch\uFF09\u7684 worker transcript \u62BD\u6210 \u226420KB digest\uFF1A\u6539\u52A8\u8FC7\u7684\u6587\u4EF6 / \u6267\u884C\u8FC7\u7684\u547D\u4EE4 / \u6210\u529F\u547D\u4EE4\u8F93\u51FA\u6458\u5F55 / \u62A5\u9519\u805A\u5408 / \u5C3E\u90E8 assistant \u8F93\u51FA\u3002\u6838\u9A8C subagent \u7528\u5B83\uFF0C\u907F\u514D\u76F4\u63A5\u8BFB\u53EF\u8FBE 10MB \u7684\u539F\u59CB\u6D41",
|
|
23550
|
-
args: LineDigestArgsSchema
|
|
23551
|
-
},
|
|
23572
|
+
// ---- master:*(授权查表,Master 经 clawd-rpc 桥调)----
|
|
23552
23573
|
"master:authorityCheck": {
|
|
23553
23574
|
summary: "\u67E5 Master \u7684\u6388\u6743\u8BBE\u5B9A\uFF08\u5168\u5C40\u4E00\u5957\uFF09\uFF1A\u8FD4\u56DE ask / allow / deny\u3002\u8868\u7F3A\u5931\u3001\u8BFB\u574F\u3001\u7ED3\u6784\u4E0D\u5BF9\u3001action \u62FC\u9519\u3001id \u672A\u767B\u8BB0\uFF0C\u4E00\u5F8B\u88C1\u51B3 ask",
|
|
23554
23575
|
args: AuthorityCheckArgsSchema
|