@nowcrew/daemon 0.6.67 → 0.6.68
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/agent-ability/materializer.js +1 -20
- package/dist/agent-ability/resolver.js +10 -7
- package/dist/agent-ability/runtime.js +0 -3
- package/dist/automatic-install-target.js +10 -15
- package/dist/computer-cli.js +1 -10
- package/dist/computer-service.js +1 -41
- package/dist/config.js +0 -44
- package/dist/daemon-migration-controller.js +18 -16
- package/dist/execution-protocol.js +1 -0
- package/dist/execution-runner.js +8 -5
- package/dist/local-execution-prompt.js +11 -15
- package/dist/local-executor.js +53 -93
- package/dist/machine-info.js +4 -3
- package/dist/main.js +0 -83
- package/dist/managed-service-lifecycle.js +2 -47
- package/dist/managed-service-startup.js +0 -2
- package/dist/profile-layout.js +4 -10
- package/dist/project-skills/controller.js +0 -2
- package/dist/prompt.js +1 -19
- package/dist/provider-env.js +21 -3
- package/dist/runner.js +2 -4
- package/dist/runtimes/codex-app-server-runner.js +23 -55
- package/dist/runtimes/codex-contract.js +5 -20
- package/dist/runtimes/codex-deepseek-config.js +17 -7
- package/dist/runtimes/codex.js +3 -4
- package/dist/self-training-workspace.js +15 -0
- package/dist/serve.js +3 -68
- package/dist/supervised-runtime.js +1 -3
- package/dist/workspace.js +14 -34
- package/package.json +1 -1
- package/dist/local-memory/index-budget.js +0 -74
- package/dist/local-memory/pending.js +0 -111
- package/dist/local-memory/runtime.js +0 -87
- package/dist/local-memory/types.js +0 -1
- package/dist/local-memory/write.js +0 -448
- package/dist/runtimes/codex-command-safety.js +0 -75
- package/dist/runtimes/codex-turn-retry.js +0 -69
- package/dist/storage-config.js +0 -175
- package/dist/storage-control.js +0 -193
- package/dist/storage-copy.js +0 -609
- package/dist/storage-credential-guard.js +0 -21
- package/dist/storage-doctor.js +0 -228
- package/dist/storage-identity.js +0 -29
- package/dist/storage-layout.js +0 -143
- package/dist/storage-manifest.js +0 -855
- package/dist/storage-migration-controller.js +0 -417
- package/dist/storage-migration-manifest.js +0 -105
- package/dist/storage-migration-policy.js +0 -100
- package/dist/storage-runtime-controller.js +0 -339
- package/dist/storage-runtime-readiness.js +0 -184
- package/dist/storage-runtime.js +0 -39
- package/dist/storage-safe-fs.js +0 -302
- package/dist/storage-serve-adapter.js +0 -65
- package/dist/storage-status-delivery.js +0 -209
- package/dist/storage-status-replay.js +0 -125
- package/dist/storage-target-confirmation.js +0 -129
- package/dist/storage-worker-command.js +0 -27
- package/dist/storage-worker-executor.js +0 -384
- package/dist/storage-worker-handoff.js +0 -151
- package/dist/storage-worker-operation.js +0 -284
- package/dist/storage-worker-status.js +0 -232
package/dist/local-executor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { delimiter, join } from "node:path";
|
|
4
|
+
import { cleanupSelfTrainingRunDir } from "./self-training-workspace.js";
|
|
4
5
|
import { prepareWorkspace, rotateAgentSession, safeKey, } from "./workspace.js";
|
|
5
6
|
import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, spawnClaude, } from "./runtimes/claude.js";
|
|
6
7
|
import { spawnCodex } from "./runtimes/codex.js";
|
|
@@ -8,7 +9,7 @@ import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepS
|
|
|
8
9
|
import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
|
|
9
10
|
import { materializePrivateCodexHome } from "./codex-home-launch.js";
|
|
10
11
|
import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
|
|
11
|
-
import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, } from "./provider-env.js";
|
|
12
|
+
import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, sanitizeEnvVars, } from "./provider-env.js";
|
|
12
13
|
import { augmentedPath } from "./runtime-path.js";
|
|
13
14
|
import { extractFinalText, extractRunMeta, normalizeEvent, parseLine, } from "./normalize.js";
|
|
14
15
|
import { readSession, writeSession, pickResumeId, isNearBudget } from "./session.js";
|
|
@@ -31,7 +32,6 @@ import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPr
|
|
|
31
32
|
import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
|
|
32
33
|
import { CodexStartupStageParser } from "./codex-startup-stage.js";
|
|
33
34
|
import { formatRuntimeStartupError } from "./runtime-startup-error.js";
|
|
34
|
-
import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, renderLocalMemoryResultInstructions, } from "./local-memory/runtime.js";
|
|
35
35
|
function memoryPruneFileFactFields(fileFacts) {
|
|
36
36
|
const fields = {};
|
|
37
37
|
for (const [label, fact] of Object.entries(fileFacts)) {
|
|
@@ -78,7 +78,6 @@ const RUNTIME_RUNNING_CALLBACK_TIMEOUT_MS = 30_000;
|
|
|
78
78
|
const MEMORY_PRUNE_DIAGNOSTICS_TIMEOUT_MS = 2_000;
|
|
79
79
|
const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 250;
|
|
80
80
|
const CLAUDE_INSTRUCTION_WARNING = "claude_additional_directory_instructions_unverified";
|
|
81
|
-
const CODEX_COLLABORATION_EXEC_WARNING = "codex_collaboration_disabled_on_exec";
|
|
82
81
|
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
83
82
|
const RESERVED_ENV = new Set([
|
|
84
83
|
"PATH",
|
|
@@ -98,14 +97,6 @@ function withoutClaudeInstructionFlag(env) {
|
|
|
98
97
|
const warnRuntimeLocally = (code) => {
|
|
99
98
|
process.stderr.write(`[runtime-warning] ${code}\n`);
|
|
100
99
|
};
|
|
101
|
-
export function sanitizeEnvVars(raw) {
|
|
102
|
-
if (!raw)
|
|
103
|
-
return {};
|
|
104
|
-
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
105
|
-
&& ENV_KEY_RE.test(key)
|
|
106
|
-
&& !key.toUpperCase().startsWith("CREW_")
|
|
107
|
-
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
108
|
-
}
|
|
109
100
|
export function awaitExit(child) {
|
|
110
101
|
return new Promise((resolve) => {
|
|
111
102
|
let settled = false;
|
|
@@ -156,6 +147,7 @@ async function launchLegacyRuntime(request) {
|
|
|
156
147
|
effectivePermission: request.effectivePermission,
|
|
157
148
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
158
149
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
150
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
159
151
|
};
|
|
160
152
|
if (request.runtime === "claude") {
|
|
161
153
|
const additionalDirectories = request.claudeAdditionalDirectories
|
|
@@ -245,6 +237,7 @@ export async function executeLocal(input, callbacks = {}, dependencies = {}) {
|
|
|
245
237
|
async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
246
238
|
const providerConfig = input.launch.providerConfig ?? {};
|
|
247
239
|
const { runtime } = input;
|
|
240
|
+
const isolatedMaintenance = input.maintenancePurpose === "self_training";
|
|
248
241
|
const isDeepSeekCodex = runtime.name === "codex" && providerConfig.provider === "deepseek";
|
|
249
242
|
const currentModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model ?? null;
|
|
250
243
|
const launchModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model;
|
|
@@ -269,6 +262,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
269
262
|
? {}
|
|
270
263
|
: { sessionContextFingerprint: input.sessionContextFingerprint }),
|
|
271
264
|
...(input.launch.description ? { description: input.launch.description } : {}),
|
|
265
|
+
...(isolatedMaintenance ? { skipLocalMemory: true } : {}),
|
|
272
266
|
}), dependencies.cancellation);
|
|
273
267
|
}
|
|
274
268
|
catch (error) {
|
|
@@ -279,7 +273,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
279
273
|
}, "workspace_prepare", error);
|
|
280
274
|
throw error;
|
|
281
275
|
}
|
|
282
|
-
const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, input.localMemoryV2Enabled === true);
|
|
283
276
|
let materialized = null;
|
|
284
277
|
let knownAttachmentDirectory = null;
|
|
285
278
|
const startupReservationState = { current: null };
|
|
@@ -289,7 +282,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
289
282
|
let memoryPruneFailurePhase = "diagnostics_before";
|
|
290
283
|
let memoryPruneBeforeSnapshot = null;
|
|
291
284
|
let memoryPruneSharedWriteKey = null;
|
|
292
|
-
let localMemoryResultPath = null;
|
|
293
285
|
let executionWorkspace = workspace;
|
|
294
286
|
let localMemoryTelemetry = null;
|
|
295
287
|
const inheritedEnv = { ...process.env };
|
|
@@ -299,12 +291,23 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
299
291
|
}
|
|
300
292
|
const sourceCodexHome = inheritedEnv.CODEX_HOME
|
|
301
293
|
?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
|
|
294
|
+
let materializedDeepSeekHome;
|
|
302
295
|
try {
|
|
303
296
|
if (isDeepSeekCodex && !providerConfig.providerApiKey) {
|
|
304
297
|
throw new Error("DeepSeek API key is not configured for this Agent");
|
|
305
298
|
}
|
|
306
299
|
if (isDeepSeekCodex) {
|
|
307
|
-
|
|
300
|
+
const providerBaseUrl = providerConfig.providerBaseUrl
|
|
301
|
+
? new URL(providerConfig.providerBaseUrl, input.launch.serverUrl).toString()
|
|
302
|
+
: undefined;
|
|
303
|
+
const materialize = dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome;
|
|
304
|
+
const materializeOptions = {
|
|
305
|
+
...(providerBaseUrl === undefined ? {} : { baseUrl: providerBaseUrl }),
|
|
306
|
+
...(isolatedMaintenance ? { selfTraining: true } : {}),
|
|
307
|
+
};
|
|
308
|
+
materializedDeepSeekHome = await awaitWithCancellation(providerBaseUrl === undefined && !isolatedMaintenance
|
|
309
|
+
? materialize(workspace.homeDir)
|
|
310
|
+
: materialize(workspace.homeDir, materializeOptions), dependencies.cancellation);
|
|
308
311
|
}
|
|
309
312
|
const supportsNativeResume = runtime.name === "claude"
|
|
310
313
|
|| (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
|
|
@@ -375,6 +378,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
375
378
|
nearBudget,
|
|
376
379
|
...(input.projectContext === undefined ? {} : { projectContext: input.projectContext }),
|
|
377
380
|
};
|
|
381
|
+
const systemPrompt = resolvePrompt(input.systemPrompt, promptContext);
|
|
378
382
|
if (input.attachments && input.attachments.length > 0) {
|
|
379
383
|
if (dependencies.cancellation?.isRequested())
|
|
380
384
|
throw new RuntimeCancelledError();
|
|
@@ -408,52 +412,30 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
408
412
|
const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
|
|
409
413
|
const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
410
414
|
memoryPruneTraceId = parseMemoryPruneTraceId(wakePrompt);
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
localMemoryTelemetry = createLocalMemoryTelemetry({
|
|
436
|
-
executionId: input.executionId,
|
|
437
|
-
agentHandle: input.handle,
|
|
438
|
-
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
439
|
-
runtime: runtime.name,
|
|
440
|
-
stableProtocolRuntime: dependencies.launchRuntime !== undefined,
|
|
441
|
-
agentDir: executionWorkspace.dir,
|
|
442
|
-
runDir: executionWorkspace.runDir,
|
|
443
|
-
isMemoryPrune: memoryPruneTraceId !== null,
|
|
444
|
-
diagnosticsTimeoutMs: dependencies.localMemoryDiagnosticsTimeoutMs
|
|
445
|
-
?? LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS,
|
|
446
|
-
});
|
|
447
|
-
await localMemoryTelemetry.captureBefore();
|
|
448
|
-
}
|
|
449
|
-
catch (error) {
|
|
450
|
-
localMemoryTelemetry = null;
|
|
451
|
-
logLocalMemoryDiagnosticsFailure({
|
|
452
|
-
executionId: input.executionId,
|
|
453
|
-
agentHandle: input.handle,
|
|
454
|
-
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
455
|
-
}, "initialize", error);
|
|
456
|
-
}
|
|
415
|
+
if (!isolatedMaintenance)
|
|
416
|
+
try {
|
|
417
|
+
localMemoryTelemetry = createLocalMemoryTelemetry({
|
|
418
|
+
executionId: input.executionId,
|
|
419
|
+
agentHandle: input.handle,
|
|
420
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
421
|
+
runtime: runtime.name,
|
|
422
|
+
stableProtocolRuntime: dependencies.launchRuntime !== undefined,
|
|
423
|
+
agentDir: executionWorkspace.dir,
|
|
424
|
+
runDir: executionWorkspace.runDir,
|
|
425
|
+
isMemoryPrune: memoryPruneTraceId !== null,
|
|
426
|
+
diagnosticsTimeoutMs: dependencies.localMemoryDiagnosticsTimeoutMs
|
|
427
|
+
?? LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS,
|
|
428
|
+
});
|
|
429
|
+
await localMemoryTelemetry.captureBefore();
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
localMemoryTelemetry = null;
|
|
433
|
+
logLocalMemoryDiagnosticsFailure({
|
|
434
|
+
executionId: input.executionId,
|
|
435
|
+
agentHandle: input.handle,
|
|
436
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
437
|
+
}, "initialize", error);
|
|
438
|
+
}
|
|
457
439
|
if (memoryPruneTraceId !== null) {
|
|
458
440
|
memoryPruneSharedWriteKey = JSON.stringify([input.launch.agentsRoot, input.handle]);
|
|
459
441
|
const activePruneCount = (activeMemoryPrunes.get(memoryPruneSharedWriteKey) ?? 0) + 1;
|
|
@@ -487,6 +469,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
487
469
|
}
|
|
488
470
|
}
|
|
489
471
|
memoryPruneFailurePhase = "runtime_prepare";
|
|
472
|
+
const crewHome = isolatedMaintenance ? executionWorkspace.runDir : workspace.dir;
|
|
490
473
|
const baseEnv = {
|
|
491
474
|
...inheritedEnv,
|
|
492
475
|
...sanitizeEnvVars(providerConfig.envVars),
|
|
@@ -495,10 +478,10 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
495
478
|
CREW_SERVER_URL: input.launch.serverUrl,
|
|
496
479
|
CREW_TOKEN: input.launch.token,
|
|
497
480
|
CREW_CHANNEL: input.channelId,
|
|
498
|
-
CREW_HOME:
|
|
481
|
+
CREW_HOME: crewHome,
|
|
499
482
|
// Expose only the training root; ability-specific workspace layout stays
|
|
500
483
|
// owned by the training package rather than the NowWork daemon.
|
|
501
|
-
QA_RUNTIME_ROOT: join(
|
|
484
|
+
QA_RUNTIME_ROOT: join(crewHome, "training"),
|
|
502
485
|
CREW_TASK_DIR: executionWorkspace.runDir,
|
|
503
486
|
CREW_TASK_LOG: executionWorkspace.workLogPath,
|
|
504
487
|
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
@@ -514,7 +497,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
514
497
|
};
|
|
515
498
|
const providerEnv = applyProviderEnv(baseEnv, runtime.name, input.launch.codexHomeMode === undefined
|
|
516
499
|
? providerConfig
|
|
517
|
-
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir);
|
|
500
|
+
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir, materializedDeepSeekHome);
|
|
518
501
|
const childEnv = runtime.name === "deepseek-harness"
|
|
519
502
|
? applyDeepSeekHarnessMachineEnv(providerEnv, inheritedEnv)
|
|
520
503
|
: providerEnv;
|
|
@@ -525,7 +508,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
525
508
|
qa_runtime_root: childEnv.QA_RUNTIME_ROOT ?? null,
|
|
526
509
|
qa_runtime_root_present: childEnv.QA_RUNTIME_ROOT !== undefined,
|
|
527
510
|
inherited_qa_runtime_root_present: inheritedEnv.QA_RUNTIME_ROOT !== undefined,
|
|
528
|
-
training_root: join(
|
|
511
|
+
training_root: join(crewHome, "training"),
|
|
529
512
|
project_context_present: input.projectContext !== undefined,
|
|
530
513
|
project_ids: JSON.stringify(input.projectContext === undefined
|
|
531
514
|
? []
|
|
@@ -539,13 +522,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
539
522
|
ability_release_id: input.abilityRelease?.releaseId,
|
|
540
523
|
ability_root_commit: input.abilityRelease?.rootCommit,
|
|
541
524
|
});
|
|
542
|
-
const
|
|
543
|
-
const launchRuntime = dependencies.launchRuntime ?? (async (launchReq) => {
|
|
544
|
-
if (launchReq.runtime === "codex" && launchReq.codexCollaborationTools === true) {
|
|
545
|
-
warn(CODEX_COLLABORATION_EXEC_WARNING);
|
|
546
|
-
}
|
|
547
|
-
return launchLegacyRuntime(launchReq);
|
|
548
|
-
});
|
|
525
|
+
const launchRuntime = dependencies.launchRuntime ?? launchLegacyRuntime;
|
|
549
526
|
if (dependencies.cancellation?.isRequested())
|
|
550
527
|
throw new RuntimeCancelledError();
|
|
551
528
|
let activeProjectSkillRuntimeRoot;
|
|
@@ -614,7 +591,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
614
591
|
}
|
|
615
592
|
else {
|
|
616
593
|
runtimeEnv = withoutClaudeInstructionFlag(childEnv);
|
|
617
|
-
|
|
594
|
+
(dependencies.warnRuntime ?? warnRuntimeLocally)(CLAUDE_INSTRUCTION_WARNING);
|
|
618
595
|
callbacks.onConsole?.({
|
|
619
596
|
stream: "system",
|
|
620
597
|
text: `[runtime-warning] ${CLAUDE_INSTRUCTION_WARNING}`,
|
|
@@ -638,9 +615,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
638
615
|
...(launchReasoning === undefined ? {} : { reasoning: launchReasoning }),
|
|
639
616
|
...(launchSessionId === null ? {} : { sessionId: launchSessionId }),
|
|
640
617
|
resume: resuming,
|
|
641
|
-
...(runtime.codexCollaborationTools === undefined
|
|
642
|
-
? {}
|
|
643
|
-
: { codexCollaborationTools: runtime.codexCollaborationTools }),
|
|
644
618
|
...(attachmentPlan.nativeImagePaths.length > 0
|
|
645
619
|
? { imagePaths: attachmentPlan.nativeImagePaths }
|
|
646
620
|
: {}),
|
|
@@ -654,6 +628,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
654
628
|
workspaceRoots: Object.freeze(input.projectContext.secondary.map((project) => project.root)),
|
|
655
629
|
projectContext: input.projectContext,
|
|
656
630
|
}),
|
|
631
|
+
...(isolatedMaintenance && runtime.name === "codex" ? { networkAccess: true } : {}),
|
|
657
632
|
};
|
|
658
633
|
return launchRequest;
|
|
659
634
|
};
|
|
@@ -927,24 +902,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
927
902
|
const { exitCode, spawnError, terminationSignal } = runtimeExit;
|
|
928
903
|
localMemoryTelemetry?.markRuntimeExited(exitCode);
|
|
929
904
|
memoryPruneRuntimeExitCode = exitCode;
|
|
930
|
-
if (localMemoryResultPath !== null) {
|
|
931
|
-
const committed = await commitLocalMemoryResultAfterExit({
|
|
932
|
-
resultPath: localMemoryResultPath,
|
|
933
|
-
agentDir: executionWorkspace.dir,
|
|
934
|
-
executionId: input.executionId,
|
|
935
|
-
pruneTraceId: memoryPruneTraceId,
|
|
936
|
-
exitCode,
|
|
937
|
-
});
|
|
938
|
-
if (committed.status === "failed") {
|
|
939
|
-
dslog("local_memory.commit_failed", "结构化本地记忆候选未落地,保留结果文件", {
|
|
940
|
-
level: "WARN",
|
|
941
|
-
execution_id: input.executionId,
|
|
942
|
-
agent_handle: input.handle,
|
|
943
|
-
error_code: committed.error,
|
|
944
|
-
result_path: localMemoryResultPath,
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
905
|
const errorTail = [
|
|
949
906
|
stderrTail.trim(),
|
|
950
907
|
spawnError === undefined
|
|
@@ -1118,5 +1075,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
1118
1075
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1119
1076
|
process.stderr.write(`[execution] failed to remove prompt ${workspace.systemPromptPath}: ${detail}\n`);
|
|
1120
1077
|
}
|
|
1078
|
+
if (isolatedMaintenance) {
|
|
1079
|
+
await cleanupSelfTrainingRunDir(join(workspace.dir, "tasks"), executionWorkspace.runDir);
|
|
1080
|
+
}
|
|
1121
1081
|
}
|
|
1122
1082
|
}
|
package/dist/machine-info.js
CHANGED
|
@@ -24,6 +24,7 @@ export { executableRuntimes } from "./runtime-capabilities.js";
|
|
|
24
24
|
const execFileP = promisify(execFile);
|
|
25
25
|
export const PROJECT_WORKSPACES_CAPABILITY = "project_workspaces_v1";
|
|
26
26
|
export const TRAINING_PROJECT_DEPENDENCIES_CAPABILITY = "training_project_dependencies_v1";
|
|
27
|
+
export const SELF_TRAINING_EXECUTION_CAPABILITY = "self_training_execution_v1";
|
|
27
28
|
export const NATIVE_PROJECT_WORKSPACE_READINESS = Object.freeze({
|
|
28
29
|
resolverReady: true,
|
|
29
30
|
nativeRuntimeAdaptersReady: true,
|
|
@@ -39,6 +40,7 @@ export const DAEMON_CAPABILITIES = Object.freeze([
|
|
|
39
40
|
"execution_machine_queue_v1",
|
|
40
41
|
"execution_agent_memory_policy_v1",
|
|
41
42
|
"execution_agent_memory_mapping_v1",
|
|
43
|
+
SELF_TRAINING_EXECUTION_CAPABILITY,
|
|
42
44
|
"project_skills_v1",
|
|
43
45
|
PROJECT_WORKSPACES_CAPABILITY,
|
|
44
46
|
TRAINING_PROJECT_DEPENDENCIES_CAPABILITY,
|
|
@@ -191,11 +193,10 @@ export async function collectMachineHello(agentsRoot, executionLimits, runtimePl
|
|
|
191
193
|
profileName: dependencies.profileName,
|
|
192
194
|
daemonHome: dependencies.daemonHome ?? daemonHome(),
|
|
193
195
|
agentsRoot,
|
|
194
|
-
...(dependencies.profileName.match(/^nw-(?:
|
|
196
|
+
...(dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)?.[1] === undefined
|
|
195
197
|
? {}
|
|
196
|
-
: { localInstanceName: dependencies.profileName.match(/^nw-(?:
|
|
198
|
+
: { localInstanceName: dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)[1] }),
|
|
197
199
|
}),
|
|
198
|
-
...(dependencies.storageIdentity ?? {}),
|
|
199
200
|
...(backend.supported ? {
|
|
200
201
|
executionProtocol: EXECUTION_PROTOCOL,
|
|
201
202
|
executionLimits: Object.freeze({
|
package/dist/main.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { parseArgs } from "node:util";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
|
-
import { join } from "node:path";
|
|
11
10
|
import { loadConfig, ConfigError } from "./config.js";
|
|
12
11
|
import { detectDaemonLang, formatDaemonText, translateDaemon } from "./i18n.js";
|
|
13
12
|
import { cliVersion, daemonVersion } from "./machine-info.js";
|
|
@@ -22,18 +21,7 @@ import { formatDaemonStartupError } from "./daemon-startup-error.js";
|
|
|
22
21
|
import { assertManagedServiceStartup } from "./managed-service-startup.js";
|
|
23
22
|
import { daemonGlobalInstallation } from "./daemon-installation.js";
|
|
24
23
|
import { acquireDaemonInstallationLease, runWithDaemonInstallationLease, } from "./daemon-installation-lease.js";
|
|
25
|
-
import { loadStorageRuntime } from "./storage-runtime.js";
|
|
26
|
-
import { readStorageMigrationReceipt } from "./storage-migration-controller.js";
|
|
27
|
-
import { storageMigrationStatusReplay } from "./storage-status-replay.js";
|
|
28
|
-
import { runStorageWorkerCommand } from "./storage-worker-command.js";
|
|
29
|
-
import { probeStorageMigrationRuntime } from "./storage-runtime-readiness.js";
|
|
30
|
-
import { createStorageTargetConfirmationHandler } from "./storage-target-confirmation.js";
|
|
31
24
|
async function main() {
|
|
32
|
-
const workerResult = await runStorageWorkerCommand(process.argv.slice(2));
|
|
33
|
-
if (workerResult !== null) {
|
|
34
|
-
process.exitCode = workerResult;
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
25
|
const computerResult = await runComputerCommand(process.argv.slice(2));
|
|
38
26
|
if (computerResult !== null) {
|
|
39
27
|
process.exitCode = computerResult;
|
|
@@ -69,10 +57,8 @@ async function main() {
|
|
|
69
57
|
// 不把 machine token 放进 argv / plist / systemd unit / scheduled task。
|
|
70
58
|
if (values["daemon-home"])
|
|
71
59
|
process.env.CREW_DAEMON_HOME = values["daemon-home"];
|
|
72
|
-
let activeDaemonHome;
|
|
73
60
|
if (values.profile) {
|
|
74
61
|
const home = daemonHome();
|
|
75
|
-
activeDaemonHome = home;
|
|
76
62
|
const profile = await loadProfile(values.profile, home);
|
|
77
63
|
if (cmd === "serve") {
|
|
78
64
|
await assertProfileAgentsRootUnique(profile, home, homedir());
|
|
@@ -85,12 +71,6 @@ async function main() {
|
|
|
85
71
|
agentsRoot: resolveAgentsRoot(profile.agentsRoot, homedir(), process.platform),
|
|
86
72
|
nodePath: process.execPath,
|
|
87
73
|
entryPath: builtDaemonEntry(),
|
|
88
|
-
environment: {
|
|
89
|
-
...(process.env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: process.env.NOWWORK_HOME }),
|
|
90
|
-
...(process.env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: process.env.NOWWORK_ENV_TAG }),
|
|
91
|
-
...(process.env.NOWWORK_STORAGE_POLICY_PATH === undefined
|
|
92
|
-
? {} : { NOWWORK_STORAGE_POLICY_PATH: process.env.NOWWORK_STORAGE_POLICY_PATH }),
|
|
93
|
-
},
|
|
94
74
|
});
|
|
95
75
|
}
|
|
96
76
|
applyProfileToEnv(profile, process.env);
|
|
@@ -120,72 +100,9 @@ async function main() {
|
|
|
120
100
|
const installationLease = installation === null
|
|
121
101
|
? null
|
|
122
102
|
: await acquireDaemonInstallationLease(installation.npmPrefix);
|
|
123
|
-
const storageRuntime = values.profile === undefined
|
|
124
|
-
? null
|
|
125
|
-
: await loadStorageRuntime({
|
|
126
|
-
profile: values.profile,
|
|
127
|
-
serverUrl: config.serverUrl,
|
|
128
|
-
userHome: homedir(),
|
|
129
|
-
platform: process.platform,
|
|
130
|
-
environment: process.env,
|
|
131
|
-
});
|
|
132
|
-
const migrationRuntime = values.profile === undefined || activeDaemonHome === undefined || storageRuntime !== null
|
|
133
|
-
? { ready: false, reason: "not_legacy_source" }
|
|
134
|
-
: await probeStorageMigrationRuntime({
|
|
135
|
-
profile: values.profile,
|
|
136
|
-
serverUrl: config.serverUrl,
|
|
137
|
-
currentDaemonHome: activeDaemonHome,
|
|
138
|
-
currentAgentsRoot: config.agentsRoot,
|
|
139
|
-
userHome: homedir(),
|
|
140
|
-
platform: process.platform,
|
|
141
|
-
...(process.getuid === undefined ? {} : { uid: process.getuid() }),
|
|
142
|
-
environment: process.env,
|
|
143
|
-
});
|
|
144
|
-
const targetConfirmation = storageRuntime?.identity.storageMigrationId === undefined
|
|
145
|
-
? null
|
|
146
|
-
: createStorageTargetConfirmationHandler({
|
|
147
|
-
backupsRoot: storageRuntime.local.backupsRoot,
|
|
148
|
-
identity: storageRuntime.identity,
|
|
149
|
-
profile: storageRuntime.installation.profile,
|
|
150
|
-
completionTimeoutMs: 180_000,
|
|
151
|
-
});
|
|
152
|
-
const storageMigration = storageRuntime !== null
|
|
153
|
-
? {
|
|
154
|
-
ready: false,
|
|
155
|
-
statusOutboxPath: join(storageRuntime.local.stateRoot, "storage-status-outbox.json"),
|
|
156
|
-
identity: storageRuntime.identity,
|
|
157
|
-
...(targetConfirmation === null ? {} : { confirm: targetConfirmation.handle }),
|
|
158
|
-
...(storageRuntime.identity.storageMigrationId === undefined ? {} : {
|
|
159
|
-
replayStatus: async (send) => {
|
|
160
|
-
const migrationId = storageRuntime.identity.storageMigrationId;
|
|
161
|
-
const receipt = await readStorageMigrationReceipt(storageRuntime.local.backupsRoot, migrationId);
|
|
162
|
-
if (receipt === null)
|
|
163
|
-
throw new Error(`storage migration receipt not found: ${migrationId}`);
|
|
164
|
-
for (const frame of storageMigrationStatusReplay(receipt, {
|
|
165
|
-
migrationId,
|
|
166
|
-
environment: storageRuntime.identity.storageEnvironment,
|
|
167
|
-
workspaceId: storageRuntime.identity.storageWorkspaceId,
|
|
168
|
-
profile: storageRuntime.installation.profile,
|
|
169
|
-
}))
|
|
170
|
-
send(frame);
|
|
171
|
-
},
|
|
172
|
-
}),
|
|
173
|
-
}
|
|
174
|
-
: migrationRuntime.ready
|
|
175
|
-
? {
|
|
176
|
-
ready: true,
|
|
177
|
-
statusOutboxPath: migrationRuntime.runtime.statusOutboxPath,
|
|
178
|
-
identity: migrationRuntime.runtime.identity,
|
|
179
|
-
controller: migrationRuntime.runtime.controller,
|
|
180
|
-
attach: migrationRuntime.runtime.attach,
|
|
181
|
-
bindServerIdentity: migrationRuntime.runtime.bindServerIdentity,
|
|
182
|
-
replayStatus: migrationRuntime.runtime.replayStatus,
|
|
183
|
-
}
|
|
184
|
-
: undefined;
|
|
185
103
|
await runWithDaemonInstallationLease(installationLease, async () => {
|
|
186
104
|
const service = serve(config, {
|
|
187
105
|
...(values.profile === undefined ? {} : { profileName: values.profile }),
|
|
188
|
-
...(storageMigration === undefined ? {} : { storageMigration }),
|
|
189
106
|
});
|
|
190
107
|
await runServeLifecycle(service);
|
|
191
108
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { acquireDaemonInstallationLease } from "./daemon-installation-lease.js";
|
|
3
|
-
import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction,
|
|
3
|
+
import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction, uninstallService, } from "./computer-service.js";
|
|
4
4
|
import { ManagedServiceConflictError, ManagedServiceRegistryError, assertRegistryCoversDescriptors, listManagedServiceDescriptorPaths, managedServiceIdentityMatches, managedServiceConflict, normalizeManagedServiceRecord, serviceDescriptorSha256, withManagedServiceRegistryLock, } from "./managed-service-registry.js";
|
|
5
5
|
function requirePortableManagedSpec(spec) {
|
|
6
6
|
if ((spec.platform !== "darwin" && spec.platform !== "linux")
|
|
@@ -35,17 +35,6 @@ function sameManagedIdentity(left, right) {
|
|
|
35
35
|
const { generation: _generation, createdAt: _createdAt, ...expected } = right;
|
|
36
36
|
return managedServiceIdentityMatches(left, expected);
|
|
37
37
|
}
|
|
38
|
-
function sameManagedResource(left, right) {
|
|
39
|
-
const { generation: _leftGeneration, createdAt: _leftCreatedAt, descriptorSha256: _leftHash, ...leftResource } = left;
|
|
40
|
-
const { generation: _rightGeneration, createdAt: _rightCreatedAt, descriptorSha256: _rightHash, ...rightResource } = right;
|
|
41
|
-
return JSON.stringify(leftResource) === JSON.stringify(rightResource);
|
|
42
|
-
}
|
|
43
|
-
function descriptorDiffIsStorageEnvironmentOnly(previous, next) {
|
|
44
|
-
if (!next.includes("\n <key>EnvironmentVariables</key>\n"))
|
|
45
|
-
return false;
|
|
46
|
-
const strip = (descriptor) => descriptor.replace(/\n <key>EnvironmentVariables<\/key>\n <dict>\n(?: <key>(?:NOWWORK_HOME|NOWWORK_ENV_TAG|NOWWORK_STORAGE_POLICY_PATH)<\/key><string>[^<]*<\/string>\n)+ <\/dict>\n/u, "\n");
|
|
47
|
-
return strip(previous) === strip(next);
|
|
48
|
-
}
|
|
49
38
|
function assertRecordDescribesSpec(input) {
|
|
50
39
|
requirePortableManagedSpec(input.spec);
|
|
51
40
|
const { spec, record } = input;
|
|
@@ -62,20 +51,8 @@ function assertRecordDescribesSpec(input) {
|
|
|
62
51
|
}
|
|
63
52
|
}
|
|
64
53
|
export async function installManagedService(input) {
|
|
54
|
+
assertRecordDescribesSpec(input);
|
|
65
55
|
requirePortableManagedSpec(input.spec);
|
|
66
|
-
const requestedDescriptor = input.spec.descriptor;
|
|
67
|
-
const expectedDescriptorHash = serviceDescriptorSha256(input.spec.descriptor);
|
|
68
|
-
const expectedPath = normalizeManagedServiceRecord({
|
|
69
|
-
...input.record,
|
|
70
|
-
descriptorPath: input.spec.descriptorPath,
|
|
71
|
-
descriptorSha256: expectedDescriptorHash,
|
|
72
|
-
}).descriptorPath;
|
|
73
|
-
if (input.record.platform !== input.spec.platform
|
|
74
|
-
|| input.record.serviceId !== input.spec.id
|
|
75
|
-
|| input.record.profile !== input.spec.profile
|
|
76
|
-
|| input.record.descriptorPath !== expectedPath) {
|
|
77
|
-
throw new ManagedServiceRegistryError(`Managed service record does not describe '${input.spec.id}'`);
|
|
78
|
-
}
|
|
79
56
|
const acquire = input.acquireInstallationLease ?? acquireDaemonInstallationLease;
|
|
80
57
|
await withManagedServiceRegistryLock(input.registry ?? {}, async ({ services, replace }) => {
|
|
81
58
|
const exact = services.find((record) => sameManagedIdentity(record, input.record));
|
|
@@ -99,28 +76,6 @@ export async function installManagedService(input) {
|
|
|
99
76
|
}
|
|
100
77
|
return;
|
|
101
78
|
}
|
|
102
|
-
const registered = services.find((record) => record.serviceId === input.record.serviceId
|
|
103
|
-
&& record.profile === input.record.profile
|
|
104
|
-
&& record.daemonHome === input.record.daemonHome);
|
|
105
|
-
if (registered !== undefined && sameManagedResource(registered, input.record)) {
|
|
106
|
-
const current = await readServiceDescriptor(input.spec);
|
|
107
|
-
if (current === null || serviceDescriptorSha256(current) !== registered.descriptorSha256) {
|
|
108
|
-
throw new ManagedServiceRegistryError(`Installed descriptor for '${input.spec.id}' does not match its registry record`);
|
|
109
|
-
}
|
|
110
|
-
if (!descriptorDiffIsStorageEnvironmentOnly(current, requestedDescriptor)) {
|
|
111
|
-
throw new ManagedServiceRegistryError(`Installed descriptor for '${input.spec.id}' conflicts with the requested identity`);
|
|
112
|
-
}
|
|
113
|
-
await replaceInstalledServiceDescriptor(input.spec, current, input.runner);
|
|
114
|
-
try {
|
|
115
|
-
await replace(services.map((record) => record === registered ? input.record : record));
|
|
116
|
-
}
|
|
117
|
-
catch (error) {
|
|
118
|
-
const rollbackSpec = { ...input.spec, descriptor: current };
|
|
119
|
-
await replaceInstalledServiceDescriptor(rollbackSpec, requestedDescriptor, input.runner).catch(() => undefined);
|
|
120
|
-
throw error;
|
|
121
|
-
}
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
79
|
const conflict = managedServiceConflict(services, input.record);
|
|
125
80
|
if (conflict !== null) {
|
|
126
81
|
throw new ManagedServiceConflictError(conflict.resource, input.record, conflict.owner);
|
|
@@ -14,7 +14,6 @@ export async function assertManagedServiceStartup(input) {
|
|
|
14
14
|
nodePath: input.nodePath,
|
|
15
15
|
entryPath: input.entryPath,
|
|
16
16
|
profileHome: input.daemonHome,
|
|
17
|
-
...(input.environment === undefined ? {} : { environment: input.environment }),
|
|
18
17
|
});
|
|
19
18
|
const readRegistry = input.readManagedServices
|
|
20
19
|
?? (() => readManagedServiceRegistry({ userHome: input.userHome }));
|
|
@@ -43,7 +42,6 @@ export async function assertManagedServiceStartup(input) {
|
|
|
43
42
|
nodePath: input.nodePath,
|
|
44
43
|
entryPath: input.entryPath,
|
|
45
44
|
profileHome: input.daemonHome,
|
|
46
|
-
...(input.environment === undefined ? {} : { environment: input.environment }),
|
|
47
45
|
legacyServiceId: true,
|
|
48
46
|
});
|
|
49
47
|
const legacyDescriptor = await readDescriptor(legacySpec);
|
package/dist/profile-layout.js
CHANGED
|
@@ -2,10 +2,6 @@ import { posix, win32 } from "node:path";
|
|
|
2
2
|
export const PROFILE_LAYOUT_VERSION = 1;
|
|
3
3
|
const PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,47}$/u;
|
|
4
4
|
const LOCAL_INSTANCE_NAME = /^[a-z0-9][a-z0-9_-]{0,39}$/u;
|
|
5
|
-
/** Accept the legacy `in` tag at the boundary, but never persist it in a new layout. */
|
|
6
|
-
export function normalizeProfileEnvironment(value) {
|
|
7
|
-
return value === "in" ? "production" : value;
|
|
8
|
-
}
|
|
9
5
|
export function validateLocalInstanceName(value) {
|
|
10
6
|
const normalized = value.trim().toLowerCase();
|
|
11
7
|
if (!LOCAL_INSTANCE_NAME.test(normalized) || normalized.includes("..")) {
|
|
@@ -14,21 +10,19 @@ export function validateLocalInstanceName(value) {
|
|
|
14
10
|
return normalized;
|
|
15
11
|
}
|
|
16
12
|
export function profileForLocalInstance(environment, localInstanceName) {
|
|
17
|
-
const
|
|
18
|
-
const profile = `nw-${canonicalEnvironment}-${validateLocalInstanceName(localInstanceName)}`;
|
|
13
|
+
const profile = `nw-${environment}-${validateLocalInstanceName(localInstanceName)}`;
|
|
19
14
|
if (!PROFILE_NAME.test(profile))
|
|
20
15
|
throw new Error("derived daemon profile exceeds 48 characters");
|
|
21
16
|
return profile;
|
|
22
17
|
}
|
|
23
18
|
export function profileLayout(input) {
|
|
24
|
-
const environment = normalizeProfileEnvironment(input.environment);
|
|
25
19
|
const localInstanceName = validateLocalInstanceName(input.localInstanceName);
|
|
26
|
-
const profile = profileForLocalInstance(environment, localInstanceName);
|
|
20
|
+
const profile = profileForLocalInstance(input.environment, localInstanceName);
|
|
27
21
|
const paths = input.platform === "win32" ? win32 : posix;
|
|
28
|
-
const base = `${environment}-${localInstanceName}`;
|
|
22
|
+
const base = `${input.environment}-${localInstanceName}`;
|
|
29
23
|
return Object.freeze({
|
|
30
24
|
layoutVersion: PROFILE_LAYOUT_VERSION,
|
|
31
|
-
environment,
|
|
25
|
+
environment: input.environment,
|
|
32
26
|
localInstanceName,
|
|
33
27
|
profile,
|
|
34
28
|
daemonHome: paths.join(input.userHome, ".crew", `daemon-${base}`),
|
|
@@ -183,8 +183,6 @@ export function createProjectSkillsController(deps) {
|
|
|
183
183
|
const parsed = ProjectCommandSchema.safeParse(input);
|
|
184
184
|
if (!parsed.success)
|
|
185
185
|
return { ok: false, error: "invalid_project_command" };
|
|
186
|
-
if (deps.isAdmissionClosed?.() === true)
|
|
187
|
-
return { ok: false, error: "storage_migration_quiescing" };
|
|
188
186
|
try {
|
|
189
187
|
assertCurrentEpoch(epochToken);
|
|
190
188
|
}
|
package/dist/prompt.js
CHANGED
|
@@ -305,7 +305,7 @@ ${memoryTaskReadRule}
|
|
|
305
305
|
- **跨任务协调看实时清单**:用 \`crew task list --mine\` 查你当前所有任务及其状态(谁在排队、谁在评审)。例:你一次只能测一个页面时,据此判断"正在测 #1、#2 排队",在 #2 的工作日志里标注"等 #1 完成"。
|
|
306
306
|
- 需要更多背景时再 \`Read $CREW_HOME/notes/<topic>.md\`;不要一次把所有笔记读进上下文。
|
|
307
307
|
|
|
308
|
-
${communicationSection}
|
|
308
|
+
${communicationSection}
|
|
309
309
|
|
|
310
310
|
CLI 成功时打印人类可读文本到 stdout;失败时 stderr 给出错误,并用**退出码**告诉你下一步:
|
|
311
311
|
- \`4\` = 任务已被他人认领 / 不可认领 → 停手,别抢,转做别的。
|
|
@@ -385,21 +385,3 @@ export function buildOriginDecisionRetryPrompt(channelId, threadId) {
|
|
|
385
385
|
"普通内部消息不算外部回复决策;--send-draft 也不是外部回复开关。",
|
|
386
386
|
].join("\n");
|
|
387
387
|
}
|
|
388
|
-
/**
|
|
389
|
-
* Codex 原生 collaboration.* 工具的用法与边界(spec 2d;2026-08-28 对 Codex 0.149.1/0.150.1 实测)。
|
|
390
|
-
* 只在 daemon 开关打开且 runtime 为 codex 时注入;是本机 runtime 能力事实,不是产品策略。
|
|
391
|
-
* 契约来自实测,不编造 Codex 未声明的返回字段。
|
|
392
|
-
*/
|
|
393
|
-
export function buildCollaborationToolsPrompt() {
|
|
394
|
-
return `
|
|
395
|
-
|
|
396
|
-
## Codex 原生协作工具
|
|
397
|
-
本机 Codex 已开启 \`collaboration.*\` 子 agent 工具。它们完全在 Codex 进程内运行:子 agent 的工作对 NowWork 的频道、线程、任务、execution 记录和按 agent 的 token 统计**都不可见**,token 并入你本轮的用量。只用于你自己会汇总的内部交叉核对、并行调研;绝不用于人类或其它 NowWork 成员需要观察、审计或接手的事。
|
|
398
|
-
- **\`collaboration.spawn_agent\`** —— 必填 \`task_name\`(仅小写字母/数字/下划线)、\`message\`;可选 \`fork_turns\`(\`"none"\`|\`"all"\`|\`"3"\` 这样的轮数,默认 \`"all"\`)、\`model\`、\`reasoning_effort\`。立即启动独立 turn,你不阻塞;子 agent 跑完后 idle 而非销毁。新 agent 用 **canonical 任务名路径**引用,如 \`/root/api_review\`。含 root 最多 4 个活跃 agent。
|
|
399
|
-
- **\`collaboration.followup_task\`** —— 必填 \`target\`(canonical 路径,不是 UUID)、\`message\`。异步投递并把 idle 的目标唤醒进新 turn,可重复。
|
|
400
|
-
- **\`collaboration.send_message\`** —— 必填 \`target\`、\`message\`。只入队,**不**启动 turn;要触发工作用 followup_task。
|
|
401
|
-
- **\`collaboration.list_agents\`** —— 可选 \`path_prefix\`。只读,列出 live agents。
|
|
402
|
-
- **\`collaboration.wait_agent\`** —— 可选 \`timeout_ms\`(10000–3600000,默认 30000)。阻塞你直到任一子 agent 有更新、有新输入或超时。
|
|
403
|
-
- **没有任何返回值有保证的 schema**:不要去解析返回值里的 \`agent_id\`/\`id\`/\`accepted\` 之类字段;用 \`list_agents\` 和 \`wait_agent\` 获取状态。
|
|
404
|
-
- **不要调用 \`collaboration.interrupt_agent\`**:它会把你自己这轮 turn 一起中断(2026-08-10 线上事故),中断后本轮结果全部丢失且不会重跑。让子 agent 自然跑完或直接忽略其结果。`;
|
|
405
|
-
}
|