@nowcrew/daemon 0.6.88-beta.2 → 0.6.88-beta.20
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/README.md +38 -10
- package/dist/agent-memory/policy.js +3 -1
- package/dist/daemon-update-eligibility.js +1 -1
- package/dist/execution-journal.js +9 -6
- package/dist/execution-protocol.js +2 -1
- package/dist/execution-runner.js +5 -3
- package/dist/list-models.js +2 -0
- package/dist/local-execution-lease.js +22 -0
- package/dist/local-executor.js +57 -37
- package/dist/local-memory/candidate.js +43 -0
- package/dist/local-memory/index-budget.js +8 -0
- package/dist/local-memory/inventory-cli.js +61 -0
- package/dist/local-memory/inventory-preview.js +154 -0
- package/dist/local-memory/inventory-source.js +159 -0
- package/dist/local-memory/pending-match.js +29 -0
- package/dist/local-memory/pending-query.js +100 -0
- package/dist/local-memory/pending-schema.js +29 -0
- package/dist/local-memory/pending-source.js +126 -0
- package/dist/local-memory/pending.js +34 -84
- package/dist/local-memory/prompt.js +14 -11
- package/dist/local-memory/read-cli.js +59 -8
- package/dist/local-memory/runtime.js +24 -17
- package/dist/local-memory/work-log-execution.js +31 -0
- package/dist/local-memory/work-log.js +58 -0
- package/dist/local-memory/write.js +27 -58
- package/dist/machine-info.js +54 -16
- package/dist/main.js +4 -69
- package/dist/native-thread-context.js +59 -0
- package/dist/pre-admission-rejection.js +64 -0
- package/dist/project-skills/execution-adapter.js +3 -2
- package/dist/project-skills/runtime-launch.js +14 -0
- package/dist/prompt.js +15 -15
- package/dist/provider-env.js +3 -4
- package/dist/runner.js +14 -15
- package/dist/runtime-probe.js +34 -5
- package/dist/runtimes/acp-probe-transport.js +69 -0
- package/dist/runtimes/claude-session-start-hook.js +24 -0
- package/dist/runtimes/claude-thread-runner.js +266 -0
- package/dist/runtimes/claude.js +23 -4
- package/dist/runtimes/codex-app-server-runner.js +43 -4
- package/dist/runtimes/deepseek-harness-profile.js +92 -0
- package/dist/runtimes/deepseek-harness-runner.js +224 -0
- package/dist/runtimes/deepseek-harness.js +64 -11
- package/dist/runtimes/harness-probe-retry.js +42 -0
- package/dist/runtimes/kimi-acp-runner.js +63 -10
- package/dist/runtimes/opencode-runner.js +7 -1
- package/dist/serve.js +58 -88
- package/dist/session.js +4 -0
- package/dist/storage-identity.js +0 -3
- package/dist/storage-serve-adapter.js +6 -72
- package/dist/supervised-runtime.js +28 -18
- package/dist/thread-input-delivery.js +217 -0
- package/package.json +2 -2
- package/dist/daemon-migration-controller.js +0 -211
- package/dist/daemon-migration-wiring.js +0 -22
- package/dist/storage-control.js +0 -212
- package/dist/storage-copy.js +0 -628
- package/dist/storage-credential-guard.js +0 -21
- package/dist/storage-doctor.js +0 -228
- package/dist/storage-manifest.js +0 -1027
- package/dist/storage-migration-controller.js +0 -440
- package/dist/storage-migration-manifest.js +0 -105
- package/dist/storage-migration-policy.js +0 -100
- package/dist/storage-runtime-controller.js +0 -381
- package/dist/storage-runtime-readiness.js +0 -240
- package/dist/storage-safe-fs.js +0 -302
- package/dist/storage-status-delivery.js +0 -224
- package/dist/storage-status-replay.js +0 -140
- package/dist/storage-target-confirmation.js +0 -129
- package/dist/storage-worker-command.js +0 -37
- package/dist/storage-worker-executor.js +0 -449
- package/dist/storage-worker-handoff.js +0 -151
- package/dist/storage-worker-operation.js +0 -294
- package/dist/storage-worker-status.js +0 -259
package/dist/serve.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* 断线指数退避重连。一个 agent:start 进来就跑一次现有的 runAgent。
|
|
4
4
|
*/
|
|
5
5
|
import { WebSocket } from "ws";
|
|
6
|
+
import { persistPreAdmissionRejection, rejectQueuedAdmission } from "./pre-admission-rejection.js";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
8
9
|
import { dslog, setSlogDefaults, drainSpool, flushSlog } from "./slog.js";
|
|
@@ -51,10 +52,9 @@ import { parseMemoryPruneTraceId } from "./memory-prune-diagnostics.js";
|
|
|
51
52
|
import { probeRuntimeHealth } from "./runtime-health.js";
|
|
52
53
|
import { handleRuntimeHealthProbeFrame } from "./runtime-health-handler.js";
|
|
53
54
|
import { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
54
|
-
import { createServeMigrationController, handleMigrationControlMessage } from "./daemon-migration-wiring.js";
|
|
55
55
|
import { createCodexHomeModeController } from "./codex-home-mode-controller.js";
|
|
56
56
|
import { resolveDaemonLocalStateRootFromEnv } from "./daemon-local-home.js";
|
|
57
|
-
import {
|
|
57
|
+
import { bindStorageServerIdentity, createStorageIdentityAdmission, } from "./storage-serve-adapter.js";
|
|
58
58
|
export { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
59
59
|
// normalize.ts 的活动种类 → activity 枚举
|
|
60
60
|
const ACTIVITY_MAP = {
|
|
@@ -127,17 +127,7 @@ export function serve(config, opts = {}) {
|
|
|
127
127
|
catch { /* reconnect/timeout reconciliation handles a lost status frame */ }
|
|
128
128
|
},
|
|
129
129
|
});
|
|
130
|
-
|
|
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
|
-
});
|
|
130
|
+
const storageIdentityAdmission = createStorageIdentityAdmission(opts.storageRuntime);
|
|
141
131
|
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
132
|
if (ws?.readyState === WebSocket.OPEN)
|
|
143
133
|
ws.send(JSON.stringify(frame));
|
|
@@ -148,7 +138,7 @@ export function serve(config, opts = {}) {
|
|
|
148
138
|
const agentAbilityRuntime = createAgentAbilityRuntime(config.agentsRoot, {
|
|
149
139
|
...opts.agentAbility,
|
|
150
140
|
coordinator: projectionCoordinator,
|
|
151
|
-
isAdmissionClosed:
|
|
141
|
+
isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
|
|
152
142
|
});
|
|
153
143
|
let projectSkillsController;
|
|
154
144
|
const projectSkillsReconciler = opts.projectSkills?.reconciler ?? createProjectSkillsReconciler({
|
|
@@ -169,7 +159,7 @@ export function serve(config, opts = {}) {
|
|
|
169
159
|
}
|
|
170
160
|
catch { /* 下一次 ready 或项目操作会重新发送完整快照 */ }
|
|
171
161
|
},
|
|
172
|
-
isAdmissionClosed:
|
|
162
|
+
isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
|
|
173
163
|
reconcile: (handle, bindings) => projectSkillsReconciler.reconcile(handle, bindings),
|
|
174
164
|
});
|
|
175
165
|
const logRegistryFailure = (error) => dslog("project_skills.registry_failed", "本机项目注册表读取失败,本次不上报项目清单(能力不受影响)", {
|
|
@@ -177,7 +167,7 @@ export function serve(config, opts = {}) {
|
|
|
177
167
|
});
|
|
178
168
|
const machineHelloPublisher = createMachineHelloPublisher({
|
|
179
169
|
effective: (hello) => {
|
|
180
|
-
const configuredIdentity = opts.
|
|
170
|
+
const configuredIdentity = opts.storageRuntime?.identity;
|
|
181
171
|
const currentIdentity = typeof configuredIdentity === "function"
|
|
182
172
|
? configuredIdentity()
|
|
183
173
|
: configuredIdentity;
|
|
@@ -187,23 +177,7 @@ export function serve(config, opts = {}) {
|
|
|
187
177
|
});
|
|
188
178
|
const initializedProjectSkills = initializedProjectSkillsReconciler(() => projectSkillsController.ensureScanned(), projectSkillsReconciler);
|
|
189
179
|
const configuredCapabilities = opts.machineInfo?.capabilities ?? daemonCapabilities(process.platform);
|
|
190
|
-
const
|
|
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)]);
|
|
180
|
+
const prepareCapabilities = async () => daemonCapabilityBindings(process.platform, [...configuredCapabilities, ...await managedDaemonCapabilities(updateEligibility)]);
|
|
207
181
|
const safeExecutionSend = (frame) => {
|
|
208
182
|
try {
|
|
209
183
|
if (ws?.readyState !== WebSocket.OPEN)
|
|
@@ -237,6 +211,11 @@ export function serve(config, opts = {}) {
|
|
|
237
211
|
return;
|
|
238
212
|
}
|
|
239
213
|
safeExecutionSend(frame);
|
|
214
|
+
if (frame.type === "execution:rejected" && frame.preAdmissionProof) {
|
|
215
|
+
const entry = await executionJournal.get(frame.executionId);
|
|
216
|
+
if (entry?.completion)
|
|
217
|
+
completionRetransmitter.track(entry.completion);
|
|
218
|
+
}
|
|
240
219
|
};
|
|
241
220
|
const replayExecutionTelemetry = async () => {
|
|
242
221
|
const frames = await executionTelemetry.replay();
|
|
@@ -316,21 +295,16 @@ export function serve(config, opts = {}) {
|
|
|
316
295
|
let connectionEpochToken;
|
|
317
296
|
ws.on("open", () => {
|
|
318
297
|
connectionEpochToken = projectSkillsController.beginConnectionEpoch();
|
|
319
|
-
|
|
298
|
+
storageIdentityAdmission.beginIdentityBinding();
|
|
320
299
|
const openedSocket = ws;
|
|
300
|
+
const connectionId = randomUUID();
|
|
321
301
|
backoff = 1000;
|
|
322
302
|
connectedAt = Date.now();
|
|
323
303
|
log(`🔌 已连接控制面 ${config.serverUrl}`);
|
|
324
|
-
dslog("daemon.ws_open", "已连接控制面", { server_url: config.serverUrl });
|
|
304
|
+
dslog("daemon.ws_open", "已连接控制面", { server_url: config.serverUrl, connection_id: connectionId });
|
|
325
305
|
// 连上了才有机会把离线期间(断连原因/退出前)落盘的日志补传上去
|
|
326
306
|
void drainSpool();
|
|
327
307
|
// 上报本机信息 (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
308
|
const { detectInstalled, detectExecutable = detectExecutionRuntimesWithSignal } = opts.machineInfo ?? {};
|
|
335
309
|
const helloPromise = (opts.machineInfo?.collect ?? collectMachineHello)(config.agentsRoot, config.executionLimits, process.platform, {
|
|
336
310
|
...(detectInstalled ? { detectInstalled } : {}),
|
|
@@ -345,28 +319,41 @@ export function serve(config, opts = {}) {
|
|
|
345
319
|
.then(async (hello) => {
|
|
346
320
|
if (hello.executionProtocol === undefined) {
|
|
347
321
|
executionRuntimesKnown = true;
|
|
322
|
+
dslog("runtime_probe.hello_refresh", "runtime hello 刷新决策", {
|
|
323
|
+
connection_id: connectionId, reason: "protocol_unavailable",
|
|
324
|
+
current_socket: ws === openedSocket, socket_open: openedSocket.readyState === WebSocket.OPEN,
|
|
325
|
+
socket_ready_state: openedSocket.readyState, installed_runtimes: JSON.stringify(hello.runtimes),
|
|
326
|
+
execution_runtimes: JSON.stringify(hello.executionRuntimes),
|
|
327
|
+
});
|
|
348
328
|
return [];
|
|
349
329
|
}
|
|
350
|
-
const detected = await runtimeProbe.detect(hello.runtimes, detectExecutable);
|
|
330
|
+
const detected = await runtimeProbe.detect(hello.runtimes, detectExecutable, connectionId);
|
|
351
331
|
executionRuntimesKnown = true;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
332
|
+
const currentSocket = ws === openedSocket;
|
|
333
|
+
const socketOpen = openedSocket.readyState === WebSocket.OPEN;
|
|
334
|
+
const hasNewRuntime = detected.some((runtime) => !hello.executionRuntimes.includes(runtime));
|
|
335
|
+
dslog("runtime_probe.hello_refresh", "runtime hello 刷新决策", {
|
|
336
|
+
connection_id: connectionId, current_socket: currentSocket, socket_open: socketOpen,
|
|
337
|
+
socket_ready_state: openedSocket.readyState, installed_runtimes: JSON.stringify(hello.runtimes),
|
|
338
|
+
initial_execution_runtimes: JSON.stringify(hello.executionRuntimes), execution_runtimes: JSON.stringify(detected),
|
|
339
|
+
reason: !currentSocket ? "stale_connection" : !socketOpen ? "socket_not_open" : !hasNewRuntime ? "unchanged" : "publish",
|
|
340
|
+
});
|
|
341
|
+
if (currentSocket && socketOpen && hasNewRuntime) {
|
|
355
342
|
detectedExecutionRuntimes = detected;
|
|
356
343
|
machineHelloPublisher.publish({ ...hello, executionRuntimes: [...detected] }, openedSocket);
|
|
357
344
|
}
|
|
358
345
|
return detected;
|
|
359
346
|
})
|
|
360
|
-
.catch((
|
|
347
|
+
.catch(() => {
|
|
361
348
|
executionRuntimesKnown = true;
|
|
362
349
|
dslog("execution.runtime_detection_failed", "runtime 探测失败", {
|
|
363
|
-
level: "ERROR",
|
|
350
|
+
level: "ERROR", reason: "detection_threw", connection_id: connectionId,
|
|
351
|
+
current_socket: ws === openedSocket, socket_open: openedSocket.readyState === WebSocket.OPEN,
|
|
364
352
|
});
|
|
365
353
|
return [];
|
|
366
354
|
});
|
|
367
355
|
void helloPromise
|
|
368
356
|
.then(async (hello) => {
|
|
369
|
-
await storageReplay;
|
|
370
357
|
if (ws !== openedSocket || openedSocket.readyState !== WebSocket.OPEN)
|
|
371
358
|
return;
|
|
372
359
|
detectedExecutionRuntimes = hello.executionRuntimes;
|
|
@@ -414,8 +401,6 @@ export function serve(config, opts = {}) {
|
|
|
414
401
|
if (typeof decoded !== "object" || decoded === null)
|
|
415
402
|
return;
|
|
416
403
|
const rawType = "type" in decoded && typeof decoded.type === "string" ? decoded.type : "";
|
|
417
|
-
if (storageStatusDelivery.acknowledge(decoded))
|
|
418
|
-
return;
|
|
419
404
|
if (rawType === "daemon:update" || rawType === "daemon:restart") {
|
|
420
405
|
void updateController.handle(decoded).catch((error) => {
|
|
421
406
|
dslog("daemon.control_failed", "daemon 控制操作处理失败", {
|
|
@@ -424,19 +409,10 @@ export function serve(config, opts = {}) {
|
|
|
424
409
|
});
|
|
425
410
|
return;
|
|
426
411
|
}
|
|
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
412
|
if (rawType === "daemon:codex-home-mode") {
|
|
432
413
|
void codexHomeModeController.handle(decoded).catch((error) => dslog("daemon.codex_home_mode_failed", "Codex Home 模式切换失败", { level: "ERROR", error_message: error.message }));
|
|
433
414
|
return;
|
|
434
415
|
}
|
|
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
416
|
if (rawType.startsWith("execution:")) {
|
|
441
417
|
const parsedExecution = ServerToDaemonExecutionFrameSchema.safeParse(decoded);
|
|
442
418
|
if (!parsedExecution.success) {
|
|
@@ -498,10 +474,10 @@ export function serve(config, opts = {}) {
|
|
|
498
474
|
return;
|
|
499
475
|
}
|
|
500
476
|
const spec = frame;
|
|
501
|
-
if (
|
|
477
|
+
if (storageIdentityAdmission.isAdmissionClosed()) {
|
|
502
478
|
safeExecutionSend(ExecutionRejectedSchema.parse({
|
|
503
479
|
type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
|
|
504
|
-
reason: "resource_limit", message: "Storage
|
|
480
|
+
reason: "resource_limit", message: "Storage identity binding is not complete",
|
|
505
481
|
at: new Date().toISOString(),
|
|
506
482
|
}));
|
|
507
483
|
return;
|
|
@@ -560,18 +536,8 @@ export function serve(config, opts = {}) {
|
|
|
560
536
|
: spec.context.scheduledRunId === undefined ? "normal" : "scheduled";
|
|
561
537
|
let reservation = sharedSlots.reserve(spec.agent.handle, "execution", executionClass);
|
|
562
538
|
if (!reservation.accepted) {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
execution_id: spec.executionId,
|
|
566
|
-
agent_handle: spec.agent.handle,
|
|
567
|
-
execution_class: executionClass,
|
|
568
|
-
...reservation.facts,
|
|
569
|
-
});
|
|
570
|
-
safeExecutionSend(ExecutionRejectedSchema.parse({
|
|
571
|
-
type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
|
|
572
|
-
reason: "resource_limit", message: "Local machine execution queue is full",
|
|
573
|
-
at: new Date().toISOString(),
|
|
574
|
-
}));
|
|
539
|
+
await rejectQueuedAdmission({ journal: executionJournal, knownExecutionHashes, executionRuns,
|
|
540
|
+
report: reportExecutionFrame, replay: sendJournalStatus }, spec, executionClass, reservation.facts);
|
|
575
541
|
return;
|
|
576
542
|
}
|
|
577
543
|
knownExecutionHashes.set(spec.executionId, hash);
|
|
@@ -741,6 +707,13 @@ export function serve(config, opts = {}) {
|
|
|
741
707
|
...reservation.facts,
|
|
742
708
|
},
|
|
743
709
|
report: reportExecutionFrame,
|
|
710
|
+
confirmPreAdmission: async (rejection) => {
|
|
711
|
+
const prior = await executionJournal.get(spec.executionId);
|
|
712
|
+
if (prior !== null || stopped || knownExecutionHashes.get(spec.executionId) !== hash
|
|
713
|
+
|| executionRuns.get(spec.executionId) !== execution)
|
|
714
|
+
return false;
|
|
715
|
+
return await persistPreAdmissionRejection(executionJournal, spec, rejection) !== null;
|
|
716
|
+
},
|
|
744
717
|
startupGate: runtimeStartupGate,
|
|
745
718
|
startupTimeoutMs: config.executionLimits.startupTimeoutMs,
|
|
746
719
|
onRuntimePhase: (phase, runtime) => {
|
|
@@ -812,17 +785,17 @@ export function serve(config, opts = {}) {
|
|
|
812
785
|
// ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
|
|
813
786
|
const r = msg;
|
|
814
787
|
setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
|
|
815
|
-
if (opts.
|
|
788
|
+
if (opts.storageRuntime !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
|
|
816
789
|
const bindingSocket = ws;
|
|
817
|
-
|
|
790
|
+
bindStorageServerIdentity(opts.storageRuntime, { workspaceId: r.workspaceId, machineId: r.machineId }, {
|
|
818
791
|
onBound: () => {
|
|
819
792
|
if (bindingSocket !== ws || bindingSocket?.readyState !== WebSocket.OPEN)
|
|
820
793
|
return;
|
|
821
|
-
|
|
794
|
+
storageIdentityAdmission.completeIdentityBinding();
|
|
822
795
|
machineHelloPublisher.republish();
|
|
823
796
|
},
|
|
824
797
|
onError: (error) => {
|
|
825
|
-
dslog("daemon.storage_identity_bind_failed", "
|
|
798
|
+
dslog("daemon.storage_identity_bind_failed", "存储 Workspace 身份绑定失败", { level: "ERROR", error_message: error.message });
|
|
826
799
|
if (bindingSocket === ws && bindingSocket?.readyState === WebSocket.OPEN)
|
|
827
800
|
bindingSocket.close(1011, "storage identity binding failed");
|
|
828
801
|
},
|
|
@@ -925,7 +898,7 @@ export function serve(config, opts = {}) {
|
|
|
925
898
|
}
|
|
926
899
|
if (msg.type !== "agent:start")
|
|
927
900
|
return; // ready/error 等忽略
|
|
928
|
-
if (
|
|
901
|
+
if (storageIdentityAdmission.isAdmissionClosed())
|
|
929
902
|
return;
|
|
930
903
|
// 任务键:scheduled run 用 runId(每次运行独立 cwd/work-log;overlap 由 server 端 skip_if_running 管,
|
|
931
904
|
// daemon 的 running 去重只兜底"同一 run 重复投递");普通唤醒仍是 线程锚点 ?? 频道。
|
|
@@ -1090,15 +1063,14 @@ export function serve(config, opts = {}) {
|
|
|
1090
1063
|
const ackHint = `\n**协作礼仪:决定接手后,先用 \`${sendCmd}\` 在该任务线程说一声你来跟进**——用日常语言带上你对问题的理解或打算先查什么,不要写"我接 task #N"这类内部编号;写不出实质内容就不单发,并进第一条实质进展,但不要长时间闷头干活把线程空着。`;
|
|
1091
1064
|
// 图片/文件附件:crew message read 会在消息下列出附件及其 id;图片需下载后用 Read 工具查看,才能真正"看到"内容。
|
|
1092
1065
|
const attHint = `\n若消息带图片/文件附件(read 会列出 id),用 \`crew attachment get <id>\` 下载到本地,图片再用 Read 工具打开查看后再处理。`;
|
|
1093
|
-
//
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
: `crew message read --channel ${msg.channelId}`;
|
|
1066
|
+
// 启动上下文由执行层交付;legacy 提示词也不能要求模型补读或退回频道历史。
|
|
1067
|
+
const contextHint = threadId
|
|
1068
|
+
? "直接依据程序交付的本线程上下文与新消息处理,无需启动补读历史。"
|
|
1069
|
+
: "没有线程锚点,只处理明确来信或请求补充当前任务线程,不读取频道历史猜测。";
|
|
1098
1070
|
const wakeText = scheduled
|
|
1099
1071
|
? buildScheduledPrompt(msg.channelId, msg.wake?.content ?? "", scheduled.outputPolicy, scheduled.externalNotificationPolicy)
|
|
1100
1072
|
: (msg.wake?.content
|
|
1101
|
-
? `你被唤醒(${msg.reason}): ${msg.wake.content}\n
|
|
1073
|
+
? `你被唤醒(${msg.reason}): ${msg.wake.content}\n${contextHint}${reasonHint}${ackHint}${threadHint}${attHint}`
|
|
1102
1074
|
: undefined);
|
|
1103
1075
|
const wakeOrigin = scheduled ? undefined : msg.wake?.origin;
|
|
1104
1076
|
const guarded = await runWithOriginDecisionGuard(wakeOrigin, async (attempt) => {
|
|
@@ -1124,8 +1096,8 @@ export function serve(config, opts = {}) {
|
|
|
1124
1096
|
},
|
|
1125
1097
|
} : {}),
|
|
1126
1098
|
...(wakeOrigin ? { wakeOrigin, originDecisionAttempt: attempt } : {}),
|
|
1127
|
-
//
|
|
1128
|
-
...(!scheduled && threadId ? { wakeMessageId: threadId } : {}),
|
|
1099
|
+
// legacy wake.threadId 已由 Server 归一到线程根;与普通触发消息 id 分开透传。
|
|
1100
|
+
...(!scheduled && threadId ? { threadId, wakeMessageId: threadId } : {}),
|
|
1129
1101
|
...(!scheduled && msg.wake?.seq !== undefined ? { wakeContextUpToSeq: msg.wake.seq } : {}),
|
|
1130
1102
|
...(attemptWake ? { wake: attemptWake } : {}),
|
|
1131
1103
|
}, reportActivity, reportConsole, {
|
|
@@ -1358,7 +1330,6 @@ export function serve(config, opts = {}) {
|
|
|
1358
1330
|
concurrencyTelemetry.stop();
|
|
1359
1331
|
runtimeProbe.stop();
|
|
1360
1332
|
completionRetransmitter.stop();
|
|
1361
|
-
storageStatusDelivery.stop();
|
|
1362
1333
|
const deadline = createShutdownDeadline(shutdownTimeoutMs);
|
|
1363
1334
|
if (reconnectTimer !== null) {
|
|
1364
1335
|
clearTimeout(reconnectTimer);
|
|
@@ -1368,7 +1339,6 @@ export function serve(config, opts = {}) {
|
|
|
1368
1339
|
const pending = [
|
|
1369
1340
|
webSocketClosed,
|
|
1370
1341
|
updateController.drain(),
|
|
1371
|
-
...(migrationController === null ? [] : [migrationController.drain()]),
|
|
1372
1342
|
codexHomeModeController.drain(),
|
|
1373
1343
|
executionFrameQueue,
|
|
1374
1344
|
...executionRuns.values(),
|
package/dist/session.js
CHANGED
|
@@ -24,6 +24,10 @@ export async function readSession(runDir) {
|
|
|
24
24
|
if (typeof o.sessionId !== "string" || !o.sessionId)
|
|
25
25
|
return null;
|
|
26
26
|
return {
|
|
27
|
+
...(typeof o.runtime === "string" ? { runtime: o.runtime } : {}),
|
|
28
|
+
...(typeof o.threadRoot === "string" ? { threadRoot: o.threadRoot } : {}),
|
|
29
|
+
...(Number.isSafeInteger(o.threadContextSeq) && o.threadContextSeq >= 0 ? { threadContextSeq: o.threadContextSeq } : {}),
|
|
30
|
+
...(typeof o.threadHistoryComplete === "boolean" ? { threadHistoryComplete: o.threadHistoryComplete } : {}),
|
|
27
31
|
sessionId: o.sessionId,
|
|
28
32
|
lastRunAt: typeof o.lastRunAt === "number" ? o.lastRunAt : 0,
|
|
29
33
|
turns: typeof o.turns === "number" ? o.turns : 0,
|
package/dist/storage-identity.js
CHANGED
|
@@ -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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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: () =>
|
|
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
|
-
import {
|
|
4
|
-
import { DEEPSEEK_HARNESS_CONFIG_ENV, deepSeekHarnessConfigPath, } from "./runtimes/deepseek-harness.js";
|
|
3
|
+
import { buildClaudeLaunch } from "./runtimes/claude.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 = {
|
|
@@ -13,23 +13,18 @@ export function supervisorLaunch(request) {
|
|
|
13
13
|
};
|
|
14
14
|
if (request.runtime === "claude") {
|
|
15
15
|
const additionalDirectories = request.claudeAdditionalDirectories;
|
|
16
|
-
return {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
...common,
|
|
20
|
-
bin: request.bin,
|
|
21
|
-
cwd: request.cwd,
|
|
22
|
-
env: request.env,
|
|
23
|
-
systemPromptPath: request.systemPromptPath,
|
|
24
|
-
...(additionalDirectories === undefined ? {} : { additionalDirectories }),
|
|
25
|
-
...(request.sessionId === undefined ? {} : {
|
|
26
|
-
sessionId: request.sessionId,
|
|
27
|
-
resume: request.resume,
|
|
28
|
-
}),
|
|
29
|
-
}),
|
|
16
|
+
return buildClaudeLaunch({
|
|
17
|
+
...common,
|
|
18
|
+
bin: request.bin,
|
|
30
19
|
cwd: request.cwd,
|
|
31
20
|
env: request.env,
|
|
32
|
-
|
|
21
|
+
systemPromptPath: request.systemPromptPath,
|
|
22
|
+
...(additionalDirectories === undefined ? {} : { additionalDirectories }),
|
|
23
|
+
...(request.sessionId === undefined ? {} : {
|
|
24
|
+
sessionId: request.sessionId,
|
|
25
|
+
resume: request.resume,
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
33
28
|
}
|
|
34
29
|
if (request.runtime === "codex") {
|
|
35
30
|
return {
|
|
@@ -87,7 +82,22 @@ export function supervisorLaunch(request) {
|
|
|
87
82
|
if (request.runtime === "deepseek-harness") {
|
|
88
83
|
if (request.resume)
|
|
89
84
|
throw new Error("DeepSeek Harness ACP does not support session resume");
|
|
90
|
-
const
|
|
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);
|
|
91
101
|
if (configPath === null) {
|
|
92
102
|
throw new Error(`${DEEPSEEK_HARNESS_CONFIG_ENV} must be an absolute path`);
|
|
93
103
|
}
|