@nowcrew/daemon 0.6.88-beta.14 → 0.6.88-beta.16

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.
Files changed (38) hide show
  1. package/README.md +15 -0
  2. package/dist/daemon-update-eligibility.js +1 -1
  3. package/dist/local-executor.js +4 -2
  4. package/dist/machine-info.js +16 -4
  5. package/dist/main.js +4 -69
  6. package/dist/project-skills/execution-adapter.js +3 -2
  7. package/dist/project-skills/runtime-launch.js +14 -0
  8. package/dist/provider-env.js +3 -4
  9. package/dist/runtimes/deepseek-harness-profile.js +92 -0
  10. package/dist/runtimes/deepseek-harness-runner.js +224 -0
  11. package/dist/runtimes/deepseek-harness.js +61 -10
  12. package/dist/runtimes/kimi-acp-runner.js +5 -5
  13. package/dist/serve.js +14 -62
  14. package/dist/storage-identity.js +0 -3
  15. package/dist/storage-serve-adapter.js +6 -72
  16. package/dist/supervised-runtime.js +17 -2
  17. package/package.json +1 -1
  18. package/dist/daemon-migration-controller.js +0 -211
  19. package/dist/daemon-migration-wiring.js +0 -22
  20. package/dist/storage-control.js +0 -212
  21. package/dist/storage-copy.js +0 -628
  22. package/dist/storage-credential-guard.js +0 -21
  23. package/dist/storage-doctor.js +0 -228
  24. package/dist/storage-manifest.js +0 -1027
  25. package/dist/storage-migration-controller.js +0 -440
  26. package/dist/storage-migration-manifest.js +0 -105
  27. package/dist/storage-migration-policy.js +0 -100
  28. package/dist/storage-runtime-controller.js +0 -381
  29. package/dist/storage-runtime-readiness.js +0 -240
  30. package/dist/storage-safe-fs.js +0 -302
  31. package/dist/storage-status-delivery.js +0 -224
  32. package/dist/storage-status-replay.js +0 -140
  33. package/dist/storage-target-confirmation.js +0 -129
  34. package/dist/storage-worker-command.js +0 -37
  35. package/dist/storage-worker-executor.js +0 -449
  36. package/dist/storage-worker-handoff.js +0 -151
  37. package/dist/storage-worker-operation.js +0 -294
  38. package/dist/storage-worker-status.js +0 -259
@@ -1,20 +1,71 @@
1
1
  import spawn from "cross-spawn";
2
2
  import { isAbsolute } from "node:path";
3
+ import { mkdtemp, rm } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { prepareHarnessProfile } from "./deepseek-harness-profile.js";
3
7
  import { probeKimiAcp } from "./kimi-acp-runner.js";
4
8
  export const DEEPSEEK_HARNESS_CONFIG_ENV = "NOWCREW_DEEPSEEK_HARNESS_CONFIG";
9
+ export const DEEPSEEK_HARNESS_PATCH_ENV = "NOWCREW_DEEPSEEK_HARNESS_PATCH";
10
+ export function deepSeekHarnessLauncher(env = process.env) {
11
+ const configPath = env[DEEPSEEK_HARNESS_CONFIG_ENV]?.trim();
12
+ const patchPath = env[DEEPSEEK_HARNESS_PATCH_ENV]?.trim();
13
+ if (configPath && !isAbsolute(configPath))
14
+ throw new Error(`${DEEPSEEK_HARNESS_CONFIG_ENV} must be an absolute path`);
15
+ if (patchPath && !isAbsolute(patchPath))
16
+ throw new Error(`${DEEPSEEK_HARNESS_PATCH_ENV} must be an absolute path`);
17
+ if (configPath && patchPath)
18
+ throw new Error("DeepSeek Harness legacy config and profile patch are mutually exclusive");
19
+ return configPath
20
+ ? { kind: "legacy", bin: "dsh-acp-demo", configPath }
21
+ : { kind: "profile", bin: "dsh", ...(patchPath ? { patchPath } : {}) };
22
+ }
5
23
  export function deepSeekHarnessConfigPath(env = process.env) {
6
24
  const path = env[DEEPSEEK_HARNESS_CONFIG_ENV]?.trim();
7
25
  return path && isAbsolute(path) ? path : null;
8
26
  }
