@clawos-dev/clawd 0.2.613 → 0.2.614

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -8168,6 +8168,10 @@ var init_schemas = __esm({
8168
8168
  sessionId: external_exports.string().min(1),
8169
8169
  /** New gateway execution identity after session:new; defaults to sessionId for the first execution. */
8170
8170
  cloudExecutionId: external_exports.string().min(1).optional(),
8171
+ /** First turn ID of the per-query SSE conversation. */
8172
+ cloudConversationId: external_exports.string().min(1).optional(),
8173
+ /** Accepted local turns whose terminal gateway status has not been observed. */
8174
+ cloudPendingTurns: external_exports.array(external_exports.object({ id: external_exports.string().min(1), text: external_exports.string(), prepared: external_exports.boolean().optional() })).optional(),
8171
8175
  /** Owner gateway session observed in ready; proves the remote Pod was opened. */
8172
8176
  cloudGatewaySessionId: external_exports.string().min(1).optional(),
8173
8177
  /** Persisted before sending so daemon restart can retry the same gateway command ID. */
@@ -44805,7 +44809,7 @@ Env (advanced):
44805
44809
  var import_node_path85 = __toESM(require("path"), 1);
44806
44810
  var import_node_fs66 = __toESM(require("fs"), 1);
44807
44811
  var import_node_os31 = __toESM(require("os"), 1);
44808
- var import_node_crypto28 = require("crypto");
44812
+ var import_node_crypto29 = require("crypto");
44809
44813
  var import_node_url6 = require("url");
44810
44814
 
44811
44815
  // src/logger.ts
@@ -46120,6 +46124,8 @@ function buildSpawnContext(state, deps) {
46120
46124
  const ctx = {
46121
46125
  cwd: file.cwd,
46122
46126
  cloudExecutionId: file.cloudExecutionId,
46127
+ ...file.cloudConversationId ? { cloudConversationId: file.cloudConversationId } : {},
46128
+ ...file.cloudPendingCommand && !file.cloudPendingTurns ? { cloudLegacyPendingCommand: true } : {},
46123
46129
  cloudGatewaySessionId: file.cloudGatewaySessionId,
46124
46130
  toolSessionId: file.toolSessionId,
46125
46131
  model: file.model,
@@ -46375,10 +46381,16 @@ function pushEventToBufferInPlace(next, event, deps) {
46375
46381
  }
46376
46382
  }
46377
46383
  if (isUserText || isTurnEnd) {
46384
+ const cloudTurnId = isUserText ? withSeq.uuid ?? `${next.file.sessionId}-${seq}` : void 0;
46385
+ const cloudPerTurn = isCloudTool(next.file.tool) && !next.file.cloudGatewaySessionId && (!next.file.cloudPendingCommand || !!next.file.cloudPendingTurns);
46378
46386
  next.file = {
46379
46387
  ...next.file,
46380
46388
  updatedAt: nowIso(deps),
46381
- ...isCloudTool(next.file.tool) && next.status !== "error" && !(withSeq.kind === "turn_end" && withSeq.reason === "interrupted") ? { cloudPendingCommand: withSeq.kind === "user_text" ? { id: withSeq.uuid ?? `${next.file.sessionId}-${seq}`, text: withSeq.text } : void 0 } : {}
46389
+ ...cloudPerTurn && isUserText && next.status !== "error" ? {
46390
+ cloudConversationId: next.file.cloudConversationId ?? next.file.cloudExecutionId ?? cloudTurnId,
46391
+ cloudPendingTurns: [...next.file.cloudPendingTurns ?? [], { id: cloudTurnId, text: withSeq.text }]
46392
+ } : {},
46393
+ ...!cloudPerTurn && isCloudTool(next.file.tool) && next.status !== "error" && !(withSeq.kind === "turn_end" && withSeq.reason === "interrupted") ? { cloudPendingCommand: withSeq.kind === "user_text" ? { id: withSeq.uuid ?? `${next.file.sessionId}-${seq}`, text: withSeq.text } : void 0 } : {}
46382
46394
  };
46383
46395
  effects.push({ kind: "persist-file", file: next.file });
46384
46396
  effects.push(sessionInfoFrame(next.file));
@@ -46443,7 +46455,7 @@ function applyCommand(state, command, deps) {
46443
46455
  const sessionId = next.file.sessionId;
46444
46456
  switch (command.kind) {
46445
46457
  case "send": {
46446
- if (command.queueWhileRunning && (next.status === "running" || next.status === "spawning")) {
46458
+ if (command.queueWhileRunning && !(isCloudTool(next.file.tool) && !next.file.cloudGatewaySessionId) && (next.status === "running" || next.status === "spawning")) {
46447
46459
  next.queuedSends = [
46448
46460
  ...next.queuedSends,
46449
46461
  {
@@ -46495,8 +46507,8 @@ function applyCommand(state, command, deps) {
46495
46507
  return { state: next, effects };
46496
46508
  }
46497
46509
  case "stop": {
46498
- if (isCloudTool(next.file.tool) && next.file.cloudPendingCommand) {
46499
- next.file = { ...next.file, cloudPendingCommand: void 0 };
46510
+ if (isCloudTool(next.file.tool) && (next.file.cloudPendingCommand || next.file.cloudPendingTurns?.length)) {
46511
+ next.file = { ...next.file, cloudPendingCommand: void 0, cloudPendingTurns: void 0 };
46500
46512
  effects.push({ kind: "persist-file", file: next.file }, sessionInfoFrame(next.file));
46501
46513
  }
46502
46514
  for (const toolUseId of Object.keys(state.pendingQuestions ?? {})) {
@@ -46634,6 +46646,9 @@ function applyCommand(state, command, deps) {
46634
46646
  toolSessionId: _drop,
46635
46647
  cloudPendingCommand: _cloudPendingCommand,
46636
46648
  cloudGatewaySessionId: _cloudGatewaySessionId,
46649
+ cloudConversationId: _cloudConversationId,
46650
+ cloudPendingTurns: _cloudPendingTurns,
46651
+ cloudExecutionId: _cloudExecutionId,
46637
46652
  contextUsage: _ctx,
46638
46653
  contextWindowSize: _win,
46639
46654
  gitBranch: _branch,
@@ -46646,6 +46661,9 @@ function applyCommand(state, command, deps) {
46646
46661
  void _drop;
46647
46662
  void _cloudPendingCommand;
46648
46663
  void _cloudGatewaySessionId;
46664
+ void _cloudConversationId;
46665
+ void _cloudPendingTurns;
46666
+ void _cloudExecutionId;
46649
46667
  void _ctx;
46650
46668
  void _win;
46651
46669
  void _branch;
@@ -46731,9 +46749,16 @@ function reduceSessionInner(state, input, deps) {
46731
46749
  }
46732
46750
  case "cloud-command-prepared": {
46733
46751
  const pending = state.file.cloudPendingCommand;
46734
- if (!pending || pending.id !== input.commandId) return { state, effects: [] };
46752
+ const turn = state.file.cloudPendingTurns?.find((item) => item.id === input.commandId);
46753
+ if ((!pending || pending.id !== input.commandId) && !turn) return { state, effects: [] };
46754
+ const next = cloneState(state);
46755
+ next.file = turn ? { ...next.file, cloudPendingTurns: next.file.cloudPendingTurns?.map((item) => item.id === input.commandId ? { ...item, text: input.text, prepared: true } : item) } : { ...next.file, cloudPendingCommand: { id: pending.id, text: input.text, prepared: true } };
46756
+ return { state: next, effects: [{ kind: "persist-file", file: next.file }] };
46757
+ }
46758
+ case "cloud-turn-terminal": {
46759
+ if (!state.file.cloudPendingTurns?.some((item) => item.id === input.turnId)) return { state, effects: [] };
46735
46760
  const next = cloneState(state);
46736
- next.file = { ...next.file, cloudPendingCommand: { id: pending.id, text: input.text, prepared: true } };
46761
+ next.file = { ...next.file, cloudPendingTurns: next.file.cloudPendingTurns?.filter((item) => item.id !== input.turnId) };
46737
46762
  return { state: next, effects: [{ kind: "persist-file", file: next.file }] };
46738
46763
  }
46739
46764
  case "proc-exit": {
@@ -47243,6 +47268,7 @@ var SessionRunner = class {
47243
47268
  this.input({ kind: "command", command: { kind: "spawn-if-needed" } });
47244
47269
  const pending = this.state.file.cloudPendingCommand;
47245
47270
  if (pending) this.session?.startTurn(pending.text, pending.id, pending.prepared === true);
47271
+ for (const turn of this.state.file.cloudPendingTurns ?? []) this.session?.startTurn(turn.text, turn.id, turn.prepared === true);
47246
47272
  } finally {
47247
47273
  this.recoveringCloud = false;
47248
47274
  }
@@ -47440,7 +47466,7 @@ var SessionRunner = class {
47440
47466
  break;
47441
47467
  case "write-stdin":
47442
47468
  if (this.session) {
47443
- this.session.startTurn(effect.payload, this.state.file.cloudPendingCommand?.id);
47469
+ this.session.startTurn(effect.payload, this.state.file.cloudPendingTurns?.at(-1)?.id ?? this.state.file.cloudPendingCommand?.id);
47444
47470
  break;
47445
47471
  }
47446
47472
  this.hooks.logger?.debug("[RG] write-stdin", {
@@ -47585,14 +47611,15 @@ var SessionRunner = class {
47585
47611
  this.input({ kind: "cloud-gateway-ready", gatewaySessionId });
47586
47612
  },
47587
47613
  onCommandPrepared: (commandId, text, prepared) => {
47588
- const pending = this.state.file.cloudPendingCommand;
47614
+ const pending = this.state.file.cloudPendingTurns?.find((turn) => turn.id === commandId) ?? this.state.file.cloudPendingCommand;
47589
47615
  if (!pending || pending.id !== commandId || prepared && pending.text !== text) return false;
47590
47616
  if (!prepared) {
47591
- this.hooks.store.write({ ...this.state.file, cloudPendingCommand: { id: commandId, text, prepared: true } });
47617
+ this.hooks.store.write(this.state.file.cloudPendingTurns?.some((turn) => turn.id === commandId) ? { ...this.state.file, cloudPendingTurns: this.state.file.cloudPendingTurns.map((turn) => turn.id === commandId ? { ...turn, text, prepared: true } : turn) } : { ...this.state.file, cloudPendingCommand: { id: commandId, text, prepared: true } });
47592
47618
  this.input({ kind: "cloud-command-prepared", commandId, text });
47593
47619
  }
47594
47620
  return true;
47595
- }
47621
+ },
47622
+ onCloudTurnTerminal: (turnId) => this.input({ kind: "cloud-turn-terminal", turnId })
47596
47623
  });
47597
47624
  return;
47598
47625
  }
@@ -48662,11 +48689,10 @@ var SessionManager = class {
48662
48689
  if (!file) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, args.sessionId);
48663
48690
  const runner = this.runners.get(args.sessionId) ?? null;
48664
48691
  if (isCloudTool(file.tool)) {
48665
- if (!file.cloudGatewaySessionId) runner?.confirmCloudCancelled();
48666
- else if (file.cloudPendingCommand || runner?.getState().procAlive) {
48692
+ if (file.cloudConversationId || file.cloudGatewaySessionId) {
48667
48693
  if (!this.cloudCancel) throw new Error("cloud canceller is not configured");
48668
48694
  await this.cloudCancel(file, runner);
48669
- }
48695
+ } else runner?.confirmCloudCancelled();
48670
48696
  }
48671
48697
  return this.delete(args);
48672
48698
  }
@@ -48836,14 +48862,13 @@ var SessionManager = class {
48836
48862
  const file = this.findOwnedSession(args.sessionId);
48837
48863
  const runner = this.runners.get(args.sessionId);
48838
48864
  if (file && isCloudTool(file.tool)) {
48839
- if (!file.cloudGatewaySessionId) runner?.confirmCloudCancelled();
48840
- else if (file.cloudPendingCommand || runner?.getState().procAlive) {
48865
+ if (file.cloudConversationId || file.cloudGatewaySessionId) {
48841
48866
  if (!this.cloudCancel) throw new Error("cloud canceller is not configured");
48842
48867
  await this.cloudCancel(file, runner ?? null);
48843
- }
48868
+ } else runner?.confirmCloudCancelled();
48844
48869
  }
48845
48870
  if (!runner) {
48846
- if (file && isCloudTool(file.tool) && file.cloudPendingCommand) this.writeOwned({ ...file, cloudPendingCommand: void 0 });
48871
+ if (file && isCloudTool(file.tool) && (file.cloudPendingCommand || file.cloudPendingTurns?.length)) this.writeOwned({ ...file, cloudPendingCommand: void 0, cloudPendingTurns: void 0 });
48847
48872
  return { response: { ok: true }, broadcast: [] };
48848
48873
  }
48849
48874
  this.clearPendingTurnEnd(args.sessionId);
@@ -49009,6 +49034,9 @@ var SessionManager = class {
49009
49034
  toolSessionId: _drop,
49010
49035
  cloudPendingCommand: _cloudPendingCommand,
49011
49036
  cloudGatewaySessionId: _cloudGatewaySessionId,
49037
+ cloudConversationId: _cloudConversationId,
49038
+ cloudPendingTurns: _cloudPendingTurns,
49039
+ cloudExecutionId: _cloudExecutionId,
49012
49040
  contextUsage: _ctx,
49013
49041
  contextWindowSize: _win,
49014
49042
  gitBranch: _branch,
@@ -49020,6 +49048,9 @@ var SessionManager = class {
49020
49048
  void _drop;
49021
49049
  void _cloudPendingCommand;
49022
49050
  void _cloudGatewaySessionId;
49051
+ void _cloudConversationId;
49052
+ void _cloudPendingTurns;
49053
+ void _cloudExecutionId;
49023
49054
  void _ctx;
49024
49055
  void _win;
49025
49056
  void _branch;
@@ -49038,12 +49069,11 @@ var SessionManager = class {
49038
49069
  const file = this.getFile(args.sessionId);
49039
49070
  if (isCloudTool(file.tool)) {
49040
49071
  const runner = this.runners.get(args.sessionId) ?? null;
49041
- if (!file.cloudGatewaySessionId) runner?.confirmCloudCancelled();
49042
- else if (file.cloudPendingCommand || runner?.getState().procAlive) {
49072
+ if (file.cloudConversationId || file.cloudGatewaySessionId) {
49043
49073
  if (!this.cloudCancel) throw new Error("cloud canceller is not configured");
49044
49074
  await this.cloudCancel(file, runner);
49045
- }
49046
- return this.newSession(args, (0, import_node_crypto3.randomUUID)());
49075
+ } else runner?.confirmCloudCancelled();
49076
+ return this.newSession(args, file.cloudGatewaySessionId ? (0, import_node_crypto3.randomUUID)() : void 0);
49047
49077
  }
49048
49078
  return this.newSession(args);
49049
49079
  }
@@ -49114,7 +49144,7 @@ var SessionManager = class {
49114
49144
  /** Reattach only to executions whose last turn lacks a durable end marker. */
49115
49145
  recoverCloudSessions() {
49116
49146
  for (const file of this.listAllOwned()) {
49117
- if (!isCloudTool(file.tool) || !file.cloudPendingCommand) continue;
49147
+ if (!isCloudTool(file.tool) || !file.cloudPendingCommand && !file.cloudPendingTurns?.length) continue;
49118
49148
  const runner = this.ensureSession(file);
49119
49149
  runner.recoverCloud();
49120
49150
  }
@@ -64751,6 +64781,9 @@ var GATEWAY_ROUTES = {
64751
64781
  eventSession: (personaId2) => `/p/${encodeURIComponent(personaId2)}/session/events`,
64752
64782
  eventCancel: (personaId2) => `/p/${encodeURIComponent(personaId2)}/session/events:cancel`,
64753
64783
  eventSend: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/events/${encodeURIComponent(sessionId)}`,
64784
+ turn: (personaId2) => `/p/${encodeURIComponent(personaId2)}/v1/chat/completions`,
64785
+ turnCommand: (personaId2, turnId) => `/p/${encodeURIComponent(personaId2)}/turns/${encodeURIComponent(turnId)}/commands`,
64786
+ conversationCancel: (personaId2, conversationId) => `/p/${encodeURIComponent(personaId2)}/conversations/${encodeURIComponent(conversationId)}:cancel`,
64754
64787
  // 调用方自己那块盘的用量(design 2026-09-19-cloud-volume-usage §3.3):同样按人、不挂 persona
64755
64788
  volume: () => "/me/volume",
64756
64789
  // 云 persona 运行看板的数据(design 2026-09-23-cloud-persona-metrics §3.6):只给发布者与管理员(gateway 问中心,design 2026-09-24-cloud-persona-conversations §3.2)
@@ -64892,6 +64925,32 @@ var CloudGatewayClient = class {
64892
64925
  logger: this.deps.logger
64893
64926
  });
64894
64927
  }
64928
+ async openTurn(personaId2, opts) {
64929
+ const { apiKey, gatewayUrl } = await this.credentials();
64930
+ const health = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.healthz());
64931
+ if (!health.ok || (await health.json()).turnProtocolVersion !== 1) {
64932
+ throw new Error("\u4E91 gateway \u4E0D\u652F\u6301\u6309\u8F6E SSE\uFF0C\u8BF7\u5148\u5347\u7EA7 gateway");
64933
+ }
64934
+ return openHttpTurn({ ...opts, fetchImpl: this.fetchImpl, gatewayUrl, apiKey, personaId: personaId2, logger: this.deps.logger });
64935
+ }
64936
+ async commandTurn(personaId2, conversationId, turnId, command) {
64937
+ const { apiKey, gatewayUrl } = await this.credentials();
64938
+ const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.turnCommand(personaId2, turnId), {
64939
+ method: "POST",
64940
+ headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json", "x-clawd-turn-protocol": "1" },
64941
+ body: JSON.stringify({ conversationId, ...command })
64942
+ });
64943
+ if (!res.ok) throw new Error(`gateway turn command returned ${res.status}`);
64944
+ }
64945
+ async cancelConversation(personaId2, conversationId) {
64946
+ const { apiKey, gatewayUrl } = await this.credentials();
64947
+ const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.conversationCancel(personaId2, conversationId), {
64948
+ method: "POST",
64949
+ headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json", "x-clawd-turn-protocol": "1" },
64950
+ body: "{}"
64951
+ });
64952
+ if (!res.ok) throw new Error(`gateway conversation cancel returned ${res.status}`);
64953
+ }
64895
64954
  async cancelEventSession(personaId2, clientSessionId, gatewaySessionId) {
64896
64955
  const { apiKey, gatewayUrl } = await this.credentials();
64897
64956
  const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.eventCancel(personaId2), {
@@ -64964,6 +65023,152 @@ var CloudGatewayClient = class {
64964
65023
  return CloudVolumeUsageSchema.parse(body.volume);
64965
65024
  }
64966
65025
  };
65026
+ function openHttpTurn(opts) {
65027
+ const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, conversationId, turnId, text, expectedEngine, engineSessionId, instructions, effort, personaMode, logger } = opts;
65028
+ const body = JSON.stringify({
65029
+ messages: [{ role: "user", content: text }],
65030
+ stream: true,
65031
+ clawd: {
65032
+ eventMode: "parsed",
65033
+ conversationId,
65034
+ turnId,
65035
+ expectedEngine,
65036
+ ...engineSessionId ? { engineSessionId } : {},
65037
+ ...instructions ? { instructions } : {},
65038
+ ...effort ? { effort } : {},
65039
+ ...personaMode ? { personaMode } : {}
65040
+ }
65041
+ });
65042
+ const eventsCbs = [];
65043
+ const stateCbs = [];
65044
+ const gapCbs = [];
65045
+ const closeCbs = [];
65046
+ const earlyEvents = [];
65047
+ const earlyStates = [];
65048
+ let earlyGap = false;
65049
+ let closed = false;
65050
+ let lastSeq = 0;
65051
+ let abort = new AbortController();
65052
+ const finish = () => {
65053
+ if (closed) return;
65054
+ closed = true;
65055
+ abort.abort();
65056
+ for (const cb of closeCbs) cb();
65057
+ };
65058
+ const state = (value) => {
65059
+ if (!stateCbs.length) earlyStates.push(value);
65060
+ for (const cb of stateCbs) cb(value);
65061
+ if (value.status === "completed" || value.status === "failed" || value.status === "cancelled") finish();
65062
+ };
65063
+ const gap = () => {
65064
+ if (!gapCbs.length) earlyGap = true;
65065
+ for (const cb of gapCbs) cb();
65066
+ };
65067
+ const retryWait = () => new Promise((resolve9) => {
65068
+ const timer = setTimeout(resolve9, 250);
65069
+ timer.unref?.();
65070
+ abort.signal.addEventListener("abort", () => {
65071
+ clearTimeout(timer);
65072
+ resolve9();
65073
+ }, { once: true });
65074
+ });
65075
+ void (async () => {
65076
+ while (!closed) {
65077
+ abort = new AbortController();
65078
+ try {
65079
+ const res = await fetchImpl(gatewayUrl + GATEWAY_ROUTES.turn(personaId2), {
65080
+ method: "POST",
65081
+ headers: {
65082
+ authorization: `Bearer ${apiKey}`,
65083
+ "content-type": "application/json",
65084
+ "x-clawd-turn-protocol": "1",
65085
+ ...lastSeq ? { "last-event-id": String(lastSeq) } : {}
65086
+ },
65087
+ body,
65088
+ signal: abort.signal
65089
+ });
65090
+ if (!res.ok || !res.body) {
65091
+ const raw = await res.text().catch(() => "");
65092
+ let code = `HTTP_${res.status}`;
65093
+ try {
65094
+ const parsed = JSON.parse(raw);
65095
+ code = parsed.error?.code ?? code;
65096
+ } catch {
65097
+ }
65098
+ if ([400, 401, 403, 404, 409, 410, 426].includes(res.status)) {
65099
+ state({ conversationId, turnId, status: "failed", errorCode: code });
65100
+ return;
65101
+ }
65102
+ throw new Error(`gateway returned ${res.status}`);
65103
+ }
65104
+ const reader = res.body.getReader();
65105
+ const decoder = new TextDecoder();
65106
+ let pending = "";
65107
+ for (; ; ) {
65108
+ const { value, done } = await reader.read();
65109
+ if (done || closed) break;
65110
+ pending += decoder.decode(value, { stream: true });
65111
+ let delimiter2;
65112
+ while ((delimiter2 = /\r?\n\r?\n/.exec(pending)) !== null) {
65113
+ const frame = pending.slice(0, delimiter2.index);
65114
+ pending = pending.slice(delimiter2.index + delimiter2[0].length);
65115
+ if (!frame || frame.startsWith(":")) continue;
65116
+ const lines = frame.split(/\r?\n/);
65117
+ const name = lines.find((line) => line.startsWith("event: "))?.slice(7);
65118
+ const dataLine = lines.find((line) => line.startsWith("data: "));
65119
+ if (!name || !dataLine) throw new Error("invalid Clawd SSE frame");
65120
+ const id = lines.find((line) => line.startsWith("id: "))?.slice(4);
65121
+ const seq = id === void 0 ? null : Number(id);
65122
+ if (seq !== null && (!Number.isSafeInteger(seq) || seq < 0)) throw new Error("invalid Clawd SSE cursor");
65123
+ if (seq !== null && seq <= lastSeq) continue;
65124
+ if (seq !== null) lastSeq = seq;
65125
+ const data = JSON.parse(dataLine.slice(6));
65126
+ if (data.turnId !== void 0 && data.turnId !== turnId) throw new Error("Clawd SSE turn ID mismatch");
65127
+ if (name === "events") {
65128
+ const events = ParsedEventSchema.array().parse(data.events);
65129
+ if (!eventsCbs.length) earlyEvents.push(events);
65130
+ for (const cb of eventsCbs) cb(events);
65131
+ } else if (name === "history-gap") gap();
65132
+ else if (["inqueue", "running", "completed", "failed", "cancelled"].includes(name)) {
65133
+ state({ ...data, conversationId, turnId, status: name });
65134
+ } else throw new Error(`unknown Clawd SSE event ${name}`);
65135
+ if (closed) return;
65136
+ }
65137
+ }
65138
+ } catch (error) {
65139
+ if (closed) return;
65140
+ if (error instanceof SyntaxError || error.name === "ZodError" || error.message.startsWith("invalid Clawd SSE")) {
65141
+ state({ conversationId, turnId, status: "failed", errorCode: "INVALID_TURN_STREAM" });
65142
+ return;
65143
+ }
65144
+ logger?.warn("cloud turn SSE disconnected; reconnecting", { message: error.message, turnId });
65145
+ }
65146
+ if (!closed) await retryWait();
65147
+ }
65148
+ })();
65149
+ return {
65150
+ onEvents(cb) {
65151
+ eventsCbs.push(cb);
65152
+ for (const events of earlyEvents.splice(0)) cb(events);
65153
+ },
65154
+ onState(cb) {
65155
+ stateCbs.push(cb);
65156
+ for (const item of earlyStates.splice(0)) cb(item);
65157
+ },
65158
+ onHistoryGap(cb) {
65159
+ gapCbs.push(cb);
65160
+ if (earlyGap) {
65161
+ earlyGap = false;
65162
+ cb();
65163
+ }
65164
+ },
65165
+ onClose(cb) {
65166
+ closeCbs.push(cb);
65167
+ if (closed) cb();
65168
+ },
65169
+ close: finish
65170
+ };
65171
+ }
64967
65172
  function openHttpEventSession(opts) {
64968
65173
  const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, clientSessionId, gatewaySessionId, engineSessionId, instructions, purpose, effort, personaMode, attachOnly, expectedEngine, logger } = opts;
64969
65174
  const headers = { authorization: `Bearer ${apiKey}`, "content-type": "application/json" };
@@ -65482,7 +65687,7 @@ init_protocol();
65482
65687
 
65483
65688
  // src/tools/codex-cloud-session.ts
65484
65689
  var import_node_fs40 = __toESM(require("fs"), 1);
65485
- var import_node_crypto13 = require("crypto");
65690
+ var import_node_crypto14 = require("crypto");
65486
65691
 
65487
65692
  // src/tools/cloud-portable-input.ts
65488
65693
  async function portableCloudInput(text, resolvers) {
@@ -65502,6 +65707,116 @@ async function portableCloudInput(text, resolvers) {
65502
65707
  return result;
65503
65708
  }
65504
65709
 
65710
+ // src/tools/cloud-turn-session.ts
65711
+ var import_node_crypto13 = require("crypto");
65712
+ function createCloudTurnSession(args) {
65713
+ const { ctx, sink, client, personaId: personaId2, engine, instructions, engineSessionId, prepare } = args;
65714
+ let conversationId = ctx.cloudConversationId ?? ctx.cloudExecutionId;
65715
+ let closed = false;
65716
+ let detached = false;
65717
+ let exited = false;
65718
+ let stopping = null;
65719
+ const streams = /* @__PURE__ */ new Map();
65720
+ const live = /* @__PURE__ */ new Set();
65721
+ let submission = Promise.resolve();
65722
+ const exit = () => {
65723
+ if (!exited) {
65724
+ exited = true;
65725
+ sink.onExit(null);
65726
+ }
65727
+ };
65728
+ const sendCommand = (turnId, command) => {
65729
+ if (!conversationId || closed) return;
65730
+ void client.commandTurn(personaId2, conversationId, turnId, command).catch((e) => sink.onError(`\u4E91\u4E0A\u547D\u4EE4\u5931\u8D25\uFF1A${e.message}`));
65731
+ };
65732
+ return {
65733
+ startTurn(text, commandId = (0, import_node_crypto13.randomUUID)(), prepared = false) {
65734
+ if (closed || live.has(commandId)) return;
65735
+ conversationId ??= commandId;
65736
+ live.add(commandId);
65737
+ submission = submission.then(async () => {
65738
+ try {
65739
+ const portable = prepared ? text : await prepare(text);
65740
+ if (closed || sink.onCommandPrepared?.(commandId, portable, prepared) === false) {
65741
+ live.delete(commandId);
65742
+ return;
65743
+ }
65744
+ const stream = await client.openTurn(personaId2, {
65745
+ conversationId,
65746
+ turnId: commandId,
65747
+ text: portable,
65748
+ expectedEngine: engine,
65749
+ ...engine === "pi" ? { engineSessionId: engineSessionId ?? conversationId } : {},
65750
+ ...instructions ? { instructions } : {},
65751
+ ...ctx.effort ? { effort: ctx.effort } : {},
65752
+ ...ctx.personaMode ? { personaMode: ctx.personaMode } : {}
65753
+ });
65754
+ if (closed) {
65755
+ stream.close();
65756
+ return;
65757
+ }
65758
+ streams.set(commandId, stream);
65759
+ let resolveAccepted;
65760
+ const accepted = new Promise((resolve9) => {
65761
+ resolveAccepted = resolve9;
65762
+ });
65763
+ stream.onEvents((events) => sink.pushEvents(events));
65764
+ stream.onHistoryGap(() => sink.onHistoryGap?.(false));
65765
+ stream.onState((state) => {
65766
+ resolveAccepted();
65767
+ if (state.status === "inqueue") sink.pushEvents([{ kind: "meta-text", text: "\u5DF2\u6392\u961F\uFF0C\u4E0A\u4E00\u8F6E\u7ED3\u675F\u540E\u6267\u884C" }]);
65768
+ if (state.status === "completed" || state.status === "failed" || state.status === "cancelled") {
65769
+ live.delete(commandId);
65770
+ streams.delete(commandId);
65771
+ if (state.status === "completed" && state.threadId) sink.pushEvents([{ kind: "session_init", toolSessionId: state.threadId }]);
65772
+ if (state.status === "failed") sink.pushEvents([{ kind: "error", message: `\u4E91\u4E0A\u6267\u884C\u5931\u8D25\uFF1A${state.errorCode ?? "TURN_FAILED"}` }]);
65773
+ if (state.status === "failed" || state.status === "cancelled") sink.pushEvents([{ kind: "turn_end", reason: "interrupted" }]);
65774
+ sink.onCloudTurnTerminal?.(commandId);
65775
+ }
65776
+ });
65777
+ stream.onClose(() => {
65778
+ resolveAccepted();
65779
+ if (streams.get(commandId) === stream) streams.delete(commandId);
65780
+ });
65781
+ await accepted;
65782
+ } catch (e) {
65783
+ live.delete(commandId);
65784
+ sink.onCloudTurnTerminal?.(commandId);
65785
+ sink.onError(`\u8FDE\u4E0D\u4E0A\u4E91 gateway\uFF1A${e.message}`);
65786
+ }
65787
+ });
65788
+ },
65789
+ respondPermission(requestId, allow) {
65790
+ const turnId = [...live][0];
65791
+ if (turnId) sendCommand(turnId, { command: "permission", requestId, allow });
65792
+ },
65793
+ interrupt() {
65794
+ const turnId = [...live][0];
65795
+ if (turnId) sendCommand(turnId, { command: "interrupt" });
65796
+ },
65797
+ detach() {
65798
+ detached = true;
65799
+ closed = true;
65800
+ for (const stream of streams.values()) stream.close();
65801
+ streams.clear();
65802
+ },
65803
+ stop() {
65804
+ if (stopping) return stopping;
65805
+ closed = true;
65806
+ for (const stream of streams.values()) stream.close();
65807
+ streams.clear();
65808
+ stopping = (async () => {
65809
+ try {
65810
+ if (!detached && conversationId) await client.cancelConversation(personaId2, conversationId);
65811
+ } finally {
65812
+ exit();
65813
+ }
65814
+ })();
65815
+ return stopping;
65816
+ }
65817
+ };
65818
+ }
65819
+
65505
65820
  // src/tools/codex-cloud-session.ts
65506
65821
  function cloudImageInput(deps) {
65507
65822
  return (url) => {
@@ -65521,7 +65836,18 @@ function cloudImageInput(deps) {
65521
65836
  }
65522
65837
  function createCloudCodexSession(ctx, sink, deps) {
65523
65838
  const personaId2 = personaIdFromCloudCwd(ctx.cwd);
65524
- const clientSessionId = ctx.titleOnly ? (0, import_node_crypto13.randomUUID)() : ctx.cloudExecutionId ?? ctx.env?.CLAWD_SESSION_ID;
65839
+ if (personaId2 && !ctx.titleOnly && !ctx.cloudGatewaySessionId && !ctx.cloudLegacyPendingCommand && deps.client.openTurn && deps.client.commandTurn && deps.client.cancelConversation) {
65840
+ return createCloudTurnSession({
65841
+ ctx,
65842
+ sink,
65843
+ client: deps.client,
65844
+ personaId: personaId2,
65845
+ engine: "codex",
65846
+ instructions: ctx.extraSystemPrompt,
65847
+ prepare: (text) => portableCloudInput(text, { resolveCodexImage: cloudImageInput(deps) })
65848
+ });
65849
+ }
65850
+ const clientSessionId = ctx.titleOnly ? (0, import_node_crypto14.randomUUID)() : ctx.cloudExecutionId ?? ctx.env?.CLAWD_SESSION_ID;
65525
65851
  let channel = null;
65526
65852
  let closed = false;
65527
65853
  let detached = false;
@@ -65595,7 +65921,7 @@ function createCloudCodexSession(ctx, sink, deps) {
65595
65921
  });
65596
65922
  };
65597
65923
  return {
65598
- startTurn(text, commandId = (0, import_node_crypto13.randomUUID)(), prepared = false) {
65924
+ startTurn(text, commandId = (0, import_node_crypto14.randomUUID)(), prepared = false) {
65599
65925
  send2(async () => {
65600
65926
  const ch = await opened;
65601
65927
  if (closed) return;
@@ -65799,7 +66125,7 @@ var PiRpcClient = class {
65799
66125
 
65800
66126
  // src/tools/pi-session.ts
65801
66127
  var import_node_child_process11 = require("child_process");
65802
- var import_node_crypto14 = require("crypto");
66128
+ var import_node_crypto15 = require("crypto");
65803
66129
  var import_node_fs41 = __toESM(require("fs"), 1);
65804
66130
  var import_node_path45 = __toESM(require("path"), 1);
65805
66131
 
@@ -66072,7 +66398,7 @@ ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
66072
66398
  }
66073
66399
  function createPiSession(ctx, sink, deps = {}) {
66074
66400
  const cmd = process.env.PI_BIN ?? "pi";
66075
- const sessionId = ctx.toolSessionId ?? (0, import_node_crypto14.randomUUID)();
66401
+ const sessionId = ctx.toolSessionId ?? (0, import_node_crypto15.randomUUID)();
66076
66402
  const skillsDir = import_node_path45.default.join(ctx.cwd, ".claude", "skills");
66077
66403
  const exists = deps.skillsDirExists ?? ((d2) => import_node_fs41.default.existsSync(d2));
66078
66404
  const args = buildPiArgs(ctx, {
@@ -66309,14 +66635,26 @@ var PiAdapter = class {
66309
66635
  };
66310
66636
 
66311
66637
  // src/tools/pi-cloud-session.ts
66312
- var import_node_crypto15 = require("crypto");
66638
+ var import_node_crypto16 = require("crypto");
66313
66639
  function cloudPiInstructions(ctx) {
66314
66640
  return [ctx.extraSystemPrompt, workDirPrompt(CLOUD_WORKSPACE_SLOT, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
66315
66641
  }
66316
66642
  function createCloudPiSession(ctx, sink, deps) {
66317
66643
  const personaId2 = personaIdFromCloudCwd(ctx.cwd);
66318
- const clientSessionId = ctx.titleOnly ? (0, import_node_crypto15.randomUUID)() : ctx.cloudExecutionId ?? ctx.env?.CLAWD_SESSION_ID;
66319
- const sessionId = ctx.toolSessionId ?? (0, import_node_crypto15.randomUUID)();
66644
+ if (personaId2 && !ctx.titleOnly && !ctx.cloudGatewaySessionId && !ctx.cloudLegacyPendingCommand && deps.client.openTurn && deps.client.commandTurn && deps.client.cancelConversation) {
66645
+ return createCloudTurnSession({
66646
+ ctx,
66647
+ sink,
66648
+ client: deps.client,
66649
+ personaId: personaId2,
66650
+ engine: "pi",
66651
+ engineSessionId: ctx.toolSessionId,
66652
+ instructions: cloudPiInstructions(ctx),
66653
+ prepare: (text) => portableCloudInput(text, { fetchPiImage: deps.fetchImage ?? makeImageFetcher() })
66654
+ });
66655
+ }
66656
+ const clientSessionId = ctx.titleOnly ? (0, import_node_crypto16.randomUUID)() : ctx.cloudExecutionId ?? ctx.env?.CLAWD_SESSION_ID;
66657
+ const sessionId = ctx.toolSessionId ?? (0, import_node_crypto16.randomUUID)();
66320
66658
  let channel = null;
66321
66659
  let closed = false;
66322
66660
  let detached = false;
@@ -66380,7 +66718,7 @@ function createCloudPiSession(ctx, sink, deps) {
66380
66718
  });
66381
66719
  };
66382
66720
  return {
66383
- startTurn(text, commandId = (0, import_node_crypto15.randomUUID)(), prepared = false) {
66721
+ startTurn(text, commandId = (0, import_node_crypto16.randomUUID)(), prepared = false) {
66384
66722
  send2(async () => {
66385
66723
  const ch = await opened;
66386
66724
  if (closed) return;
@@ -67499,7 +67837,7 @@ var wrapper_default = import_websocket.default;
67499
67837
 
67500
67838
  // src/transport/local-ws-server.ts
67501
67839
  var import_node_http = __toESM(require("http"), 1);
67502
- var import_node_crypto16 = require("crypto");
67840
+ var import_node_crypto17 = require("crypto");
67503
67841
  var LocalWsServer = class {
67504
67842
  constructor(opts) {
67505
67843
  this.opts = opts;
@@ -67716,7 +68054,7 @@ var LocalWsServer = class {
67716
68054
  }
67717
68055
  }
67718
68056
  onConnection(socket, remoteAddress) {
67719
- const id = (0, import_node_crypto16.randomUUID)();
68057
+ const id = (0, import_node_crypto17.randomUUID)();
67720
68058
  const subscribedSessions = /* @__PURE__ */ new Map();
67721
68059
  const handle = {
67722
68060
  id,
@@ -68681,7 +69019,7 @@ var import_node_path54 = __toESM(require("path"), 1);
68681
69019
  // src/attachment/upload.ts
68682
69020
  var import_node_fs48 = __toESM(require("fs"), 1);
68683
69021
  var import_node_path52 = __toESM(require("path"), 1);
68684
- var import_node_crypto17 = __toESM(require("crypto"), 1);
69022
+ var import_node_crypto18 = __toESM(require("crypto"), 1);
68685
69023
  var import_promises8 = require("stream/promises");
68686
69024
  var UploadError = class extends Error {
68687
69025
  constructor(code, message) {
@@ -68705,11 +69043,11 @@ async function writeUploadedAttachment(args) {
68705
69043
  } catch (err) {
68706
69044
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
68707
69045
  }
68708
- const hasher = import_node_crypto17.default.createHash("sha256");
69046
+ const hasher = import_node_crypto18.default.createHash("sha256");
68709
69047
  let actualSize = 0;
68710
69048
  const tmpPath = import_node_path52.default.join(
68711
69049
  attachmentsRoot,
68712
- `.upload-${process.pid}-${Date.now()}-${import_node_crypto17.default.randomBytes(4).toString("hex")}`
69050
+ `.upload-${process.pid}-${Date.now()}-${import_node_crypto18.default.randomBytes(4).toString("hex")}`
68713
69051
  );
68714
69052
  try {
68715
69053
  await (0, import_promises8.pipeline)(
@@ -69560,7 +69898,7 @@ function runAttachmentGc(args) {
69560
69898
  // src/attachment/group.ts
69561
69899
  var import_node_fs51 = __toESM(require("fs"), 1);
69562
69900
  var import_node_path56 = __toESM(require("path"), 1);
69563
- var import_node_crypto18 = __toESM(require("crypto"), 1);
69901
+ var import_node_crypto19 = __toESM(require("crypto"), 1);
69564
69902
  init_protocol();
69565
69903
  var GroupFileStore = class {
69566
69904
  dataDir;
@@ -69649,7 +69987,7 @@ var GroupFileStore = class {
69649
69987
  entries[idx] = next;
69650
69988
  } else {
69651
69989
  next = {
69652
- id: `gf-${import_node_crypto18.default.randomBytes(6).toString("base64url")}`,
69990
+ id: `gf-${import_node_crypto19.default.randomBytes(6).toString("base64url")}`,
69653
69991
  relPath: input.relPath,
69654
69992
  from: input.from,
69655
69993
  label: input.label,
@@ -69795,7 +70133,7 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
69795
70133
  // src/tunnel/tunnel-manager.ts
69796
70134
  var import_node_fs56 = __toESM(require("fs"), 1);
69797
70135
  var import_node_path61 = __toESM(require("path"), 1);
69798
- var import_node_crypto19 = __toESM(require("crypto"), 1);
70136
+ var import_node_crypto20 = __toESM(require("crypto"), 1);
69799
70137
  var import_node_child_process16 = require("child_process");
69800
70138
 
69801
70139
  // src/tunnel/tunnel-store.ts
@@ -70294,7 +70632,7 @@ var TunnelManager = class {
70294
70632
  override: this.deps.frpcBinaryOverride ?? void 0
70295
70633
  });
70296
70634
  const tomlPath = import_node_path61.default.join(this.deps.dataDir, "frpc.toml");
70297
- const proxyName = `clawd-${t2.subdomain}-${localPort}-${import_node_crypto19.default.randomBytes(3).toString("hex")}`;
70635
+ const proxyName = `clawd-${t2.subdomain}-${localPort}-${import_node_crypto20.default.randomBytes(3).toString("hex")}`;
70298
70636
  const toml = buildFrpcToml({
70299
70637
  serverAddr: t2.frpsHost,
70300
70638
  serverPort: t2.frpsPort,
@@ -70395,7 +70733,7 @@ async function waitForFrpcReady(proc, timeoutMs) {
70395
70733
  // src/tunnel/device-key.ts
70396
70734
  var import_node_os22 = __toESM(require("os"), 1);
70397
70735
  var import_node_path62 = __toESM(require("path"), 1);
70398
- var import_node_crypto20 = __toESM(require("crypto"), 1);
70736
+ var import_node_crypto21 = __toESM(require("crypto"), 1);
70399
70737
  var DERIVE_SALT = "clawd-tunnel-device-v1";
70400
70738
  function deriveStableDeviceKey(opts = {}) {
70401
70739
  const hostname = opts.hostname ?? import_node_os22.default.hostname();
@@ -70405,13 +70743,13 @@ function deriveStableDeviceKey(opts = {}) {
70405
70743
  const normalizedDataDir = opts.dataDir ? import_node_path62.default.resolve(opts.dataDir) : null;
70406
70744
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
70407
70745
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
70408
- return import_node_crypto20.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
70746
+ return import_node_crypto21.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
70409
70747
  }
70410
70748
 
70411
70749
  // src/auth-store.ts
70412
70750
  var import_node_fs57 = __toESM(require("fs"), 1);
70413
70751
  var import_node_path63 = __toESM(require("path"), 1);
70414
- var import_node_crypto21 = __toESM(require("crypto"), 1);
70752
+ var import_node_crypto22 = __toESM(require("crypto"), 1);
70415
70753
  var AUTH_FILE_NAME = "auth.json";
70416
70754
  function authFilePath(dataDir) {
70417
70755
  return import_node_path63.default.join(dataDir, AUTH_FILE_NAME);
@@ -70448,10 +70786,10 @@ function loadOrCreateTunnelDeviceKey(opts) {
70448
70786
  return tunnelDeviceKey;
70449
70787
  }
70450
70788
  function defaultGenerateToken() {
70451
- return import_node_crypto21.default.randomBytes(32).toString("base64url");
70789
+ return import_node_crypto22.default.randomBytes(32).toString("base64url");
70452
70790
  }
70453
70791
  function defaultGenerateOwnerPrincipalId() {
70454
- return `owner-${import_node_crypto21.default.randomUUID()}`;
70792
+ return `owner-${import_node_crypto22.default.randomUUID()}`;
70455
70793
  }
70456
70794
  function readAuthFile(file) {
70457
70795
  try {
@@ -70583,7 +70921,7 @@ var OwnerIdentityStore = class {
70583
70921
  };
70584
70922
 
70585
70923
  // src/feishu-auth/login-flow.ts
70586
- var import_node_crypto22 = __toESM(require("crypto"), 1);
70924
+ var import_node_crypto23 = __toESM(require("crypto"), 1);
70587
70925
  var STATE_TTL_MS = 5 * 60 * 1e3;
70588
70926
  var LoginFlow = class {
70589
70927
  constructor(deps) {
@@ -70592,7 +70930,7 @@ var LoginFlow = class {
70592
70930
  deps;
70593
70931
  pendingStates = /* @__PURE__ */ new Map();
70594
70932
  start() {
70595
- const state = import_node_crypto22.default.randomBytes(16).toString("base64url");
70933
+ const state = import_node_crypto23.default.randomBytes(16).toString("base64url");
70596
70934
  const now = (this.deps.now ?? Date.now)();
70597
70935
  this.pendingStates.set(state, now);
70598
70936
  this.gcExpired(now);
@@ -71123,7 +71461,7 @@ init_protocol();
71123
71461
  var import_node_fs60 = __toESM(require("fs"), 1);
71124
71462
  var import_node_os24 = __toESM(require("os"), 1);
71125
71463
  var import_node_path66 = __toESM(require("path"), 1);
71126
- var import_node_crypto23 = require("crypto");
71464
+ var import_node_crypto24 = require("crypto");
71127
71465
  init_claude_history();
71128
71466
  function readJsonlEntries(file) {
71129
71467
  const raw = import_node_fs60.default.readFileSync(file, "utf8");
@@ -71160,7 +71498,7 @@ function forkSession(input) {
71160
71498
  throw new Error(`fork: messageUuid not found in transcript: ${input.messageUuid}`);
71161
71499
  }
71162
71500
  const sliced = mainEntries.slice(0, targetIdx + 1);
71163
- const forkedToolSessionId = (0, import_node_crypto23.randomUUID)();
71501
+ const forkedToolSessionId = (0, import_node_crypto24.randomUUID)();
71164
71502
  let parentUuid = null;
71165
71503
  const forkedLines = [];
71166
71504
  for (const entry of sliced) {
@@ -73948,7 +74286,7 @@ function computeMethodAccess(args) {
73948
74286
  }
73949
74287
 
73950
74288
  // src/version.ts
73951
- var version = "0.2.613".length > 0 ? "0.2.613" : "dev";
74289
+ var version = "0.2.614".length > 0 ? "0.2.614" : "dev";
73952
74290
 
73953
74291
  // src/cli-probe/probe.ts
73954
74292
  var fs84 = __toESM(require("fs"), 1);
@@ -74345,7 +74683,7 @@ init_protocol();
74345
74683
  // src/extension/bundle-zip.ts
74346
74684
  var import_promises12 = __toESM(require("fs/promises"), 1);
74347
74685
  var import_node_path74 = __toESM(require("path"), 1);
74348
- var import_node_crypto24 = __toESM(require("crypto"), 1);
74686
+ var import_node_crypto25 = __toESM(require("crypto"), 1);
74349
74687
  var import_jszip2 = __toESM(require_lib3(), 1);
74350
74688
  async function bundleExtensionDir(dir) {
74351
74689
  const entries = await listFilesSorted(dir);
@@ -74360,7 +74698,7 @@ async function bundleExtensionDir(dir) {
74360
74698
  compression: "DEFLATE",
74361
74699
  compressionOptions: { level: 6 }
74362
74700
  });
74363
- const sha256 = import_node_crypto24.default.createHash("sha256").update(buffer).digest("hex");
74701
+ const sha256 = import_node_crypto25.default.createHash("sha256").update(buffer).digest("hex");
74364
74702
  return { buffer, sha256 };
74365
74703
  }
74366
74704
  var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
@@ -74428,7 +74766,7 @@ function computePublishCheck(args) {
74428
74766
  var import_promises13 = __toESM(require("fs/promises"), 1);
74429
74767
  var import_node_path76 = __toESM(require("path"), 1);
74430
74768
  var import_node_os28 = __toESM(require("os"), 1);
74431
- var import_node_crypto25 = __toESM(require("crypto"), 1);
74769
+ var import_node_crypto26 = __toESM(require("crypto"), 1);
74432
74770
  var import_jszip3 = __toESM(require_lib3(), 1);
74433
74771
 
74434
74772
  // src/extension/paths.ts
@@ -74457,7 +74795,7 @@ var InstallError = class extends Error {
74457
74795
  };
74458
74796
  async function installFromChannel(args, deps) {
74459
74797
  const { channelRef, snapshotHash, bundleZip } = args;
74460
- const computed = import_node_crypto25.default.createHash("sha256").update(bundleZip).digest("hex");
74798
+ const computed = import_node_crypto26.default.createHash("sha256").update(bundleZip).digest("hex");
74461
74799
  if (computed !== snapshotHash) {
74462
74800
  throw new InstallError(
74463
74801
  "HASH_MISMATCH",
@@ -74549,7 +74887,7 @@ async function installFromChannel(args, deps) {
74549
74887
  var import_promises14 = __toESM(require("fs/promises"), 1);
74550
74888
  var import_node_path77 = __toESM(require("path"), 1);
74551
74889
  var import_node_os29 = __toESM(require("os"), 1);
74552
- var import_node_crypto26 = __toESM(require("crypto"), 1);
74890
+ var import_node_crypto27 = __toESM(require("crypto"), 1);
74553
74891
  var import_jszip4 = __toESM(require_lib3(), 1);
74554
74892
  var UpdateError = class extends Error {
74555
74893
  constructor(code, message) {
@@ -74587,7 +74925,7 @@ async function updateFromChannel(args, deps) {
74587
74925
  if (e instanceof UpdateError) throw e;
74588
74926
  throw e;
74589
74927
  }
74590
- const computed = import_node_crypto26.default.createHash("sha256").update(bundleZip).digest("hex");
74928
+ const computed = import_node_crypto27.default.createHash("sha256").update(bundleZip).digest("hex");
74591
74929
  if (computed !== snapshotHash) {
74592
74930
  throw new UpdateError(
74593
74931
  "HASH_MISMATCH",
@@ -75716,7 +76054,7 @@ async function uninstall(deps) {
75716
76054
  }
75717
76055
 
75718
76056
  // src/handlers/index.ts
75719
- var import_node_crypto27 = require("crypto");
76057
+ var import_node_crypto28 = require("crypto");
75720
76058
 
75721
76059
  // src/handlers/peer-exec.ts
75722
76060
  init_protocol();
@@ -75873,7 +76211,7 @@ function buildMethodHandlers(deps) {
75873
76211
  const c2 = deps.contactStore.get(deviceId);
75874
76212
  return c2 ? { deviceId: c2.deviceId, remoteUrl: c2.remoteUrl, connectToken: c2.connectToken } : null;
75875
76213
  },
75876
- genId: () => (0, import_node_crypto27.randomUUID)(),
76214
+ genId: () => (0, import_node_crypto28.randomUUID)(),
75877
76215
  now: () => Date.now(),
75878
76216
  forwardInboxPostToPeer,
75879
76217
  logger: deps.logger
@@ -76606,7 +76944,7 @@ async function startDaemon(config) {
76606
76944
  const channelStore = createChannelStore({
76607
76945
  filePath: import_node_path85.default.join(config.dataDir, "channels.json"),
76608
76946
  now: () => Date.now(),
76609
- genId: () => `ch-${(0, import_node_crypto28.randomUUID)().slice(0, 8)}`,
76947
+ genId: () => `ch-${(0, import_node_crypto29.randomUUID)().slice(0, 8)}`,
76610
76948
  onFlushError: (err) => logger.error("channel store flush failed", {
76611
76949
  reason: err instanceof Error ? err.message : String(err)
76612
76950
  })
@@ -76615,7 +76953,7 @@ async function startDaemon(config) {
76615
76953
  const topicStore = createTopicStore({
76616
76954
  filePath: import_node_path85.default.join(config.dataDir, "topics.json"),
76617
76955
  now: () => Date.now(),
76618
- genId: () => `tp-${(0, import_node_crypto28.randomUUID)().slice(0, 8)}`,
76956
+ genId: () => `tp-${(0, import_node_crypto29.randomUUID)().slice(0, 8)}`,
76619
76957
  onFlushError: (err) => logger.error("topic store flush failed", {
76620
76958
  reason: err instanceof Error ? err.message : String(err)
76621
76959
  })
@@ -76623,7 +76961,7 @@ async function startDaemon(config) {
76623
76961
  await topicStore.load();
76624
76962
  let deliverPendingRef;
76625
76963
  const personaDispatchManager = new PersonaDispatchManager({
76626
- genId: () => (0, import_node_crypto28.randomUUID)(),
76964
+ genId: () => (0, import_node_crypto29.randomUUID)(),
76627
76965
  store: dispatchStore,
76628
76966
  now: () => Date.now(),
76629
76967
  onCompleted: (sid) => deliverPendingRef?.(sid),
@@ -77047,6 +77385,11 @@ async function startDaemon(config) {
77047
77385
  manager.setCloudCanceller(async (file, runner) => {
77048
77386
  const personaId2 = personaIdFromCloudCwd(file.cwd);
77049
77387
  if (!personaId2) throw new Error(`\u4E91\u4F1A\u8BDD ${file.sessionId} \u7F3A\u5C11 gateway persona ID`);
77388
+ if (file.cloudConversationId) {
77389
+ await cloudGateway.cancelConversation(personaId2, file.cloudConversationId);
77390
+ runner?.confirmCloudCancelled();
77391
+ return;
77392
+ }
77050
77393
  const executionId = file.cloudExecutionId ?? file.sessionId;
77051
77394
  if (!file.cloudGatewaySessionId) throw new Error(`\u4E91\u4F1A\u8BDD ${file.sessionId} \u5C1A\u672A\u6536\u5230 gateway ready`);
77052
77395
  cloudCancelOutbox.record(executionId, personaId2, file.cloudGatewaySessionId);