@clawos-dev/clawd 0.2.599 → 0.2.601
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 +174 -37
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -46612,6 +46612,7 @@ function applyCommand(state, command, deps) {
|
|
|
46612
46612
|
resolvedModel: _model,
|
|
46613
46613
|
// aiLabel 是旧对话内容生成的标题,/clear 后属于不存在的话题,一并清掉
|
|
46614
46614
|
aiLabel: _aiLabel,
|
|
46615
|
+
aiLabelGeneratedAt: _titleGeneratedAt,
|
|
46615
46616
|
...rest
|
|
46616
46617
|
} = next.file;
|
|
46617
46618
|
void _drop;
|
|
@@ -46620,6 +46621,7 @@ function applyCommand(state, command, deps) {
|
|
|
46620
46621
|
void _branch;
|
|
46621
46622
|
void _model;
|
|
46622
46623
|
void _aiLabel;
|
|
46624
|
+
void _titleGeneratedAt;
|
|
46623
46625
|
const nextFile = {
|
|
46624
46626
|
...rest,
|
|
46625
46627
|
...command.nextToolSessionId ? { toolSessionId: command.nextToolSessionId } : {},
|
|
@@ -47686,7 +47688,7 @@ var SessionManager = class {
|
|
|
47686
47688
|
// sessionId → SessionRunner;在 send / ensureSession 时按需创建
|
|
47687
47689
|
runners = /* @__PURE__ */ new Map();
|
|
47688
47690
|
// 标题生成 in-flight 防重(进程内);跨重启防重靠 SessionFile.aiLabelGeneratedAt
|
|
47689
|
-
titleGenPending = /* @__PURE__ */ new
|
|
47691
|
+
titleGenPending = /* @__PURE__ */ new Map();
|
|
47690
47692
|
// 当同步命令执行期间,runner 的 emit-frame 临时 push 到这里;
|
|
47691
47693
|
// null 表示当前是异步路径(stdout 'data' / proc 'exit' 等),走 deps.broadcastFrame
|
|
47692
47694
|
currentCollector = null;
|
|
@@ -48445,6 +48447,10 @@ var SessionManager = class {
|
|
|
48445
48447
|
}
|
|
48446
48448
|
update(args) {
|
|
48447
48449
|
const existing = this.getFile(args.sessionId);
|
|
48450
|
+
if (args.patch.model !== void 0 || args.patch.label) {
|
|
48451
|
+
this.titleGenPending.get(args.sessionId)?.abort();
|
|
48452
|
+
this.titleGenPending.delete(args.sessionId);
|
|
48453
|
+
}
|
|
48448
48454
|
const runner = this.runners.get(args.sessionId);
|
|
48449
48455
|
if (runner) {
|
|
48450
48456
|
const { value, broadcast } = this.withCollector(() => {
|
|
@@ -48454,6 +48460,12 @@ var SessionManager = class {
|
|
|
48454
48460
|
return { response: value, broadcast };
|
|
48455
48461
|
}
|
|
48456
48462
|
const updated = { ...existing, ...args.patch, updatedAt: nowIso2(this.deps) };
|
|
48463
|
+
if (args.patch.model !== void 0) {
|
|
48464
|
+
delete updated.resolvedModel;
|
|
48465
|
+
delete updated.resolvedEffort;
|
|
48466
|
+
} else if (args.patch.effort !== void 0) {
|
|
48467
|
+
delete updated.resolvedEffort;
|
|
48468
|
+
}
|
|
48457
48469
|
this.writeOwned(updated);
|
|
48458
48470
|
return { response: updated, broadcast: [] };
|
|
48459
48471
|
}
|
|
@@ -48517,6 +48529,7 @@ var SessionManager = class {
|
|
|
48517
48529
|
delete(args) {
|
|
48518
48530
|
const existing = this.findOwnedSession(args.sessionId);
|
|
48519
48531
|
if (!existing) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, args.sessionId);
|
|
48532
|
+
this.titleGenPending.get(args.sessionId)?.abort();
|
|
48520
48533
|
const scope = this.scopeForFile(existing);
|
|
48521
48534
|
const runner = this.runners.get(args.sessionId);
|
|
48522
48535
|
if (runner) {
|
|
@@ -48580,7 +48593,7 @@ var SessionManager = class {
|
|
|
48580
48593
|
/**
|
|
48581
48594
|
* 首条 send 触发的智能标题自产(spec:PR #1196 追加拍板 2026-08-17)。
|
|
48582
48595
|
* 背景见 tools/title-generator.ts 头注释——CC/codex 两条上游标题数据源都等不来
|
|
48583
|
-
* 「首轮即有」,daemon
|
|
48596
|
+
* 「首轮即有」,daemon 用本会话选定的引擎和模型自己生成,与主 turn 并行,完成后经
|
|
48584
48597
|
* manager.update 内部通道写 aiLabel(marker patch:不刷 updatedAt、广播 session:info)。
|
|
48585
48598
|
*
|
|
48586
48599
|
* 跳过条件:
|
|
@@ -48595,15 +48608,20 @@ var SessionManager = class {
|
|
|
48595
48608
|
const sessionId = file.sessionId;
|
|
48596
48609
|
if (file.aiLabelGeneratedAt || file.label || file.larkChatId) return;
|
|
48597
48610
|
if (this.titleGenPending.has(sessionId)) return;
|
|
48598
|
-
|
|
48599
|
-
|
|
48611
|
+
const controller = new AbortController();
|
|
48612
|
+
this.titleGenPending.set(sessionId, controller);
|
|
48613
|
+
void generate(file, text, controller.signal).then((title) => {
|
|
48614
|
+
if (controller.signal.aborted) return;
|
|
48615
|
+
const current = this.findOwnedSession(sessionId);
|
|
48616
|
+
if (!current || current.label || current.aiLabelGeneratedAt || current.tool !== file.tool || current.model !== file.model) return;
|
|
48600
48617
|
if (!title) return;
|
|
48601
48618
|
this.applyResolvedMeta(sessionId, { aiLabel: title });
|
|
48602
48619
|
this.deps.logger?.info("title-gen ok", { sessionId, tool: file.tool, title });
|
|
48603
48620
|
}).catch((err) => {
|
|
48621
|
+
if (controller.signal.aborted) return;
|
|
48604
48622
|
this.deps.logger?.warn("title-gen failed", { sessionId, message: err.message });
|
|
48605
48623
|
}).finally(() => {
|
|
48606
|
-
this.titleGenPending.delete(sessionId);
|
|
48624
|
+
if (this.titleGenPending.get(sessionId) === controller) this.titleGenPending.delete(sessionId);
|
|
48607
48625
|
});
|
|
48608
48626
|
}
|
|
48609
48627
|
/**
|
|
@@ -48842,6 +48860,8 @@ var SessionManager = class {
|
|
|
48842
48860
|
}
|
|
48843
48861
|
newSession(args) {
|
|
48844
48862
|
const existingFile = this.getFile(args.sessionId);
|
|
48863
|
+
this.titleGenPending.get(args.sessionId)?.abort();
|
|
48864
|
+
this.titleGenPending.delete(args.sessionId);
|
|
48845
48865
|
const nextToolSessionId = this.deps.mode === "tui" && (existingFile.tool ?? "claude") === "claude" ? (0, import_node_crypto3.randomUUID)() : void 0;
|
|
48846
48866
|
this.clearPendingTurnEnd(args.sessionId);
|
|
48847
48867
|
const runner = this.runners.get(args.sessionId);
|
|
@@ -48862,6 +48882,8 @@ var SessionManager = class {
|
|
|
48862
48882
|
contextWindowSize: _win,
|
|
48863
48883
|
gitBranch: _branch,
|
|
48864
48884
|
resolvedModel: _model,
|
|
48885
|
+
aiLabel: _aiLabel,
|
|
48886
|
+
aiLabelGeneratedAt: _titleGeneratedAt,
|
|
48865
48887
|
...rest
|
|
48866
48888
|
} = existing;
|
|
48867
48889
|
void _drop;
|
|
@@ -48869,6 +48891,8 @@ var SessionManager = class {
|
|
|
48869
48891
|
void _win;
|
|
48870
48892
|
void _branch;
|
|
48871
48893
|
void _model;
|
|
48894
|
+
void _aiLabel;
|
|
48895
|
+
void _titleGeneratedAt;
|
|
48872
48896
|
const updated = {
|
|
48873
48897
|
...rest,
|
|
48874
48898
|
...nextToolSessionId ? { toolSessionId: nextToolSessionId } : {},
|
|
@@ -48941,6 +48965,8 @@ var SessionManager = class {
|
|
|
48941
48965
|
return { runningSessions: running };
|
|
48942
48966
|
}
|
|
48943
48967
|
stopAll() {
|
|
48968
|
+
for (const controller of this.titleGenPending.values()) controller.abort();
|
|
48969
|
+
this.titleGenPending.clear();
|
|
48944
48970
|
for (const r2 of this.runners.values()) {
|
|
48945
48971
|
r2.input({ kind: "command", command: { kind: "stop" } });
|
|
48946
48972
|
}
|
|
@@ -49117,6 +49143,8 @@ var SessionManager = class {
|
|
|
49117
49143
|
`sub-session not found: ${scopeKey(args.scope)}/${args.sessionId}`
|
|
49118
49144
|
);
|
|
49119
49145
|
}
|
|
49146
|
+
this.titleGenPending.get(args.sessionId)?.abort();
|
|
49147
|
+
this.titleGenPending.delete(args.sessionId);
|
|
49120
49148
|
const runner = this.ensureRunnerForScope(file, args.scope);
|
|
49121
49149
|
const nextToolSessionId = this.deps.mode === "tui" && (file.tool ?? "claude") === "claude" ? (0, import_node_crypto3.randomUUID)() : void 0;
|
|
49122
49150
|
const { broadcast } = this.withCollector(() => {
|
|
@@ -53763,7 +53791,7 @@ function driveCodexThread(opts) {
|
|
|
53763
53791
|
if (opts.skillsExtraRoots?.length) {
|
|
53764
53792
|
void client.request("skills/extraRoots/set", { extraRoots: opts.skillsExtraRoots }).catch((e) => logger?.warn("codex skills/extraRoots/set failed", { message: e.message }));
|
|
53765
53793
|
}
|
|
53766
|
-
const threadParams = typeof opts.threadParams === "function" ? await opts.threadParams() : opts.threadParams;
|
|
53794
|
+
const threadParams = typeof opts.threadParams === "function" ? await opts.threadParams(client) : opts.threadParams;
|
|
53767
53795
|
if (threadId) {
|
|
53768
53796
|
await client.request("thread/resume", { threadId, ...threadParams });
|
|
53769
53797
|
} else {
|
|
@@ -64352,6 +64380,7 @@ var CloudGatewayClient = class {
|
|
|
64352
64380
|
* `toolSessionId`:调用方预分配的引擎会话 id(pi 的 --session-id;codex 的 thread id 是协议内的、不传),进 open 的 body `engineSessionId`
|
|
64353
64381
|
* (契约 2026-09-18 §3.2)。`instructions`:会话级指令(pi-cloud 的连接 intro,工作目录写 CLOUD_WORKSPACE_SLOT),进 body `instructions`,
|
|
64354
64382
|
* gateway 落成 Pod 里的 /run/agent/instructions.md(契约 §3.1);codex-cloud 不发(走协议内 developerInstructions)。
|
|
64383
|
+
* `expectedEngine` 是 session 创建时的引擎;gateway 在起 Pod 前与当前发布核对。
|
|
64355
64384
|
*/
|
|
64356
64385
|
async openSession(personaId2, opts = {}) {
|
|
64357
64386
|
const { apiKey, gatewayUrl } = await this.credentials();
|
|
@@ -64362,6 +64391,8 @@ var CloudGatewayClient = class {
|
|
|
64362
64391
|
personaId: personaId2,
|
|
64363
64392
|
...opts.toolSessionId ? { engineSessionId: opts.toolSessionId } : {},
|
|
64364
64393
|
...opts.instructions ? { instructions: opts.instructions } : {},
|
|
64394
|
+
...opts.purpose ? { purpose: opts.purpose } : {},
|
|
64395
|
+
...opts.expectedEngine ? { expectedEngine: opts.expectedEngine } : {},
|
|
64365
64396
|
logger: this.deps.logger
|
|
64366
64397
|
});
|
|
64367
64398
|
}
|
|
@@ -64419,7 +64450,9 @@ function parseControlLine(line) {
|
|
|
64419
64450
|
kind: "ready",
|
|
64420
64451
|
session: typeof r2.session === "string" ? r2.session : "",
|
|
64421
64452
|
personaDir: typeof r2.personaDir === "string" ? r2.personaDir : "",
|
|
64422
|
-
workDir: typeof r2.workDir === "string" ? r2.workDir : ""
|
|
64453
|
+
workDir: typeof r2.workDir === "string" ? r2.workDir : "",
|
|
64454
|
+
...typeof r2.model === "string" ? { model: r2.model } : {},
|
|
64455
|
+
...typeof r2.engine === "string" ? { engine: r2.engine } : {}
|
|
64423
64456
|
};
|
|
64424
64457
|
case "error":
|
|
64425
64458
|
return { kind: "error", message: typeof r2.message === "string" ? r2.message : "gateway error" };
|
|
@@ -64428,7 +64461,7 @@ function parseControlLine(line) {
|
|
|
64428
64461
|
}
|
|
64429
64462
|
}
|
|
64430
64463
|
function openHttpLineStream(opts) {
|
|
64431
|
-
const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, engineSessionId, instructions, logger } = opts;
|
|
64464
|
+
const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, engineSessionId, instructions, purpose, expectedEngine, logger } = opts;
|
|
64432
64465
|
const headers = { authorization: `Bearer ${apiKey}`, "content-type": "application/json" };
|
|
64433
64466
|
const ac = new AbortController();
|
|
64434
64467
|
const lineCbs = [];
|
|
@@ -64496,7 +64529,7 @@ function openHttpLineStream(opts) {
|
|
|
64496
64529
|
res = await fetchImpl(gatewayUrl + GATEWAY_ROUTES.session(personaId2), {
|
|
64497
64530
|
method: "POST",
|
|
64498
64531
|
headers,
|
|
64499
|
-
body: JSON.stringify({ ...engineSessionId ? { engineSessionId } : {}, ...instructions ? { instructions } : {} }),
|
|
64532
|
+
body: JSON.stringify({ ...engineSessionId ? { engineSessionId } : {}, ...instructions ? { instructions } : {}, ...purpose ? { purpose } : {}, ...expectedEngine ? { expectedEngine } : {} }),
|
|
64500
64533
|
signal: ac.signal
|
|
64501
64534
|
});
|
|
64502
64535
|
} catch (e) {
|
|
@@ -64510,6 +64543,7 @@ function openHttpLineStream(opts) {
|
|
|
64510
64543
|
const body = await res.json();
|
|
64511
64544
|
if (typeof body.message === "string") message = body.message;
|
|
64512
64545
|
else if (typeof body.error === "string") message = body.error;
|
|
64546
|
+
if (body.error === "SESSION_VERSION_EXPIRED" && !message.includes("SESSION_VERSION_EXPIRED")) message = `SESSION_VERSION_EXPIRED: ${message}`;
|
|
64513
64547
|
} catch {
|
|
64514
64548
|
}
|
|
64515
64549
|
emitControl({ kind: "error", message });
|
|
@@ -64550,6 +64584,14 @@ function openHttpLineStream(opts) {
|
|
|
64550
64584
|
});
|
|
64551
64585
|
break;
|
|
64552
64586
|
}
|
|
64587
|
+
if (expectedEngine && control.engine !== expectedEngine) {
|
|
64588
|
+
emitControl({ kind: "error", message: control.engine ? `SESSION_VERSION_EXPIRED: \u5F53\u524D Pod \u662F ${control.engine}\uFF0C\u8FD9\u6761\u4F1A\u8BDD\u9700\u8981 ${expectedEngine}` : "gateway \u7684 ready \u63A7\u5236\u884C\u6CA1\u5E26 engine\uFF0Cgateway \u592A\u8001\uFF0C\u5148\u5347 gateway" });
|
|
64589
|
+
stop = true;
|
|
64590
|
+
ac.abort();
|
|
64591
|
+
void reader.cancel().catch(() => {
|
|
64592
|
+
});
|
|
64593
|
+
break;
|
|
64594
|
+
}
|
|
64553
64595
|
sessionId = control.session;
|
|
64554
64596
|
flush();
|
|
64555
64597
|
}
|
|
@@ -64613,8 +64655,8 @@ init_protocol();
|
|
|
64613
64655
|
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
64614
64656
|
function cloudThreadParams(ctx, paths) {
|
|
64615
64657
|
const { model: _model, ...base } = threadStartParams(ctx);
|
|
64616
|
-
const developerInstructions = [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
|
|
64617
|
-
return { ...base, cwd: paths.personaDir, approvalPolicy: "never", sandbox: "danger-full-access", developerInstructions };
|
|
64658
|
+
const developerInstructions = ctx.titleOnly ? "\u53EA\u6839\u636E\u7528\u6237\u6D88\u606F\u8FD4\u56DE\u7B80\u77ED\u6807\u9898\u3002\u4E0D\u8981\u8C03\u7528\u5DE5\u5177\u3001\u8BFB\u53D6\u6587\u4EF6\u3001\u8BBF\u95EE\u7F51\u7EDC\u6216\u4FEE\u6539\u4EFB\u4F55\u5185\u5BB9\u3002" : [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
|
|
64659
|
+
return { ...base, ...paths.model ? { model: paths.model } : {}, cwd: paths.personaDir, approvalPolicy: "never", sandbox: ctx.titleOnly ? "read-only" : "danger-full-access", developerInstructions };
|
|
64618
64660
|
}
|
|
64619
64661
|
function deferredLineStream() {
|
|
64620
64662
|
let target = null;
|
|
@@ -64693,7 +64735,18 @@ function createCloudCodexSession(ctx, sink, deps) {
|
|
|
64693
64735
|
sink,
|
|
64694
64736
|
stream,
|
|
64695
64737
|
logger: deps.logger,
|
|
64696
|
-
threadParams:
|
|
64738
|
+
threadParams: async (client) => {
|
|
64739
|
+
const p3 = await paths;
|
|
64740
|
+
const params = cloudThreadParams(ctx, p3);
|
|
64741
|
+
if (!ctx.toolSessionId || p3.model) return params;
|
|
64742
|
+
const config = await client.request("config/read", {});
|
|
64743
|
+
if (config?.config?.model) return { ...params, model: config.config.model };
|
|
64744
|
+
const list = await client.request("model/list", {});
|
|
64745
|
+
const selected = list?.data?.find((m3) => m3.isDefault);
|
|
64746
|
+
const current = selected?.id || selected?.model;
|
|
64747
|
+
if (!current) throw new Error("\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D Codex \u9ED8\u8BA4\u6A21\u578B\uFF0C\u62D2\u7EDD\u6CBF\u7528\u65E7\u4F1A\u8BDD\u6A21\u578B");
|
|
64748
|
+
return { ...params, model: current };
|
|
64749
|
+
},
|
|
64697
64750
|
imageInput: cloudImageInput(deps),
|
|
64698
64751
|
// skills 在 Pod 里由 persona 目录自带,本机路径对它没意义
|
|
64699
64752
|
// 「让对端走人」= 断开下行流,gateway 收到就删 Job;信号对 Pod 没有意义
|
|
@@ -64701,7 +64754,9 @@ function createCloudCodexSession(ctx, sink, deps) {
|
|
|
64701
64754
|
// 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
|
|
64702
64755
|
onTurnStarting: () => channel?.markNextLineTurnStart(),
|
|
64703
64756
|
// 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
|
|
64704
|
-
onTurnCompleted: (outcome, afterFirstByteMs) =>
|
|
64757
|
+
onTurnCompleted: (outcome, afterFirstByteMs) => {
|
|
64758
|
+
if (!ctx.titleOnly) void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} });
|
|
64759
|
+
},
|
|
64705
64760
|
now: deps.now
|
|
64706
64761
|
});
|
|
64707
64762
|
if (!personaId2) {
|
|
@@ -64711,12 +64766,12 @@ function createCloudCodexSession(ctx, sink, deps) {
|
|
|
64711
64766
|
});
|
|
64712
64767
|
return driver.session;
|
|
64713
64768
|
}
|
|
64714
|
-
void deps.client.openSession(personaId2).then(
|
|
64769
|
+
void deps.client.openSession(personaId2, { expectedEngine: "codex", ...ctx.titleOnly ? { purpose: "title" } : {} }).then(
|
|
64715
64770
|
(opened) => {
|
|
64716
64771
|
channel = opened;
|
|
64717
64772
|
channel.onControl((c2) => {
|
|
64718
64773
|
if (c2.kind === "ready") {
|
|
64719
|
-
resolvePaths({ personaDir: c2.personaDir, workDir: c2.workDir });
|
|
64774
|
+
resolvePaths({ personaDir: c2.personaDir, workDir: c2.workDir, model: c2.model });
|
|
64720
64775
|
} else if (c2.kind === "queued") {
|
|
64721
64776
|
sink.pushEvents([{ kind: "meta-text", text: `\u4E91\u4E0A\u673A\u5668\u6709 ${c2.running} \u4E2A\u4EFB\u52A1\u5728\u8DD1\uFF0C\u7B49\u4E00\u4E2A\u7ED3\u675F\u5C31\u8F6E\u5230\u4F60` }]);
|
|
64722
64777
|
} else if (c2.kind === "error") {
|
|
@@ -65431,7 +65486,9 @@ function createCloudPiSession(ctx, sink, deps) {
|
|
|
65431
65486
|
// 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
|
|
65432
65487
|
onTurnStarting: () => channel?.markNextLineTurnStart(),
|
|
65433
65488
|
// 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
|
|
65434
|
-
onTurnCompleted: (outcome, afterFirstByteMs) =>
|
|
65489
|
+
onTurnCompleted: (outcome, afterFirstByteMs) => {
|
|
65490
|
+
if (!ctx.titleOnly) void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} });
|
|
65491
|
+
},
|
|
65435
65492
|
now: deps.now
|
|
65436
65493
|
});
|
|
65437
65494
|
if (!personaId2) {
|
|
@@ -65441,7 +65498,7 @@ function createCloudPiSession(ctx, sink, deps) {
|
|
|
65441
65498
|
});
|
|
65442
65499
|
return driver.session;
|
|
65443
65500
|
}
|
|
65444
|
-
void deps.client.openSession(personaId2, { toolSessionId: sessionId, instructions: cloudPiInstructions(ctx) }).then(
|
|
65501
|
+
void deps.client.openSession(personaId2, { toolSessionId: sessionId, instructions: ctx.titleOnly ? "" : cloudPiInstructions(ctx), expectedEngine: "pi", ...ctx.titleOnly ? { purpose: "title" } : {} }).then(
|
|
65445
65502
|
(opened) => {
|
|
65446
65503
|
channel = opened;
|
|
65447
65504
|
channel.onControl((c2) => {
|
|
@@ -66313,10 +66370,6 @@ var import_node_fs45 = __toESM(require("fs"), 1);
|
|
|
66313
66370
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
66314
66371
|
var import_node_path49 = __toESM(require("path"), 1);
|
|
66315
66372
|
var TITLE_GEN_DIR_PREFIX = "clawd-title-";
|
|
66316
|
-
var TITLE_MODEL_BY_TOOL = {
|
|
66317
|
-
claude: "haiku",
|
|
66318
|
-
codex: "gpt-5.4-mini"
|
|
66319
|
-
};
|
|
66320
66373
|
var TITLE_INPUT_MAX_CHARS = 400;
|
|
66321
66374
|
var DEFAULT_TITLE_GEN_TIMEOUT_MS = 45e3;
|
|
66322
66375
|
var outFileSeq = 0;
|
|
@@ -66337,8 +66390,8 @@ function sanitizeTitle(raw) {
|
|
|
66337
66390
|
if (!stripped) return null;
|
|
66338
66391
|
return stripped.slice(0, 60);
|
|
66339
66392
|
}
|
|
66340
|
-
function buildClaudeTitleArgs(prompt) {
|
|
66341
|
-
return ["-p", "--model",
|
|
66393
|
+
function buildClaudeTitleArgs(prompt, model) {
|
|
66394
|
+
return ["-p", "--no-session-persistence", "--tools", "", ...model ? ["--model", model] : [], prompt];
|
|
66342
66395
|
}
|
|
66343
66396
|
var CODEX_TITLE_SCHEMA = JSON.stringify({
|
|
66344
66397
|
type: "object",
|
|
@@ -66358,7 +66411,7 @@ function parseTitleJson(raw) {
|
|
|
66358
66411
|
}
|
|
66359
66412
|
return sanitizeTitle(text);
|
|
66360
66413
|
}
|
|
66361
|
-
function buildCodexTitleArgs(prompt, outFile, schemaFile) {
|
|
66414
|
+
function buildCodexTitleArgs(prompt, outFile, schemaFile, model) {
|
|
66362
66415
|
return [
|
|
66363
66416
|
"exec",
|
|
66364
66417
|
"--ephemeral",
|
|
@@ -66367,8 +66420,7 @@ function buildCodexTitleArgs(prompt, outFile, schemaFile) {
|
|
|
66367
66420
|
schemaFile,
|
|
66368
66421
|
"-s",
|
|
66369
66422
|
"read-only",
|
|
66370
|
-
"-m",
|
|
66371
|
-
TITLE_MODEL_BY_TOOL.codex,
|
|
66423
|
+
...model ? ["-m", model] : [],
|
|
66372
66424
|
"--color",
|
|
66373
66425
|
"never",
|
|
66374
66426
|
"-o",
|
|
@@ -66376,10 +66428,35 @@ function buildCodexTitleArgs(prompt, outFile, schemaFile) {
|
|
|
66376
66428
|
prompt
|
|
66377
66429
|
];
|
|
66378
66430
|
}
|
|
66379
|
-
|
|
66380
|
-
|
|
66431
|
+
function buildPiTitleArgs(prompt, model) {
|
|
66432
|
+
return [
|
|
66433
|
+
"-p",
|
|
66434
|
+
"--mode",
|
|
66435
|
+
"text",
|
|
66436
|
+
"--no-session",
|
|
66437
|
+
"--no-tools",
|
|
66438
|
+
"--no-skills",
|
|
66439
|
+
"--no-context-files",
|
|
66440
|
+
...model ? ["--model", model] : [],
|
|
66441
|
+
"--",
|
|
66442
|
+
prompt
|
|
66443
|
+
];
|
|
66444
|
+
}
|
|
66445
|
+
async function generateSessionTitle(file, firstMessage, deps = {}) {
|
|
66446
|
+
const tool = file.tool ?? "claude";
|
|
66447
|
+
if (tool !== "claude" && tool !== "pi" && tool !== "pi-cloud" && !isCodexFamily(tool)) return null;
|
|
66381
66448
|
const text = firstMessage.trim();
|
|
66382
|
-
if (!text) return null;
|
|
66449
|
+
if (!text || deps.signal?.aborted) return null;
|
|
66450
|
+
if (tool === "codex-cloud" || tool === "pi-cloud") {
|
|
66451
|
+
if (!deps.generateCloud) return null;
|
|
66452
|
+
try {
|
|
66453
|
+
const raw = await deps.generateCloud(file, buildClaudeTitlePrompt(text), deps.signal);
|
|
66454
|
+
return raw && !deps.signal?.aborted ? parseTitleJson(raw) : null;
|
|
66455
|
+
} catch (err) {
|
|
66456
|
+
deps.logger?.warn("title-gen cloud failed", { tool, message: err.message });
|
|
66457
|
+
return null;
|
|
66458
|
+
}
|
|
66459
|
+
}
|
|
66383
66460
|
let workDir;
|
|
66384
66461
|
try {
|
|
66385
66462
|
workDir = import_node_fs45.default.mkdtempSync(import_node_path49.default.join(import_node_os18.default.tmpdir(), TITLE_GEN_DIR_PREFIX));
|
|
@@ -66394,7 +66471,7 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
|
|
|
66394
66471
|
};
|
|
66395
66472
|
const outFile = import_node_path49.default.join(workDir, `codex-title-${process.pid}-${++outFileSeq}-${Date.now()}.txt`);
|
|
66396
66473
|
const schemaFile = import_node_path49.default.join(workDir, "title-schema.json");
|
|
66397
|
-
if (
|
|
66474
|
+
if (tool === "codex") {
|
|
66398
66475
|
try {
|
|
66399
66476
|
import_node_fs45.default.writeFileSync(schemaFile, CODEX_TITLE_SCHEMA);
|
|
66400
66477
|
} catch {
|
|
@@ -66402,14 +66479,16 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
|
|
|
66402
66479
|
return null;
|
|
66403
66480
|
}
|
|
66404
66481
|
}
|
|
66405
|
-
const cmd = tool === "claude" ? process.env.CLAUDE_BIN ?? "claude" : process.env.CODEX_BIN ?? "codex";
|
|
66406
|
-
const
|
|
66482
|
+
const cmd = tool === "claude" ? process.env.CLAUDE_BIN ?? "claude" : tool === "pi" ? process.env.PI_BIN ?? "pi" : process.env.CODEX_BIN ?? "codex";
|
|
66483
|
+
const chosenModel = file.resolvedModel || file.model;
|
|
66484
|
+
const model = tool === "codex" ? sanitizeCodexModel(chosenModel) : chosenModel;
|
|
66485
|
+
const args = tool === "claude" ? buildClaudeTitleArgs(buildClaudeTitlePrompt(text), model) : tool === "pi" ? buildPiTitleArgs(buildClaudeTitlePrompt(text), model) : buildCodexTitleArgs(buildTitlePrompt(text), outFile, schemaFile, model);
|
|
66407
66486
|
const doSpawn = deps.spawnOverride ?? ((c2, a2, o2) => (0, import_node_child_process13.spawn)(c2, a2, { ...o2, stdio: ["ignore", "pipe", "pipe"] }));
|
|
66408
66487
|
const timeoutMs = deps.timeoutMs ?? DEFAULT_TITLE_GEN_TIMEOUT_MS;
|
|
66409
66488
|
return await new Promise((resolve9) => {
|
|
66410
66489
|
let proc;
|
|
66411
66490
|
try {
|
|
66412
|
-
proc = doSpawn(cmd, args, { cwd: workDir });
|
|
66491
|
+
proc = doSpawn(cmd, args, { cwd: model ? workDir : file.cwd });
|
|
66413
66492
|
} catch (err) {
|
|
66414
66493
|
deps.logger?.warn("title-gen spawn failed", { tool, message: err.message });
|
|
66415
66494
|
cleanupWorkDir();
|
|
@@ -66421,10 +66500,13 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
|
|
|
66421
66500
|
if (settled) return;
|
|
66422
66501
|
settled = true;
|
|
66423
66502
|
clearTimeout(timer);
|
|
66503
|
+
deps.signal?.removeEventListener("abort", onAbort);
|
|
66424
66504
|
if (proc.exitCode === null && proc.signalCode === null) proc.kill("SIGKILL");
|
|
66425
66505
|
cleanupWorkDir();
|
|
66426
66506
|
resolve9(title);
|
|
66427
66507
|
};
|
|
66508
|
+
const onAbort = () => finish(null);
|
|
66509
|
+
deps.signal?.addEventListener("abort", onAbort, { once: true });
|
|
66428
66510
|
const timer = setTimeout(() => {
|
|
66429
66511
|
deps.logger?.warn("title-gen timed out", { tool, timeoutMs });
|
|
66430
66512
|
finish(null);
|
|
@@ -66443,7 +66525,7 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
|
|
|
66443
66525
|
deps.logger?.warn("title-gen exited non-zero", { tool, code });
|
|
66444
66526
|
return finish(null);
|
|
66445
66527
|
}
|
|
66446
|
-
if (tool === "claude") return finish(parseTitleJson(stdout));
|
|
66528
|
+
if (tool === "claude" || tool === "pi") return finish(parseTitleJson(stdout));
|
|
66447
66529
|
let fileText = "";
|
|
66448
66530
|
try {
|
|
66449
66531
|
fileText = import_node_fs45.default.readFileSync(outFile, "utf8");
|
|
@@ -66455,6 +66537,57 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
|
|
|
66455
66537
|
});
|
|
66456
66538
|
}
|
|
66457
66539
|
|
|
66540
|
+
// src/tools/cloud-title.ts
|
|
66541
|
+
function generateCloudTitle(file, prompt, signal, getAdapter2, timeoutMs = 45e3) {
|
|
66542
|
+
if (signal?.aborted) return Promise.resolve(null);
|
|
66543
|
+
return new Promise((resolve9) => {
|
|
66544
|
+
let agent;
|
|
66545
|
+
let settled = false;
|
|
66546
|
+
let finalText = "";
|
|
66547
|
+
let partialText = "";
|
|
66548
|
+
let failed = false;
|
|
66549
|
+
const finish = (raw) => {
|
|
66550
|
+
if (settled) return;
|
|
66551
|
+
settled = true;
|
|
66552
|
+
clearTimeout(timer);
|
|
66553
|
+
signal?.removeEventListener("abort", onAbort);
|
|
66554
|
+
try {
|
|
66555
|
+
void Promise.resolve(agent?.stop("SIGKILL")).catch(() => {
|
|
66556
|
+
});
|
|
66557
|
+
} catch {
|
|
66558
|
+
}
|
|
66559
|
+
resolve9(raw);
|
|
66560
|
+
};
|
|
66561
|
+
const onAbort = () => finish(null);
|
|
66562
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
66563
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
66564
|
+
try {
|
|
66565
|
+
const adapter = getAdapter2(file.tool);
|
|
66566
|
+
if (!adapter.createSession) return finish(null);
|
|
66567
|
+
agent = adapter.createSession({ cwd: file.cwd, env: {}, titleOnly: true }, {
|
|
66568
|
+
pushEvents(events) {
|
|
66569
|
+
for (const event of events) {
|
|
66570
|
+
if (event.kind === "text") {
|
|
66571
|
+
if (event.partialId) partialText += event.text;
|
|
66572
|
+
else finalText = event.text;
|
|
66573
|
+
} else if (event.kind === "error" || event.kind === "tool_call" || event.kind === "permission_request") {
|
|
66574
|
+
failed = true;
|
|
66575
|
+
finish(null);
|
|
66576
|
+
} else if (event.kind === "turn_end") {
|
|
66577
|
+
finish(failed ? null : finalText || partialText || null);
|
|
66578
|
+
}
|
|
66579
|
+
}
|
|
66580
|
+
},
|
|
66581
|
+
onExit: () => finish(null),
|
|
66582
|
+
onError: () => finish(null)
|
|
66583
|
+
});
|
|
66584
|
+
agent.startTurn(prompt);
|
|
66585
|
+
} catch {
|
|
66586
|
+
finish(null);
|
|
66587
|
+
}
|
|
66588
|
+
});
|
|
66589
|
+
}
|
|
66590
|
+
|
|
66458
66591
|
// ../node_modules/.pnpm/ws@8.20.0/node_modules/ws/wrapper.mjs
|
|
66459
66592
|
var import_stream = __toESM(require_stream(), 1);
|
|
66460
66593
|
var import_extension2 = __toESM(require_extension(), 1);
|
|
@@ -72959,7 +73092,7 @@ function computeMethodAccess(args) {
|
|
|
72959
73092
|
}
|
|
72960
73093
|
|
|
72961
73094
|
// src/version.ts
|
|
72962
|
-
var version = "0.2.
|
|
73095
|
+
var version = "0.2.601".length > 0 ? "0.2.601" : "dev";
|
|
72963
73096
|
|
|
72964
73097
|
// src/cli-probe/probe.ts
|
|
72965
73098
|
var fs83 = __toESM(require("fs"), 1);
|
|
@@ -75912,9 +76045,13 @@ async function startDaemon(config) {
|
|
|
75912
76045
|
// per-persona 凭据注入(M1b Step 5):runner 在解释 spawn effect 时按 ctx.personaId 调,
|
|
75913
76046
|
// 产出拼进 ctx.mcpConfigs。**不在 reducer 里**——渲染要读写 fs,那边是纯函数。
|
|
75914
76047
|
resolvePersonaMcpConfig,
|
|
75915
|
-
// 会话标题自产(首条 send 并行 fire
|
|
76048
|
+
// 会话标题自产(首条 send 并行 fire,沿用会话引擎和模型)。
|
|
75916
76049
|
// 不注入 = 功能关(manager 测试默认关闭防烧真调用),这里是唯一的生产装配点。
|
|
75917
|
-
generateTitle: (
|
|
76050
|
+
generateTitle: (file, firstMessage, signal) => generateSessionTitle(file, firstMessage, {
|
|
76051
|
+
logger,
|
|
76052
|
+
signal,
|
|
76053
|
+
generateCloud: (titleFile, prompt, titleSignal) => generateCloudTitle(titleFile, prompt, titleSignal, getAdapter)
|
|
76054
|
+
}),
|
|
75918
76055
|
broadcastFrame: (frame, target) => {
|
|
75919
76056
|
if (target === "all") {
|
|
75920
76057
|
transport?.broadcastAll(frame);
|