@clawos-dev/clawd 0.2.700 → 0.2.701

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.
@@ -31133,6 +31133,11 @@ var CloudWorkspaceResultSchema = external_exports.object({
31133
31133
  notStored: external_exports.boolean().optional()
31134
31134
  }).optional()
31135
31135
  });
31136
+ var CloudForkResultSchema = external_exports.object({
31137
+ threadId: external_exports.string().min(1),
31138
+ conversationId: external_exports.string().min(1),
31139
+ workspace: WorkspaceBindingSchema.optional()
31140
+ });
31136
31141
 
31137
31142
  // ../protocol/src/subscription-channel.ts
31138
31143
  var SubscriptionChannelSchema = external_exports.object({
@@ -33349,6 +33354,11 @@ var SessionCreateArgs = external_exports.object({
33349
33354
  serviceTier: CodexServiceTierSchema.optional(),
33350
33355
  iconKey: external_exports.string().optional(),
33351
33356
  forkedFromSessionId: external_exports.string().min(1).optional(),
33357
+ /** Cloud fork point (with forkedFromSessionId of a cloud session): Claude cuts at a message, Codex at a completed turn. */
33358
+ forkAt: external_exports.union([
33359
+ external_exports.object({ messageUuid: external_exports.string().min(1) }).strict(),
33360
+ external_exports.object({ turnId: external_exports.string().min(1) }).strict()
33361
+ ]).optional(),
33352
33362
  ephemeral: external_exports.boolean().optional(),
33353
33363
  // Persona context derives cwd and spawn defaults; missing cwd is allowed when persona identity determines it.
33354
33364
  ownerPersonaId: external_exports.string().min(1).optional(),
package/dist/cli.cjs CHANGED
@@ -4150,7 +4150,7 @@ var init_zod = __esm({
4150
4150
  });
4151
4151
 
4152
4152
  // ../protocol/src/cloud-workspace-schemas.ts
4153
- var WORKSPACE_COMPARISON_VALUES, Id, RepoId, Part, WorkspaceBranchSchema, FilePath, WorkspaceBindingSchema, WorkspaceRepoSchema, WorkspaceStatusSchema, Session, CloudWorkspaceArgsSchema, CloudWorkspaceResultSchema;
4153
+ var WORKSPACE_COMPARISON_VALUES, Id, RepoId, Part, WorkspaceBranchSchema, FilePath, WorkspaceBindingSchema, WorkspaceRepoSchema, WorkspaceStatusSchema, Session, CloudWorkspaceArgsSchema, CloudWorkspaceResultSchema, CloudForkResultSchema;
4154
4154
  var init_cloud_workspace_schemas = __esm({
4155
4155
  "../protocol/src/cloud-workspace-schemas.ts"() {
4156
4156
  "use strict";
@@ -4251,6 +4251,11 @@ var init_cloud_workspace_schemas = __esm({
4251
4251
  notStored: external_exports.boolean().optional()
4252
4252
  }).optional()
4253
4253
  });
4254
+ CloudForkResultSchema = external_exports.object({
4255
+ threadId: external_exports.string().min(1),
4256
+ conversationId: external_exports.string().min(1),
4257
+ workspace: WorkspaceBindingSchema.optional()
4258
+ });
4254
4259
  }
4255
4260
  });
4256
4261
 
@@ -8558,6 +8563,11 @@ var init_schemas = __esm({
8558
8563
  serviceTier: CodexServiceTierSchema.optional(),
8559
8564
  iconKey: external_exports.string().optional(),
8560
8565
  forkedFromSessionId: external_exports.string().min(1).optional(),
8566
+ /** Cloud fork point (with forkedFromSessionId of a cloud session): Claude cuts at a message, Codex at a completed turn. */
8567
+ forkAt: external_exports.union([
8568
+ external_exports.object({ messageUuid: external_exports.string().min(1) }).strict(),
8569
+ external_exports.object({ turnId: external_exports.string().min(1) }).strict()
8570
+ ]).optional(),
8561
8571
  ephemeral: external_exports.boolean().optional(),
8562
8572
  // Persona context derives cwd and spawn defaults; missing cwd is allowed when persona identity determines it.
8563
8573
  ownerPersonaId: external_exports.string().min(1).optional(),
@@ -46811,7 +46821,7 @@ function requireEnvironmentIdentity(stores, environment) {
46811
46821
  var import_node_path90 = __toESM(require("path"), 1);
46812
46822
  var import_node_fs69 = __toESM(require("fs"), 1);
46813
46823
  var import_node_os33 = __toESM(require("os"), 1);
46814
- var import_node_crypto32 = require("crypto");
46824
+ var import_node_crypto33 = require("crypto");
46815
46825
  var import_node_url6 = require("url");
46816
46826
 
46817
46827
  // src/logger.ts
@@ -50287,10 +50297,14 @@ var SessionManager = class {
50287
50297
  const iso = nowIso2(this.deps);
50288
50298
  const toolSessionId = this.deps.mode === "tui" && tool === "claude" ? (0, import_node_crypto5.randomUUID)() : void 0;
50289
50299
  const file = {
50290
- sessionId: newSessionId(),
50300
+ sessionId: args.cloudFork?.sessionId ?? newSessionId(),
50291
50301
  cwd,
50292
50302
  tool,
50293
- toolSessionId,
50303
+ toolSessionId: args.cloudFork?.toolSessionId ?? toolSessionId,
50304
+ ...args.cloudFork ? {
50305
+ cloudConversationId: args.cloudFork.cloudConversationId,
50306
+ ...args.cloudFork.cloudWorkspace ? { cloudWorkspace: args.cloudFork.cloudWorkspace } : {}
50307
+ } : {},
50294
50308
  label: args.label,
50295
50309
  aiLabel: args.aiLabel,
50296
50310
  ...personaDefaultFields(engineOverridden ? void 0 : personaDefaults, {
@@ -58098,6 +58112,7 @@ var GATEWAY_ROUTES = {
58098
58112
  conversationCancel: (personaId2, conversationId) => `/p/${encodeURIComponent(personaId2)}/conversations/${encodeURIComponent(conversationId)}:cancel`,
58099
58113
  volume: () => "/me/volume",
58100
58114
  workspace: (personaId2) => `/p/${encodeURIComponent(personaId2)}/workspace`,
58115
+ fork: (personaId2) => `/p/${encodeURIComponent(personaId2)}/fork`,
58101
58116
  metrics: (personaId2) => `/metrics/${encodeURIComponent(personaId2)}`
58102
58117
  };
58103
58118
  var METRICS_TIMEOUT_MS = 3e4;
@@ -58236,6 +58251,21 @@ var CloudGatewayClient = class {
58236
58251
  log("finished", { outcome, statusCode, durationMs: Math.round(performance.now() - started) });
58237
58252
  }
58238
58253
  }
58254
+ /** Copy a cloud conversation under the child's ids (design 2026-10-20-cloud-session-fork §4.2); nothing is written locally here. */
58255
+ async fork(personaId2, body) {
58256
+ const { apiKey, gatewayUrl } = await this.credentials();
58257
+ const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.fork(personaId2), {
58258
+ method: "POST",
58259
+ headers: { authorization: "Bearer " + apiKey, "content-type": "application/json" },
58260
+ body: JSON.stringify(body),
58261
+ signal: AbortSignal.timeout(3e5)
58262
+ });
58263
+ const result = await res.json().catch(() => ({}));
58264
+ if (res.status === 404 && !result.error?.code) throw new Error("\u4E91\u7AEF\u6682\u4E0D\u652F\u6301 fork");
58265
+ if (res.status === 409 && result.error?.code === "FORK_POINT_NOT_SYNCED") throw new Error("\u8FD9\u6761\u6D88\u606F\u8FD8\u6CA1\u540C\u6B65\u5230\u4E91\u7AEF\uFF0C\u7A0D\u540E\u518D\u8BD5");
58266
+ if (!res.ok) throw new Error("\u5206\u53C9\u5931\u8D25\uFF1A" + (result.error?.message ?? `gateway \u56DE ${res.status}`));
58267
+ return CloudForkResultSchema.parse(result);
58268
+ }
58239
58269
  async openTurn(personaId2, opts) {
58240
58270
  const { apiKey, gatewayUrl } = await this.credentials();
58241
58271
  const health = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.healthz());
@@ -67270,7 +67300,7 @@ var CLOUD_CODEX_CAPABILITIES = {
67270
67300
  toolSessionIdLabel: "Codex Thread ID",
67271
67301
  models: [],
67272
67302
  permissionModes: [CLOUD_APPROVAL],
67273
- features: { rewind: false, subagents: true, tui: false, observe: false, fileSharing: false, fork: false },
67303
+ features: { rewind: false, subagents: true, tui: false, observe: false, fileSharing: false, fork: true },
67274
67304
  configSchema: [
67275
67305
  {
67276
67306
  name: "permissionMode",
@@ -68111,7 +68141,7 @@ var CLOUD_CLAUDE_CAPABILITIES = {
68111
68141
  toolSessionIdLabel: "Claude Session ID",
68112
68142
  models: [],
68113
68143
  permissionModes: [CLOUD_APPROVAL3],
68114
- features: { rewind: false, subagents: false, tui: false, observe: false, fileSharing: false, fork: false },
68144
+ features: { rewind: false, subagents: false, tui: false, observe: false, fileSharing: false, fork: true },
68115
68145
  configSchema: [
68116
68146
  {
68117
68147
  name: "permissionMode",
@@ -72823,6 +72853,45 @@ var import_node_os26 = __toESM(require("os"), 1);
72823
72853
  var import_node_path70 = __toESM(require("path"), 1);
72824
72854
  var import_node_crypto26 = require("crypto");
72825
72855
  init_claude_history();
72856
+
72857
+ // src/session/fork-transcript.ts
72858
+ var ForkPointNotFoundError = class extends Error {
72859
+ code = "FORK_POINT_NOT_FOUND";
72860
+ };
72861
+ function forkTranscriptEntries(entries, opts) {
72862
+ const mainEntries = entries.filter((e) => e.isSidechain !== true);
72863
+ const targetIdx = mainEntries.findIndex((e) => e.uuid === opts.messageUuid);
72864
+ if (targetIdx === -1) {
72865
+ throw new ForkPointNotFoundError(`fork: messageUuid not found in transcript: ${opts.messageUuid}`);
72866
+ }
72867
+ const moveCwd = (cwd) => {
72868
+ const from = opts.cwd.from;
72869
+ if (cwd === from) return opts.cwd.to;
72870
+ return cwd.startsWith(`${from}/`) ? opts.cwd.to + cwd.slice(from.length) : cwd;
72871
+ };
72872
+ let parentUuid = null;
72873
+ const out = [];
72874
+ for (const entry of mainEntries.slice(0, targetIdx + 1)) {
72875
+ const originalUuid = entry.uuid;
72876
+ const forked = {
72877
+ ...entry,
72878
+ sessionId: opts.sessionId,
72879
+ parentUuid,
72880
+ isSidechain: false
72881
+ };
72882
+ if (opts.cwd && typeof entry.cwd === "string") forked.cwd = moveCwd(entry.cwd);
72883
+ if (originalUuid) {
72884
+ forked.forkedFrom = { sessionId: entry.sessionId ?? opts.sourceSessionId, messageUuid: originalUuid };
72885
+ }
72886
+ if (entry.type !== "progress" && originalUuid) {
72887
+ parentUuid = originalUuid;
72888
+ }
72889
+ out.push(forked);
72890
+ }
72891
+ return out;
72892
+ }
72893
+
72894
+ // src/session/fork.ts
72826
72895
  function readJsonlEntries(file) {
72827
72896
  const raw = import_node_fs62.default.readFileSync(file, "utf8");
72828
72897
  const out = [];
@@ -72851,33 +72920,12 @@ function forkSession(input) {
72851
72920
  sourceFile = found;
72852
72921
  projectDir = import_node_path70.default.dirname(found);
72853
72922
  }
72854
- const entries = readJsonlEntries(sourceFile);
72855
- const mainEntries = entries.filter((e) => e.isSidechain !== true);
72856
- const targetIdx = mainEntries.findIndex((e) => e.uuid === input.messageUuid);
72857
- if (targetIdx === -1) {
72858
- throw new Error(`fork: messageUuid not found in transcript: ${input.messageUuid}`);
72859
- }
72860
- const sliced = mainEntries.slice(0, targetIdx + 1);
72861
72923
  const forkedToolSessionId = (0, import_node_crypto26.randomUUID)();
72862
- let parentUuid = null;
72863
- const forkedLines = [];
72864
- for (const entry of sliced) {
72865
- const originalSessionId = entry.sessionId ?? input.toolSessionId;
72866
- const originalUuid = entry.uuid;
72867
- const forked = {
72868
- ...entry,
72869
- sessionId: forkedToolSessionId,
72870
- parentUuid,
72871
- isSidechain: false
72872
- };
72873
- if (originalUuid) {
72874
- forked.forkedFrom = { sessionId: originalSessionId, messageUuid: originalUuid };
72875
- }
72876
- if (entry.type !== "progress" && originalUuid) {
72877
- parentUuid = originalUuid;
72878
- }
72879
- forkedLines.push(JSON.stringify(forked));
72880
- }
72924
+ const forkedLines = forkTranscriptEntries(readJsonlEntries(sourceFile), {
72925
+ messageUuid: input.messageUuid,
72926
+ sessionId: forkedToolSessionId,
72927
+ sourceSessionId: input.toolSessionId
72928
+ }).map((entry) => JSON.stringify(entry));
72881
72929
  const outDir = input.targetCwd ? import_node_path70.default.join(baseDir, "projects", cwdToHashDir(input.targetCwd)) : projectDir;
72882
72930
  const forkedFilePath = import_node_path70.default.join(outDir, `${forkedToolSessionId}.jsonl`);
72883
72931
  import_node_fs62.default.mkdirSync(outDir, { recursive: true });
@@ -73227,6 +73275,7 @@ function appendImportLedger(dataDir, entry) {
73227
73275
 
73228
73276
  // src/handlers/session.ts
73229
73277
  init_protocol();
73278
+ var import_node_crypto27 = require("crypto");
73230
73279
  var import_node_path74 = __toESM(require("path"), 1);
73231
73280
  function canReadSearchHit(ctx, file) {
73232
73281
  if (!ctx || ctx.principal.kind === "owner") return true;
@@ -73277,6 +73326,45 @@ function buildSessionHandlers(deps) {
73277
73326
  `${MASTER_PERSONA_ID} \u7684\u4F1A\u8BDD\u7531\u8BDD\u9898\u521B\u5EFA\uFF0C\u4E0D\u80FD\u76F4\u63A5\u5EFA\uFF1A\u8BF7\u5728\u9891\u9053\u91CC\u65B0\u5EFA\u8BDD\u9898`
73278
73327
  );
73279
73328
  };
73329
+ const forkCloudSession = async (args, forkAt, tool, ctx) => {
73330
+ if (!args.forkedFromSessionId) throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "fork: forkAt requires forkedFromSessionId");
73331
+ const source = manager.get({ sessionId: args.forkedFromSessionId }).response;
73332
+ if (!canReadSearchHit(ctx, source)) {
73333
+ throw new ClawdError(ERROR_CODES.UNAUTHORIZED, `principal ${ctx?.principal.kind}:${ctx?.principal.id} cannot fork session ${source.sessionId}`);
73334
+ }
73335
+ const engine = source.tool === CLAUDE_CLOUD_TOOL_ID ? "claude" : source.tool === CODEX_CLOUD_TOOL_ID ? "codex" : null;
73336
+ if (!engine || !deps.cloudFork) throw new ClawdError(ERROR_CODES.TOOL_NOT_SUPPORTED, `tool ${source.tool} does not support cloud fork`);
73337
+ if (tool !== source.tool || args.ownerPersonaId !== source.ownerPersonaId) {
73338
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "fork: cloud fork must stay on the source persona and engine");
73339
+ }
73340
+ if (engine === "claude" ? !("messageUuid" in forkAt) : !("turnId" in forkAt)) {
73341
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `fork: ${source.tool} needs ${engine === "claude" ? "messageUuid" : "turnId"}`);
73342
+ }
73343
+ const sourceConversationId = source.cloudConversationId ?? source.cloudExecutionId;
73344
+ if (!source.toolSessionId || !sourceConversationId) throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "fork: \u6E90\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u4E91\u7AEF\u5386\u53F2");
73345
+ if (source.cloudPendingTurns?.length) throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "fork: \u8BF7\u7B49\u5F85\u6E90\u4F1A\u8BDD\u5F53\u524D\u4EFB\u52A1\u5B8C\u6210");
73346
+ const sessionId = newSessionId();
73347
+ const threadId = (0, import_node_crypto27.randomUUID)();
73348
+ const result = await deps.cloudFork(source, {
73349
+ engine,
73350
+ source: {
73351
+ conversationId: sourceConversationId,
73352
+ threadId: source.toolSessionId,
73353
+ ...source.cloudWorkspace ? { workspaceId: source.cloudWorkspace.workspaceId } : {}
73354
+ },
73355
+ at: forkAt,
73356
+ child: { conversationId: sessionId, threadId, ...source.cloudWorkspace ? { workspaceId: sessionId } : {} }
73357
+ });
73358
+ if (result.threadId !== threadId || result.conversationId !== sessionId || Boolean(result.workspace) !== Boolean(source.cloudWorkspace)) {
73359
+ throw new Error("\u5206\u53C9\u5931\u8D25\uFF1Agateway \u8FD4\u56DE\u7684\u5B50\u4F1A\u8BDD\u4E0E\u8BF7\u6C42\u4E0D\u4E00\u81F4");
73360
+ }
73361
+ return {
73362
+ sessionId,
73363
+ toolSessionId: threadId,
73364
+ cloudConversationId: sessionId,
73365
+ ...result.workspace ? { cloudWorkspace: result.workspace } : {}
73366
+ };
73367
+ };
73280
73368
  const create = async (frame, _client, ctx) => {
73281
73369
  const args = SessionCreateArgs.parse(frame);
73282
73370
  assertNotMasterPersona(args.ownerPersonaId, args.ephemeral);
@@ -73303,8 +73391,10 @@ function buildSessionHandlers(deps) {
73303
73391
  const creatorPrincipalId = ctx?.principal.id ?? ownerPrincipalId;
73304
73392
  const creatorDisplayName = ctx?.principal.displayName ?? "Owner";
73305
73393
  const creatorFeishuUnionId = ctx?.principal.feishuUnionId;
73394
+ const { forkAt, ...createArgs } = args;
73395
+ const cloudFork = forkAt ? await forkCloudSession(args, forkAt, tool, ctx) : void 0;
73306
73396
  const { response, broadcast } = manager.create(
73307
- { ...args, tool, model, effort },
73397
+ { ...createArgs, tool, model, effort, ...cloudFork ? { cloudFork } : {} },
73308
73398
  creatorPrincipalId,
73309
73399
  creatorDisplayName,
73310
73400
  creatorFeishuUnionId
@@ -75391,7 +75481,7 @@ function buildCloudConnectorsHandlers(deps) {
75391
75481
 
75392
75482
  // src/handlers/cloud-workspace.ts
75393
75483
  init_protocol();
75394
- var import_node_crypto27 = require("crypto");
75484
+ var import_node_crypto28 = require("crypto");
75395
75485
  function buildCloudWorkspaceHandlers(deps) {
75396
75486
  return {
75397
75487
  "cloud:workspace": async (frame, _client, ctx) => {
@@ -75419,7 +75509,7 @@ function buildCloudWorkspaceHandlers(deps) {
75419
75509
  ) || file.cloudPendingTurns?.length))
75420
75510
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "\u8BF7\u7B49\u5F85\u5F53\u524D\u4EFB\u52A1\u5B8C\u6210");
75421
75511
  const conversationId = file.cloudConversationId ?? file.cloudExecutionId ?? file.sessionId;
75422
- const traceId = typeof frame.requestId === "string" && /^[A-Za-z0-9:_-]{1,128}$/.test(frame.requestId) ? frame.requestId : (0, import_node_crypto27.randomUUID)();
75512
+ const traceId = typeof frame.requestId === "string" && /^[A-Za-z0-9:_-]{1,128}$/.test(frame.requestId) ? frame.requestId : (0, import_node_crypto28.randomUUID)();
75423
75513
  const started = performance.now();
75424
75514
  const meta = { traceId, sessionId: args.sessionId, action: args.action };
75425
75515
  const log = (event, detail) => {
@@ -75778,7 +75868,7 @@ function computeMethodAccess(args) {
75778
75868
  }
75779
75869
 
75780
75870
  // src/version.ts
75781
- var version = "0.2.700".length > 0 ? "0.2.700" : "dev";
75871
+ var version = "0.2.701".length > 0 ? "0.2.701" : "dev";
75782
75872
 
75783
75873
  // src/cli-probe/probe.ts
75784
75874
  var fs87 = __toESM(require("fs"), 1);
@@ -76176,7 +76266,7 @@ init_protocol();
76176
76266
  // src/extension/bundle-zip.ts
76177
76267
  var import_promises12 = __toESM(require("fs/promises"), 1);
76178
76268
  var import_node_path79 = __toESM(require("path"), 1);
76179
- var import_node_crypto28 = __toESM(require("crypto"), 1);
76269
+ var import_node_crypto29 = __toESM(require("crypto"), 1);
76180
76270
  var import_jszip3 = __toESM(require_lib3(), 1);
76181
76271
  async function bundleExtensionDir(dir) {
76182
76272
  const entries = await listFilesSorted(dir);
@@ -76191,7 +76281,7 @@ async function bundleExtensionDir(dir) {
76191
76281
  compression: "DEFLATE",
76192
76282
  compressionOptions: { level: 6 }
76193
76283
  });
76194
- const sha256 = import_node_crypto28.default.createHash("sha256").update(buffer).digest("hex");
76284
+ const sha256 = import_node_crypto29.default.createHash("sha256").update(buffer).digest("hex");
76195
76285
  return { buffer, sha256 };
76196
76286
  }
76197
76287
  var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
@@ -76259,7 +76349,7 @@ function computePublishCheck(args) {
76259
76349
  var import_promises13 = __toESM(require("fs/promises"), 1);
76260
76350
  var import_node_path81 = __toESM(require("path"), 1);
76261
76351
  var import_node_os30 = __toESM(require("os"), 1);
76262
- var import_node_crypto29 = __toESM(require("crypto"), 1);
76352
+ var import_node_crypto30 = __toESM(require("crypto"), 1);
76263
76353
  var import_jszip4 = __toESM(require_lib3(), 1);
76264
76354
 
76265
76355
  // src/extension/paths.ts
@@ -76288,7 +76378,7 @@ var InstallError = class extends Error {
76288
76378
  };
76289
76379
  async function installFromChannel(args, deps) {
76290
76380
  const { channelRef, snapshotHash, bundleZip } = args;
76291
- const computed = import_node_crypto29.default.createHash("sha256").update(bundleZip).digest("hex");
76381
+ const computed = import_node_crypto30.default.createHash("sha256").update(bundleZip).digest("hex");
76292
76382
  if (computed !== snapshotHash) {
76293
76383
  throw new InstallError(
76294
76384
  "HASH_MISMATCH",
@@ -76380,7 +76470,7 @@ async function installFromChannel(args, deps) {
76380
76470
  var import_promises14 = __toESM(require("fs/promises"), 1);
76381
76471
  var import_node_path82 = __toESM(require("path"), 1);
76382
76472
  var import_node_os31 = __toESM(require("os"), 1);
76383
- var import_node_crypto30 = __toESM(require("crypto"), 1);
76473
+ var import_node_crypto31 = __toESM(require("crypto"), 1);
76384
76474
  var import_jszip5 = __toESM(require_lib3(), 1);
76385
76475
  var UpdateError = class extends Error {
76386
76476
  constructor(code, message) {
@@ -76418,7 +76508,7 @@ async function updateFromChannel(args, deps) {
76418
76508
  if (e instanceof UpdateError) throw e;
76419
76509
  throw e;
76420
76510
  }
76421
- const computed = import_node_crypto30.default.createHash("sha256").update(bundleZip).digest("hex");
76511
+ const computed = import_node_crypto31.default.createHash("sha256").update(bundleZip).digest("hex");
76422
76512
  if (computed !== snapshotHash) {
76423
76513
  throw new UpdateError(
76424
76514
  "HASH_MISMATCH",
@@ -77562,7 +77652,7 @@ async function uninstall(deps) {
77562
77652
  }
77563
77653
 
77564
77654
  // src/handlers/index.ts
77565
- var import_node_crypto31 = require("crypto");
77655
+ var import_node_crypto32 = require("crypto");
77566
77656
 
77567
77657
  // src/handlers/peer-exec.ts
77568
77658
  init_protocol();
@@ -77719,7 +77809,7 @@ function buildMethodHandlers(deps) {
77719
77809
  const c2 = deps.contactStore.get(deviceId);
77720
77810
  return c2 ? { deviceId: c2.deviceId, remoteUrl: c2.remoteUrl, connectToken: c2.connectToken } : null;
77721
77811
  },
77722
- genId: () => (0, import_node_crypto31.randomUUID)(),
77812
+ genId: () => (0, import_node_crypto32.randomUUID)(),
77723
77813
  now: () => Date.now(),
77724
77814
  forwardInboxPostToPeer,
77725
77815
  logger: deps.logger
@@ -78453,7 +78543,7 @@ async function startDaemon(config) {
78453
78543
  const channelStore = createChannelStore({
78454
78544
  filePath: import_node_path90.default.join(config.dataDir, "channels.json"),
78455
78545
  now: () => Date.now(),
78456
- genId: () => `ch-${(0, import_node_crypto32.randomUUID)().slice(0, 8)}`,
78546
+ genId: () => `ch-${(0, import_node_crypto33.randomUUID)().slice(0, 8)}`,
78457
78547
  onFlushError: (err) => logger.error("channel store flush failed", {
78458
78548
  reason: err instanceof Error ? err.message : String(err)
78459
78549
  })
@@ -78462,7 +78552,7 @@ async function startDaemon(config) {
78462
78552
  const topicStore = createTopicStore({
78463
78553
  filePath: import_node_path90.default.join(config.dataDir, "topics.json"),
78464
78554
  now: () => Date.now(),
78465
- genId: () => `tp-${(0, import_node_crypto32.randomUUID)().slice(0, 8)}`,
78555
+ genId: () => `tp-${(0, import_node_crypto33.randomUUID)().slice(0, 8)}`,
78466
78556
  onFlushError: (err) => logger.error("topic store flush failed", {
78467
78557
  reason: err instanceof Error ? err.message : String(err)
78468
78558
  })
@@ -78470,7 +78560,7 @@ async function startDaemon(config) {
78470
78560
  await topicStore.load();
78471
78561
  let deliverPendingRef;
78472
78562
  const personaDispatchManager = new PersonaDispatchManager({
78473
- genId: () => (0, import_node_crypto32.randomUUID)(),
78563
+ genId: () => (0, import_node_crypto33.randomUUID)(),
78474
78564
  store: dispatchStore,
78475
78565
  now: () => Date.now(),
78476
78566
  onCompleted: (sid) => deliverPendingRef?.(sid),
@@ -78494,7 +78584,7 @@ async function startDaemon(config) {
78494
78584
  const readSecrets = () => readSecretTable({ bundleRoot: secretsBundleRoot, overrideDir: localSecretsDir, logger });
78495
78585
  const githubTokenFile = import_node_path90.default.join(localSecretsDir, GITHUB_TOKEN_FILE);
78496
78586
  const managedGithubVar = "CLAWD_GITHUB_MANAGED_TOKEN_SHA256";
78497
- const githubFingerprint = (token) => (0, import_node_crypto32.createHash)("sha256").update(token).digest("hex");
78587
+ const githubFingerprint = (token) => (0, import_node_crypto33.createHash)("sha256").update(token).digest("hex");
78498
78588
  const githubIdentity = createGithubIdentity({
78499
78589
  isManaged: (token) => readEnvFileVar(githubTokenFile, managedGithubVar) === githubFingerprint(token),
78500
78590
  markManaged: (token) => {
@@ -79333,6 +79423,11 @@ async function startDaemon(config) {
79333
79423
  fill: (args) => centralConnectorFill({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ...args })
79334
79424
  }
79335
79425
  },
79426
+ cloudFork: (file, body) => {
79427
+ const personaId2 = personaIdFromCloudCwd(file.cwd);
79428
+ if (!personaId2) throw new Error(`\u4E91\u4F1A\u8BDD ${file.sessionId} \u7F3A\u5C11 gateway persona ID`);
79429
+ return gatewayForEnvironment(file.cloudEnvironment).fork(personaId2, body);
79430
+ },
79336
79431
  cloudWorkspace: {
79337
79432
  logger,
79338
79433
  getFile: (id) => manager.get({ sessionId: id }).response,