9
27
  /** The published DeepSeek Harness ACP executable speaks JSON-RPC directly on stdio. */
10
- export function probeDeepSeekHarnessAcp(options, spawnProcess = spawn) {
11
- const configPath = options.configPath ?? deepSeekHarnessConfigPath();
12
- if (!configPath || !isAbsolute(configPath))
13
- return Promise.resolve(false);
14
- return probeKimiAcp({
15
- ...options,
16
- provider: "deepseek-harness",
17
- configPath,
18
- effectivePermission: "sandboxed",
19
- }, spawnProcess);
28
+ export async function probeDeepSeekHarnessAcp(options, spawnProcess = spawn) {
29
+ if (options.signal?.aborted)
30
+ return false;
31
+ let root;
32
+ try {
33
+ const launcher = options.configPath === undefined
34
+ ? deepSeekHarnessLauncher()
35
+ : deepSeekHarnessLauncher({
36
+ [DEEPSEEK_HARNESS_CONFIG_ENV]: options.configPath,
37
+ });
38
+ if (launcher.kind === "legacy")
39
+ return await probeKimiAcp({
40
+ ...options,
41
+ provider: "deepseek-harness",
42
+ configPath: launcher.configPath,
43
+ effectivePermission: "sandboxed",
44
+ }, spawnProcess);
45
+ root = await mkdtemp(join(tmpdir(), "nowwork-dsh-probe-"));
46
+ const profile = await prepareHarnessProfile({
47
+ stateRoot: root,
48
+ cwd: root,
49
+ systemPrompt: "",
50
+ skillRoots: [],
51
+ effectivePermission: "sandboxed",
52
+ env: process.env,
53
+ ...(launcher.patchPath ? { patchPath: launcher.patchPath } : {}),
54
+ });
55
+ return await probeKimiAcp({
56
+ ...options,
57
+ provider: "deepseek-harness",
58
+ effectivePermission: "sandboxed",
59
+ launchArgs: profile.args,
60
+ launchEnv: profile.env,
61
+ launchCwd: root,
62
+ }, spawnProcess);
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ finally {
68
+ if (root)
69
+ await rm(root, { recursive: true, force: true }).catch(() => undefined);
70
+ }
20
71
  }
@@ -198,10 +198,10 @@ async function stopChild(child) {
198
198
  export async function probeKimiAcp(options, spawnProcess = spawn) {
199
199
  const provider = options.provider ?? "kimi";
200
200
  const permission = options.effectivePermission ?? "sandboxed";
201
- const child = spawnProcess(options.bin, providerArgs(provider, options.configPath), {
202
- cwd: process.cwd(),
201
+ const child = spawnProcess(options.bin, options.launchArgs ?? providerArgs(provider, options.configPath), {
202
+ cwd: options.launchCwd ?? process.cwd(),
203
203
  // probe 在 daemon 自身 PATH 下运行,补上用户级 CLI 目录,与 which 探测保持一致。
204
- env: { ...providerEnv(provider, permission), PATH: augmentedPath() },
204
+ env: { ...(options.launchEnv ?? providerEnv(provider, permission)), PATH: augmentedPath() },
205
205
  stdio: ["pipe", "pipe", "pipe"],
206
206
  });
207
207
  if (child.stdin === null || child.stdout === null || child.stderr === null)
@@ -217,12 +217,12 @@ export async function probeKimiAcp(options, spawnProcess = spawn) {
217
217
  try {
218
218
  const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
219
219
  const connected = app.connectWith(stream, async (context) => {
220
- await context.request(methods.agent.initialize, {
220
+ const initialized = await context.request(methods.agent.initialize, {
221
221
  protocolVersion: PROTOCOL_VERSION,
222
222
  clientCapabilities: {},
223
223
  clientInfo: { name: "nowcrew-daemon", version: "1" },
224
224
  });
225
- return true;
225
+ return initialized.protocolVersion === PROTOCOL_VERSION;
226
226
  });
227
227
  const result = await Promise.race([
228
228
  connected,
package/dist/serve.js CHANGED
@@ -51,10 +51,9 @@ import { parseMemoryPruneTraceId } from "./memory-prune-diagnostics.js";
51
51
  import { probeRuntimeHealth } from "./runtime-health.js";
52
52
  import { handleRuntimeHealthProbeFrame } from "./runtime-health-handler.js";
53
53
  import { buildControlPlaneUrl } from "./control-plane-url.js";
54
- import { createServeMigrationController, handleMigrationControlMessage } from "./daemon-migration-wiring.js";
55
54
  import { createCodexHomeModeController } from "./codex-home-mode-controller.js";
56
55
  import { resolveDaemonLocalStateRootFromEnv } from "./daemon-local-home.js";
57
- import { attachStorageMigrationRuntime, bindStorageMigrationServerIdentity, createServeStorageStatusDelivery, handleStorageMigrationControl, replayStorageMigrationStatus, storageMigrationCapabilities, } from "./storage-serve-adapter.js";
56
+ import { bindStorageServerIdentity, createStorageIdentityAdmission, } from "./storage-serve-adapter.js";
58
57
  export { buildControlPlaneUrl } from "./control-plane-url.js";
59
58
  // normalize.ts 的活动种类 → activity 枚举
60
59
  const ACTIVITY_MAP = {
@@ -127,17 +126,7 @@ export function serve(config, opts = {}) {
127
126
  catch { /* reconnect/timeout reconciliation handles a lost status frame */ }
128
127
  },
129
128
  });
130
- let storageMigrationAdmissionClosed = () => false;
131
- const migrationController = createServeMigrationController({
132
- config,
133
- ...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
134
- isBusy: () => executionRuns.size > 0 || legacyRuns.size > 0,
135
- sendStatus: (frame) => { try {
136
- if (ws?.readyState === WebSocket.OPEN)
137
- ws.send(JSON.stringify(frame));
138
- }
139
- catch { /* reconnect */ } },
140
- });
129
+ const storageIdentityAdmission = createStorageIdentityAdmission(opts.storageRuntime);
141
130
  const codexHomeModeController = createCodexHomeModeController({ ...(opts.profileName === undefined ? {} : { profileName: opts.profileName }), scheduleRestart: opts.update?.scheduleRestart ?? scheduleServiceRestart, getServiceSpec: async () => { const eligibility = await updateEligibility(); return eligibility.eligible ? eligibility.serviceSpec : null; }, isBusy: () => executionRuns.size > 0 || legacyRuns.size > 0, sendStatus: (frame) => { try {
142
131
  if (ws?.readyState === WebSocket.OPEN)
143
132
  ws.send(JSON.stringify(frame));
@@ -148,7 +137,7 @@ export function serve(config, opts = {}) {
148
137
  const agentAbilityRuntime = createAgentAbilityRuntime(config.agentsRoot, {
149
138
  ...opts.agentAbility,
150
139
  coordinator: projectionCoordinator,
151
- isAdmissionClosed: () => storageMigrationAdmissionClosed(),
140
+ isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
152
141
  });
153
142
  let projectSkillsController;
154
143
  const projectSkillsReconciler = opts.projectSkills?.reconciler ?? createProjectSkillsReconciler({
@@ -169,7 +158,7 @@ export function serve(config, opts = {}) {
169
158
  }
170
159
  catch { /* 下一次 ready 或项目操作会重新发送完整快照 */ }
171
160
  },
172
- isAdmissionClosed: () => storageMigrationAdmissionClosed(),
161
+ isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
173
162
  reconcile: (handle, bindings) => projectSkillsReconciler.reconcile(handle, bindings),
174
163
  });
175
164
  const logRegistryFailure = (error) => dslog("project_skills.registry_failed", "本机项目注册表读取失败,本次不上报项目清单(能力不受影响)", {
@@ -177,7 +166,7 @@ export function serve(config, opts = {}) {
177
166
  });
178
167
  const machineHelloPublisher = createMachineHelloPublisher({
179
168
  effective: (hello) => {
180
- const configuredIdentity = opts.storageMigration?.identity;
169
+ const configuredIdentity = opts.storageRuntime?.identity;
181
170
  const currentIdentity = typeof configuredIdentity === "function"
182
171
  ? configuredIdentity()
183
172
  : configuredIdentity;
@@ -187,23 +176,7 @@ export function serve(config, opts = {}) {
187
176
  });
188
177
  const initializedProjectSkills = initializedProjectSkillsReconciler(() => projectSkillsController.ensureScanned(), projectSkillsReconciler);
189
178
  const configuredCapabilities = opts.machineInfo?.capabilities ?? daemonCapabilities(process.platform);
190
- const storageCapabilities = storageMigrationCapabilities(configuredCapabilities, opts.storageMigration);
191
- const storageStatusDelivery = createServeStorageStatusDelivery(opts.storageMigration, {
192
- onPersistenceError: (error) => dslog("daemon.storage_status_outbox_failed", "存储迁移状态 outbox 持久化失败", { level: "ERROR", error_message: error.message }),
193
- transmit: (frame) => { try {
194
- if (ws?.readyState === WebSocket.OPEN)
195
- ws.send(JSON.stringify(frame));
196
- }
197
- catch { /* retry */ } },
198
- });
199
- const storageMigrationRuntime = attachStorageMigrationRuntime(opts.storageMigration, {
200
- isBusy: () => executionRuns.size > 0 || legacyRuns.size > 0,
201
- waitForExecutionFrames: () => executionFrameQueue,
202
- flushLocalState: flushSlog,
203
- sendStatus: storageStatusDelivery.send,
204
- });
205
- storageMigrationAdmissionClosed = storageMigrationRuntime.isAdmissionClosed;
206
- const prepareCapabilities = async () => daemonCapabilityBindings(process.platform, [...storageCapabilities, ...await managedDaemonCapabilities(updateEligibility)]);
179
+ const prepareCapabilities = async () => daemonCapabilityBindings(process.platform, [...configuredCapabilities, ...await managedDaemonCapabilities(updateEligibility)]);
207
180
  const safeExecutionSend = (frame) => {
208
181
  try {
209
182
  if (ws?.readyState !== WebSocket.OPEN)
@@ -316,7 +289,7 @@ export function serve(config, opts = {}) {
316
289
  let connectionEpochToken;
317
290
  ws.on("open", () => {
318
291
  connectionEpochToken = projectSkillsController.beginConnectionEpoch();
319
- storageMigrationRuntime.beginIdentityBinding();
292
+ storageIdentityAdmission.beginIdentityBinding();
320
293
  const openedSocket = ws;
321
294
  backoff = 1000;
322
295
  connectedAt = Date.now();
@@ -325,12 +298,6 @@ export function serve(config, opts = {}) {
325
298
  // 连上了才有机会把离线期间(断连原因/退出前)落盘的日志补传上去
326
299
  void drainSpool();
327
300
  // 上报本机信息 (hostname/os/daemon 版本/已装 runtimes)
328
- storageStatusDelivery.replayPending();
329
- const storageReplay = replayStorageMigrationStatus(opts.storageMigration, openedSocket, () => ws === openedSocket, (error) => {
330
- dslog("daemon.storage_status_replay_failed", "存储迁移状态重放失败", {
331
- level: "ERROR", error_message: error.message,
332
- });
333
- }, storageStatusDelivery.send);
334
301
  const { detectInstalled, detectExecutable = detectExecutionRuntimesWithSignal } = opts.machineInfo ?? {};
335
302
  const helloPromise = (opts.machineInfo?.collect ?? collectMachineHello)(config.agentsRoot, config.executionLimits, process.platform, {
336
303
  ...(detectInstalled ? { detectInstalled } : {}),
@@ -349,7 +316,6 @@ export function serve(config, opts = {}) {
349
316
  }
350
317
  const detected = await runtimeProbe.detect(hello.runtimes, detectExecutable);
351
318
  executionRuntimesKnown = true;
352
- await storageReplay;
353
319
  if (ws === openedSocket && openedSocket.readyState === WebSocket.OPEN
354
320
  && detected.some((runtime) => !hello.executionRuntimes.includes(runtime))) {
355
321
  detectedExecutionRuntimes = detected;
@@ -366,7 +332,6 @@ export function serve(config, opts = {}) {
366
332
  });
367
333
  void helloPromise
368
334
  .then(async (hello) => {
369
- await storageReplay;
370
335
  if (ws !== openedSocket || openedSocket.readyState !== WebSocket.OPEN)
371
336
  return;
372
337
  detectedExecutionRuntimes = hello.executionRuntimes;
@@ -414,8 +379,6 @@ export function serve(config, opts = {}) {
414
379
  if (typeof decoded !== "object" || decoded === null)
415
380
  return;
416
381
  const rawType = "type" in decoded && typeof decoded.type === "string" ? decoded.type : "";
417
- if (storageStatusDelivery.acknowledge(decoded))
418
- return;
419
382
  if (rawType === "daemon:update" || rawType === "daemon:restart") {
420
383
  void updateController.handle(decoded).catch((error) => {
421
384
  dslog("daemon.control_failed", "daemon 控制操作处理失败", {
@@ -424,19 +387,10 @@ export function serve(config, opts = {}) {
424
387
  });
425
388
  return;
426
389
  }
427
- if (rawType === "daemon:migrate") {
428
- void handleMigrationControlMessage(migrationController, decoded).catch((error) => dslog("daemon.migration_failed", "daemon 布局迁移失败", { level: "ERROR", error_message: error.message }));
429
- return;
430
- }
431
390
  if (rawType === "daemon:codex-home-mode") {
432
391
  void codexHomeModeController.handle(decoded).catch((error) => dslog("daemon.codex_home_mode_failed", "Codex Home 模式切换失败", { level: "ERROR", error_message: error.message }));
433
392
  return;
434
393
  }
435
- if (handleStorageMigrationControl(opts.storageMigration, rawType, decoded, storageStatusDelivery.send, (kind, error) => {
436
- dslog(kind === "migration" ? "daemon.storage_migration_failed" : "daemon.storage_migration_confirmation_failed", kind === "migration" ? "存储迁移控制操作失败" : "存储迁移 hello 确认处理失败", { level: "ERROR", error_message: error.message });
437
- })) {
438
- return;
439
- }
440
394
  if (rawType.startsWith("execution:")) {
441
395
  const parsedExecution = ServerToDaemonExecutionFrameSchema.safeParse(decoded);
442
396
  if (!parsedExecution.success) {
@@ -498,10 +452,10 @@ export function serve(config, opts = {}) {
498
452
  return;
499
453
  }
500
454
  const spec = frame;
501
- if (storageMigrationRuntime.isAdmissionClosed()) {
455
+ if (storageIdentityAdmission.isAdmissionClosed()) {
502
456
  safeExecutionSend(ExecutionRejectedSchema.parse({
503
457
  type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
504
- reason: "resource_limit", message: "Storage migration is quiescing this machine",
458
+ reason: "resource_limit", message: "Storage identity binding is not complete",
505
459
  at: new Date().toISOString(),
506
460
  }));
507
461
  return;
@@ -812,17 +766,17 @@ export function serve(config, opts = {}) {
812
766
  // ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
813
767
  const r = msg;
814
768
  setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
815
- if (opts.storageMigration !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
769
+ if (opts.storageRuntime !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
816
770
  const bindingSocket = ws;
817
- bindStorageMigrationServerIdentity(opts.storageMigration, { workspaceId: r.workspaceId, machineId: r.machineId }, {
771
+ bindStorageServerIdentity(opts.storageRuntime, { workspaceId: r.workspaceId, machineId: r.machineId }, {
818
772
  onBound: () => {
819
773
  if (bindingSocket !== ws || bindingSocket?.readyState !== WebSocket.OPEN)
820
774
  return;
821
- storageMigrationRuntime.completeIdentityBinding();
775
+ storageIdentityAdmission.completeIdentityBinding();
822
776
  machineHelloPublisher.republish();
823
777
  },
824
778
  onError: (error) => {
825
- dslog("daemon.storage_identity_bind_failed", "存储迁移 Workspace 身份绑定失败", { level: "ERROR", error_message: error.message });
779
+ dslog("daemon.storage_identity_bind_failed", "存储 Workspace 身份绑定失败", { level: "ERROR", error_message: error.message });
826
780
  if (bindingSocket === ws && bindingSocket?.readyState === WebSocket.OPEN)
827
781
  bindingSocket.close(1011, "storage identity binding failed");
828
782
  },
@@ -925,7 +879,7 @@ export function serve(config, opts = {}) {
925
879
  }
926
880
  if (msg.type !== "agent:start")
927
881
  return; // ready/error 等忽略
928
- if (storageMigrationRuntime.isAdmissionClosed())
882
+ if (storageIdentityAdmission.isAdmissionClosed())
929
883
  return;
930
884
  // 任务键:scheduled run 用 runId(每次运行独立 cwd/work-log;overlap 由 server 端 skip_if_running 管,
931
885
  // daemon 的 running 去重只兜底"同一 run 重复投递");普通唤醒仍是 线程锚点 ?? 频道。
@@ -1357,7 +1311,6 @@ export function serve(config, opts = {}) {
1357
1311
  concurrencyTelemetry.stop();
1358
1312
  runtimeProbe.stop();
1359
1313
  completionRetransmitter.stop();
1360
- storageStatusDelivery.stop();
1361
1314
  const deadline = createShutdownDeadline(shutdownTimeoutMs);
1362
1315
  if (reconnectTimer !== null) {
1363
1316
  clearTimeout(reconnectTimer);
@@ -1367,7 +1320,6 @@ export function serve(config, opts = {}) {
1367
1320
  const pending = [
1368
1321
  webSocketClosed,
1369
1322
  updateController.drain(),
1370
- ...(migrationController === null ? [] : [migrationController.drain()]),
1371
1323
  codexHomeModeController.drain(),
1372
1324
  executionFrameQueue,
1373
1325
  ...executionRuns.values(),
@@ -39,8 +39,5 @@ export function storageHelloIdentity(input) {
39
39
  daemonRootFingerprint: fingerprint("daemon-root", layout.daemonRoot),
40
40
  agentRootFingerprint: fingerprint("agent-root", layout.agentItemsRoot),
41
41
  ...(ownership === undefined ? {} : { storageOwnership: ownership }),
42
- ...(installation.storageMigrationId === undefined
43
- ? {}
44
- : { storageMigrationId: installation.storageMigrationId }),
45
42
  });
46
43
  }
@@ -1,55 +1,12 @@
1
- import { STORAGE_MIGRATION_CAPABILITY, STORAGE_MIGRATION_STATUS_CAPABILITY, } from "./storage-control.js";
2
- import { createStorageStatusDelivery } from "./storage-status-delivery.js";
3
- export function storageMigrationCapabilities(configured, options) {
4
- if (options === undefined)
5
- return configured;
6
- return Object.freeze([...new Set([
7
- ...configured,
8
- STORAGE_MIGRATION_STATUS_CAPABILITY,
9
- ...(options.ready ? [STORAGE_MIGRATION_CAPABILITY] : []),
10
- ])]);
1
+ export function bindStorageServerIdentity(options, identity, callbacks) {
2
+ void Promise.resolve().then(() => options.bindServerIdentity?.(identity))
3
+ .then(callbacks.onBound).catch(callbacks.onError);
11
4
  }
12
- export function bindStorageMigrationServerIdentity(options, identity, callbacks) {
13
- const bind = options?.bindServerIdentity;
14
- if (bind === undefined) {
15
- if (options?.ready === true)
16
- callbacks.onBound();
17
- return;
18
- }
19
- void Promise.resolve().then(() => bind(identity)).then(callbacks.onBound).catch(callbacks.onError);
20
- }
21
- export function createServeStorageStatusDelivery(options, dependencies) {
22
- return createStorageStatusDelivery({
23
- ...(options?.statusOutboxPath === undefined ? {} : { outboxPath: options.statusOutboxPath }),
24
- ...(options?.statusRetryMs === undefined ? {} : { retryMs: options.statusRetryMs }),
25
- ...dependencies,
26
- });
27
- }
28
- export function attachStorageMigrationRuntime(options, dependencies) {
29
- let migrationAdmissionClosed = false;
5
+ /** 新连接必须重新验证 Server 身份,不能沿用上一次连接的执行准入。 */
6
+ export function createStorageIdentityAdmission(options) {
30
7
  let identityAdmissionClosed = options?.bindServerIdentity !== undefined;
31
- if (options?.ready === true) {
32
- options.attach?.({
33
- gate: {
34
- isBusy: dependencies.isBusy,
35
- closeAndFlush: async () => {
36
- migrationAdmissionClosed = true;
37
- await dependencies.waitForExecutionFrames();
38
- if (dependencies.isBusy()) {
39
- migrationAdmissionClosed = false;
40
- throw Object.assign(new Error("active execution entered before storage migration quiesce"), {
41
- code: "runtime_busy",
42
- });
43
- }
44
- await dependencies.flushLocalState();
45
- },
46
- reopen: () => { migrationAdmissionClosed = false; },
47
- },
48
- sendStatus: dependencies.sendStatus,
49
- });
50
- }
51
8
  return Object.freeze({
52
- isAdmissionClosed: () => migrationAdmissionClosed || identityAdmissionClosed,
9
+ isAdmissionClosed: () => identityAdmissionClosed,
53
10
  beginIdentityBinding: () => {
54
11
  if (options?.bindServerIdentity !== undefined)
55
12
  identityAdmissionClosed = true;
@@ -57,26 +14,3 @@ export function attachStorageMigrationRuntime(options, dependencies) {
57
14
  completeIdentityBinding: () => { identityAdmissionClosed = false; },
58
15
  });
59
16
  }
60
- export function replayStorageMigrationStatus(options, socket, isCurrent, onError, deliver) {
61
- return options?.replayStatus?.((frame) => {
62
- if (!isCurrent())
63
- return;
64
- if (deliver !== undefined)
65
- deliver(frame);
66
- else if (socket.readyState === socket.OPEN)
67
- socket.send(JSON.stringify(frame));
68
- }).catch(onError) ?? Promise.resolve();
69
- }
70
- export function handleStorageMigrationControl(options, rawType, raw, sendStatus, onError) {
71
- if (rawType === "daemon:storage-doctor" || rawType === "daemon:storage-migrate") {
72
- if (options?.ready === true)
73
- void options.controller.handle(raw).catch((error) => onError("migration", error));
74
- return true;
75
- }
76
- if (rawType !== "daemon:storage-migration-confirmed")
77
- return false;
78
- const confirm = options?.ready === false ? options.confirm : undefined;
79
- if (confirm !== undefined)
80
- void confirm(raw, sendStatus).catch((error) => onError("confirmation", error));
81
- return true;
82
- }
@@ -1,7 +1,7 @@
1
1
  import { fileURLToPath } from "node:url";
2
2
  import { startDormantSupervisor, } from "./execution-supervisor.js";
3
3
  import { buildClaudeLaunch } from "./runtimes/claude.js";
4
- import { DEEPSEEK_HARNESS_CONFIG_ENV, deepSeekHarnessConfigPath, } from "./runtimes/deepseek-harness.js";
4
+ import { DEEPSEEK_HARNESS_CONFIG_ENV, deepSeekHarnessConfigPath, deepSeekHarnessLauncher, } from "./runtimes/deepseek-harness.js";
5
5
  import { RuntimeCancelledError } from "./runtime-cancellation.js";
6
6
  export function supervisorLaunch(request) {
7
7
  const common = {
@@ -82,7 +82,22 @@ export function supervisorLaunch(request) {
82
82
  if (request.runtime === "deepseek-harness") {
83
83
  if (request.resume)
84
84
  throw new Error("DeepSeek Harness ACP does not support session resume");
85
- const configPath = deepSeekHarnessConfigPath();
85
+ const launcher = deepSeekHarnessLauncher(request.env);
86
+ if (launcher.kind === "profile") {
87
+ return {
88
+ command: process.execPath,
89
+ args: [fileURLToPath(new URL("./runtimes/deepseek-harness-runner.js", import.meta.url)),
90
+ "--bin", request.bin, ...(launcher.patchPath ? ["--patch", launcher.patchPath] : [])],
91
+ cwd: request.cwd, env: request.env,
92
+ stdinText: JSON.stringify({ systemPrompt: request.systemPrompt, wakePrompt: request.wakePrompt,
93
+ stateRoot: request.harnessStateRoot ?? request.cwd, skillRoots: request.harnessSkillRoots ?? [],
94
+ effectivePermission: request.effectivePermission }),
95
+ };
96
+ }
97
+ if ((request.harnessSkillRoots?.length ?? 0) > 0) {
98
+ throw new Error("DeepSeek Harness bound Skills require the modern dsh ACP profile");
99
+ }
100
+ const configPath = deepSeekHarnessConfigPath(request.env);
86
101
  if (configPath === null) {
87
102
  throw new Error(`${DEEPSEEK_HARNESS_CONFIG_ENV} must be an absolute path`);
88
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.88-beta.14",
3
+ "version": "0.6.88-beta.16",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",