@nowcrew/daemon 0.6.66 → 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/execution-protocol.js +1 -0
- package/dist/execution-runner.js +8 -3
- package/dist/local-execution-prompt.js +4 -3
- package/dist/local-executor.js +50 -37
- package/dist/machine-info.js +2 -0
- package/dist/provider-env.js +21 -3
- package/dist/runner.js +2 -1
- package/dist/runtimes/codex-app-server-runner.js +9 -1
- package/dist/runtimes/codex-deepseek-config.js +17 -7
- package/dist/runtimes/codex.js +3 -0
- package/dist/self-training-workspace.js +15 -0
- package/dist/supervised-runtime.js +1 -0
- package/dist/workspace.js +13 -10
- package/package.json +1 -1
|
@@ -197,6 +197,7 @@ export const ExecutionStartSchema = z.object({
|
|
|
197
197
|
language: z.enum(["zh", "en"]),
|
|
198
198
|
systemPrompt: z.string(),
|
|
199
199
|
wakePrompt: z.string(),
|
|
200
|
+
maintenancePurpose: z.literal("self_training").optional(),
|
|
200
201
|
}).strict(),
|
|
201
202
|
runtime: z.object({
|
|
202
203
|
name: RuntimeSchema,
|
package/dist/execution-runner.js
CHANGED
|
@@ -515,8 +515,9 @@ export async function runExecution(config, input, dependencies) {
|
|
|
515
515
|
projectContext = await cancellable(resolveProjectContext(projectSnapshot, dependencies.projectWorkspaceRegistry ?? createProjectRegistry(config.agentsRoot)), dependencies.cancellation);
|
|
516
516
|
sessionContextFingerprint = projectSessionContextFingerprint(spec.runtime.name, projectSnapshot);
|
|
517
517
|
}
|
|
518
|
+
const selfTraining = spec.instructions.maintenancePurpose === "self_training";
|
|
518
519
|
let recalledMemory = "";
|
|
519
|
-
if (spec.agent.memoryEnabled === true && dependencies.agentMemory !== undefined) {
|
|
520
|
+
if (!selfTraining && spec.agent.memoryEnabled === true && dependencies.agentMemory !== undefined) {
|
|
520
521
|
try {
|
|
521
522
|
recalledMemory = await cancellable(dependencies.agentMemory.recall(spec.agent.handle, spec.instructions.wakePrompt, spec.executionId, spec.agent.memoryAgentId ?? null), dependencies.cancellation);
|
|
522
523
|
}
|
|
@@ -775,7 +776,9 @@ export async function runExecution(config, input, dependencies) {
|
|
|
775
776
|
const baseSystemPromptBudget = spec.agent.abilityRelease === undefined
|
|
776
777
|
? systemPromptBudget
|
|
777
778
|
: Math.max(0, Math.min(systemPromptBudget, Math.max(32 * 1024, systemPromptBudget - 168 * 1024)));
|
|
778
|
-
const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget
|
|
779
|
+
const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget, {
|
|
780
|
+
...(selfTraining ? { isolateLocalMemory: true } : {}),
|
|
781
|
+
});
|
|
779
782
|
const boundedSystemPrompt = (context) => appendAgentMemoryContext(typeof systemPromptWithLocalFacts === "string"
|
|
780
783
|
? systemPromptWithLocalFacts
|
|
781
784
|
: systemPromptWithLocalFacts(context), recalledMemory, baseSystemPromptBudget);
|
|
@@ -820,12 +823,13 @@ export async function runExecution(config, input, dependencies) {
|
|
|
820
823
|
} : {}),
|
|
821
824
|
},
|
|
822
825
|
session: {
|
|
823
|
-
enabled: config.resume,
|
|
826
|
+
enabled: selfTraining ? false : config.resume,
|
|
824
827
|
warmMs: config.resumeWarmMs,
|
|
825
828
|
budgetTokens: config.sessionBudgetTokens,
|
|
826
829
|
softTokens: config.sessionSoftTokens,
|
|
827
830
|
maxTurns: config.sessionMaxTurns,
|
|
828
831
|
},
|
|
832
|
+
...(selfTraining ? { maintenancePurpose: "self_training" } : {}),
|
|
829
833
|
};
|
|
830
834
|
const result = await Promise.race([
|
|
831
835
|
execute(localInput, callbacks, localDependencies),
|
|
@@ -991,6 +995,7 @@ export async function runExecution(config, input, dependencies) {
|
|
|
991
995
|
}
|
|
992
996
|
await dependencies.journal.complete(spec.executionId, completion);
|
|
993
997
|
if (completion.outcome === "succeeded"
|
|
998
|
+
&& spec.instructions.maintenancePurpose !== "self_training"
|
|
994
999
|
&& spec.agent.memoryEnabled === true
|
|
995
1000
|
&& memoryCaptureFinalText !== null
|
|
996
1001
|
&& dependencies.agentMemory !== undefined) {
|
|
@@ -30,13 +30,14 @@ function takeCompletePromptSections(sections, maxBytes) {
|
|
|
30
30
|
}, { value: "", bytes: 0, full: true }).value;
|
|
31
31
|
}
|
|
32
32
|
/** v1 server instructions opt in to daemon-owned paths and bounded local workspace context. */
|
|
33
|
-
export function withLocalExecutionFacts(serverPrompt, maxBytes) {
|
|
33
|
+
export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
|
|
34
34
|
return ({ workspace, resuming, projectContext }) => {
|
|
35
|
-
const memory = capMemoryForInject(workspace.memory);
|
|
35
|
+
const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory);
|
|
36
36
|
const workLog = resuming ? "" : capWorkLogForInject(workspace.workLog);
|
|
37
37
|
const mandatoryWorkspacePrompt = buildRuntimeWorkspacePrompt(projectContext);
|
|
38
|
+
const crewHome = options.isolateLocalMemory ? workspace.runDir : workspace.dir;
|
|
38
39
|
const localFactSections = [`\n\n## Local execution facts
|
|
39
|
-
- $CREW_HOME: ${
|
|
40
|
+
- $CREW_HOME: ${crewHome}
|
|
40
41
|
- $CREW_TASK_LOG: ${workspace.workLogPath}`,
|
|
41
42
|
...(memory ? [`\n\n## Injected MEMORY.md (bounded local context)\n${memory}`] : []),
|
|
42
43
|
...(workLog ? [`\n\n## Injected work-log (bounded local context)\n${workLog}`] : []),
|
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";
|
|
@@ -96,14 +97,6 @@ function withoutClaudeInstructionFlag(env) {
|
|
|
96
97
|
const warnRuntimeLocally = (code) => {
|
|
97
98
|
process.stderr.write(`[runtime-warning] ${code}\n`);
|
|
98
99
|
};
|
|
99
|
-
export function sanitizeEnvVars(raw) {
|
|
100
|
-
if (!raw)
|
|
101
|
-
return {};
|
|
102
|
-
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
103
|
-
&& ENV_KEY_RE.test(key)
|
|
104
|
-
&& !key.toUpperCase().startsWith("CREW_")
|
|
105
|
-
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
106
|
-
}
|
|
107
100
|
export function awaitExit(child) {
|
|
108
101
|
return new Promise((resolve) => {
|
|
109
102
|
let settled = false;
|
|
@@ -154,6 +147,7 @@ async function launchLegacyRuntime(request) {
|
|
|
154
147
|
effectivePermission: request.effectivePermission,
|
|
155
148
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
156
149
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
150
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
157
151
|
};
|
|
158
152
|
if (request.runtime === "claude") {
|
|
159
153
|
const additionalDirectories = request.claudeAdditionalDirectories
|
|
@@ -243,6 +237,7 @@ export async function executeLocal(input, callbacks = {}, dependencies = {}) {
|
|
|
243
237
|
async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
244
238
|
const providerConfig = input.launch.providerConfig ?? {};
|
|
245
239
|
const { runtime } = input;
|
|
240
|
+
const isolatedMaintenance = input.maintenancePurpose === "self_training";
|
|
246
241
|
const isDeepSeekCodex = runtime.name === "codex" && providerConfig.provider === "deepseek";
|
|
247
242
|
const currentModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model ?? null;
|
|
248
243
|
const launchModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model;
|
|
@@ -267,6 +262,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
267
262
|
? {}
|
|
268
263
|
: { sessionContextFingerprint: input.sessionContextFingerprint }),
|
|
269
264
|
...(input.launch.description ? { description: input.launch.description } : {}),
|
|
265
|
+
...(isolatedMaintenance ? { skipLocalMemory: true } : {}),
|
|
270
266
|
}), dependencies.cancellation);
|
|
271
267
|
}
|
|
272
268
|
catch (error) {
|
|
@@ -295,12 +291,23 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
295
291
|
}
|
|
296
292
|
const sourceCodexHome = inheritedEnv.CODEX_HOME
|
|
297
293
|
?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
|
|
294
|
+
let materializedDeepSeekHome;
|
|
298
295
|
try {
|
|
299
296
|
if (isDeepSeekCodex && !providerConfig.providerApiKey) {
|
|
300
297
|
throw new Error("DeepSeek API key is not configured for this Agent");
|
|
301
298
|
}
|
|
302
299
|
if (isDeepSeekCodex) {
|
|
303
|
-
|
|
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);
|
|
304
311
|
}
|
|
305
312
|
const supportsNativeResume = runtime.name === "claude"
|
|
306
313
|
|| (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
|
|
@@ -405,29 +412,30 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
405
412
|
const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
|
|
406
413
|
const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
407
414
|
memoryPruneTraceId = parseMemoryPruneTraceId(wakePrompt);
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
+
}
|
|
431
439
|
if (memoryPruneTraceId !== null) {
|
|
432
440
|
memoryPruneSharedWriteKey = JSON.stringify([input.launch.agentsRoot, input.handle]);
|
|
433
441
|
const activePruneCount = (activeMemoryPrunes.get(memoryPruneSharedWriteKey) ?? 0) + 1;
|
|
@@ -461,6 +469,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
461
469
|
}
|
|
462
470
|
}
|
|
463
471
|
memoryPruneFailurePhase = "runtime_prepare";
|
|
472
|
+
const crewHome = isolatedMaintenance ? executionWorkspace.runDir : workspace.dir;
|
|
464
473
|
const baseEnv = {
|
|
465
474
|
...inheritedEnv,
|
|
466
475
|
...sanitizeEnvVars(providerConfig.envVars),
|
|
@@ -469,10 +478,10 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
469
478
|
CREW_SERVER_URL: input.launch.serverUrl,
|
|
470
479
|
CREW_TOKEN: input.launch.token,
|
|
471
480
|
CREW_CHANNEL: input.channelId,
|
|
472
|
-
CREW_HOME:
|
|
481
|
+
CREW_HOME: crewHome,
|
|
473
482
|
// Expose only the training root; ability-specific workspace layout stays
|
|
474
483
|
// owned by the training package rather than the NowWork daemon.
|
|
475
|
-
QA_RUNTIME_ROOT: join(
|
|
484
|
+
QA_RUNTIME_ROOT: join(crewHome, "training"),
|
|
476
485
|
CREW_TASK_DIR: executionWorkspace.runDir,
|
|
477
486
|
CREW_TASK_LOG: executionWorkspace.workLogPath,
|
|
478
487
|
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
@@ -488,7 +497,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
488
497
|
};
|
|
489
498
|
const providerEnv = applyProviderEnv(baseEnv, runtime.name, input.launch.codexHomeMode === undefined
|
|
490
499
|
? providerConfig
|
|
491
|
-
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir);
|
|
500
|
+
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir, materializedDeepSeekHome);
|
|
492
501
|
const childEnv = runtime.name === "deepseek-harness"
|
|
493
502
|
? applyDeepSeekHarnessMachineEnv(providerEnv, inheritedEnv)
|
|
494
503
|
: providerEnv;
|
|
@@ -499,7 +508,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
499
508
|
qa_runtime_root: childEnv.QA_RUNTIME_ROOT ?? null,
|
|
500
509
|
qa_runtime_root_present: childEnv.QA_RUNTIME_ROOT !== undefined,
|
|
501
510
|
inherited_qa_runtime_root_present: inheritedEnv.QA_RUNTIME_ROOT !== undefined,
|
|
502
|
-
training_root: join(
|
|
511
|
+
training_root: join(crewHome, "training"),
|
|
503
512
|
project_context_present: input.projectContext !== undefined,
|
|
504
513
|
project_ids: JSON.stringify(input.projectContext === undefined
|
|
505
514
|
? []
|
|
@@ -619,6 +628,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
619
628
|
workspaceRoots: Object.freeze(input.projectContext.secondary.map((project) => project.root)),
|
|
620
629
|
projectContext: input.projectContext,
|
|
621
630
|
}),
|
|
631
|
+
...(isolatedMaintenance && runtime.name === "codex" ? { networkAccess: true } : {}),
|
|
622
632
|
};
|
|
623
633
|
return launchRequest;
|
|
624
634
|
};
|
|
@@ -1065,5 +1075,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
1065
1075
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1066
1076
|
process.stderr.write(`[execution] failed to remove prompt ${workspace.systemPromptPath}: ${detail}\n`);
|
|
1067
1077
|
}
|
|
1078
|
+
if (isolatedMaintenance) {
|
|
1079
|
+
await cleanupSelfTrainingRunDir(join(workspace.dir, "tasks"), executionWorkspace.runDir);
|
|
1080
|
+
}
|
|
1068
1081
|
}
|
|
1069
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,
|
package/dist/provider-env.js
CHANGED
|
@@ -11,6 +11,24 @@
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { DEEPSEEK_CODEX_KEY_ENV, deepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
13
13
|
import { DEEPSEEK_HARNESS_CONFIG_ENV } from "./runtimes/deepseek-harness.js";
|
|
14
|
+
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15
|
+
const RESERVED_ENV = new Set([
|
|
16
|
+
"PATH",
|
|
17
|
+
"XDG_CONFIG_HOME",
|
|
18
|
+
"XDG_DATA_HOME",
|
|
19
|
+
"XDG_CACHE_HOME",
|
|
20
|
+
"GH_CONFIG_DIR",
|
|
21
|
+
"CLOUDSDK_CONFIG",
|
|
22
|
+
]);
|
|
23
|
+
/** Keep user-provided variables away from daemon-owned runtime and credential fields. */
|
|
24
|
+
export function sanitizeEnvVars(raw) {
|
|
25
|
+
if (!raw)
|
|
26
|
+
return {};
|
|
27
|
+
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
28
|
+
&& ENV_KEY_RE.test(key)
|
|
29
|
+
&& !key.toUpperCase().startsWith("CREW_")
|
|
30
|
+
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
31
|
+
}
|
|
14
32
|
/** custom 模式下必须从继承 env 中剔除的全局路由/凭证变量(避免与注入值叠加或抢优先级)。 */
|
|
15
33
|
const CONFLICTING_ENV = [
|
|
16
34
|
"ANTHROPIC_BASE_URL",
|
|
@@ -39,12 +57,12 @@ const CONFLICTING_CODEX_ENV = [
|
|
|
39
57
|
DEEPSEEK_CODEX_KEY_ENV,
|
|
40
58
|
];
|
|
41
59
|
/** 返回叠加了 provider 隔离的新 env(不改入参);不支持的 runtime/provider 组合原样返回。 */
|
|
42
|
-
export function applyProviderEnv(base, runtime, cfg, homeDir) {
|
|
60
|
+
export function applyProviderEnv(base, runtime, cfg, homeDir, deepSeekHomeOverride) {
|
|
43
61
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
44
62
|
const env = { ...base };
|
|
45
63
|
for (const key of CONFLICTING_CODEX_ENV)
|
|
46
64
|
delete env[key];
|
|
47
|
-
env.CODEX_HOME = deepSeekCodexHome(homeDir);
|
|
65
|
+
env.CODEX_HOME = deepSeekHomeOverride ?? deepSeekCodexHome(homeDir);
|
|
48
66
|
if (cfg.providerApiKey)
|
|
49
67
|
env[DEEPSEEK_CODEX_KEY_ENV] = cfg.providerApiKey;
|
|
50
68
|
return env;
|
|
@@ -100,7 +118,7 @@ export function applyDeepSeekHarnessMachineEnv(base, machineEnv) {
|
|
|
100
118
|
*/
|
|
101
119
|
export function providerFingerprint(runtime, cfg) {
|
|
102
120
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
103
|
-
return `deepseek|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
121
|
+
return `deepseek|${cfg.providerBaseUrl ?? "default"}|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
104
122
|
}
|
|
105
123
|
if (runtime !== "claude" || cfg.provider !== "custom")
|
|
106
124
|
return null;
|
package/dist/runner.js
CHANGED
|
@@ -10,7 +10,8 @@ import { readOriginDecisionFile, resetOriginDecisionFile } from "./origin-decisi
|
|
|
10
10
|
import { readBoundImDecisionFile, resetBoundImDecisionFile } from "./bound-im-decision.js";
|
|
11
11
|
import { launchSupervisedRuntime } from "./supervised-runtime.js";
|
|
12
12
|
import { awaitWithCancellation, } from "./runtime-cancellation.js";
|
|
13
|
-
export { awaitExit, exitActivity
|
|
13
|
+
export { awaitExit, exitActivity } from "./local-executor.js";
|
|
14
|
+
export { sanitizeEnvVars } from "./provider-env.js";
|
|
14
15
|
const ICON = {
|
|
15
16
|
init: "🟢", text: "💬", reading: "📖", sending: "📨", checking: "🔎",
|
|
16
17
|
claiming: "📌", crew: "⚙️", tool: "🛠️", tool_result: "↩️", done: "✅", error: "❌",
|
|
@@ -20,8 +20,13 @@ const RunnerInputSchema = z.object({
|
|
|
20
20
|
cwd: z.string().min(1).optional(),
|
|
21
21
|
workspaceRoots: z.array(z.string().min(1)).optional(),
|
|
22
22
|
skillRoots: z.array(z.string().min(1)).optional(),
|
|
23
|
+
networkAccess: z.boolean().optional(),
|
|
23
24
|
resume: z.boolean(),
|
|
24
|
-
}).strict()
|
|
25
|
+
}).strict().superRefine((input, ctx) => {
|
|
26
|
+
if (input.networkAccess === true && input.effectivePermission !== "workspace_write") {
|
|
27
|
+
ctx.addIssue({ code: "custom", message: "network_access_requires_workspace_write" });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
25
30
|
const RPC_TIMEOUT_MS = 30_000;
|
|
26
31
|
const INITIALIZE_RPC_TIMEOUT_MS = 60_000;
|
|
27
32
|
const ERROR_MESSAGE_CAP = 2_000;
|
|
@@ -560,6 +565,9 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
|
|
|
560
565
|
...(input.reasoning === undefined || input.reasoning === "default"
|
|
561
566
|
? {}
|
|
562
567
|
: { effort: input.reasoning }),
|
|
568
|
+
...(input.networkAccess === true ? {
|
|
569
|
+
sandboxPolicy: { type: "workspaceWrite", networkAccess: true },
|
|
570
|
+
} : {}),
|
|
563
571
|
});
|
|
564
572
|
logStage(activeStage, "ok", attempt, stageStartedAt);
|
|
565
573
|
turnId = started.turn.id;
|
|
@@ -7,10 +7,20 @@ export const DEEPSEEK_CODEX_BASE_URL = "https://api.deepseek.com/";
|
|
|
7
7
|
export const DEEPSEEK_CODEX_KEY_ENV = "NOWCREW_DEEPSEEK_API_KEY";
|
|
8
8
|
export const DEEPSEEK_CODEX_REASONING_LEVELS = ["low", "high", "max"];
|
|
9
9
|
const PRIVATE_CODEX_HOME = ".codex-deepseek";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const SELF_TRAINING_PRIVATE_CODEX_HOME = ".codex-deepseek-self-training";
|
|
11
|
+
export function deepSeekCodexHome(homeDir, selfTraining = false) {
|
|
12
|
+
return resolve(homeDir, selfTraining ? SELF_TRAINING_PRIVATE_CODEX_HOME : PRIVATE_CODEX_HOME);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
function normalizedBaseUrl(value) {
|
|
15
|
+
const url = new URL(value);
|
|
16
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
17
|
+
throw new Error("DeepSeek provider base URL must use HTTP(S)");
|
|
18
|
+
}
|
|
19
|
+
if (!url.pathname.endsWith("/"))
|
|
20
|
+
url.pathname = `${url.pathname}/`;
|
|
21
|
+
return url.toString();
|
|
22
|
+
}
|
|
23
|
+
export function renderDeepSeekConfig(codexHome, baseUrl = DEEPSEEK_CODEX_BASE_URL) {
|
|
14
24
|
const catalogPath = join(codexHome, "models.json");
|
|
15
25
|
return [
|
|
16
26
|
`model = ${JSON.stringify(DEEPSEEK_CODEX_MODEL)}`,
|
|
@@ -20,7 +30,7 @@ export function renderDeepSeekConfig(codexHome) {
|
|
|
20
30
|
"",
|
|
21
31
|
"[model_providers.deepseek]",
|
|
22
32
|
'name = "deepseek"',
|
|
23
|
-
`base_url = ${JSON.stringify(
|
|
33
|
+
`base_url = ${JSON.stringify(normalizedBaseUrl(baseUrl))}`,
|
|
24
34
|
'wire_api = "responses"',
|
|
25
35
|
`env_key = ${JSON.stringify(DEEPSEEK_CODEX_KEY_ENV)}`,
|
|
26
36
|
"",
|
|
@@ -40,11 +50,11 @@ async function writePrivateAtomic(path, content) {
|
|
|
40
50
|
await rm(temporary, { force: true });
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
|
-
export async function materializeDeepSeekCodexHome(homeDir) {
|
|
44
|
-
const codexHome = deepSeekCodexHome(homeDir);
|
|
53
|
+
export async function materializeDeepSeekCodexHome(homeDir, options = {}) {
|
|
54
|
+
const codexHome = deepSeekCodexHome(homeDir, options.selfTraining === true);
|
|
45
55
|
await mkdir(codexHome, { recursive: true, mode: 0o700 });
|
|
46
56
|
await chmod(codexHome, 0o700);
|
|
47
57
|
await writePrivateAtomic(join(codexHome, "models.json"), DEEPSEEK_CODEX_CATALOG);
|
|
48
|
-
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome));
|
|
58
|
+
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome, options.baseUrl));
|
|
49
59
|
return codexHome;
|
|
50
60
|
}
|
package/dist/runtimes/codex.js
CHANGED
|
@@ -32,6 +32,9 @@ export function buildCodexArgs(input) {
|
|
|
32
32
|
else if (input.effectivePermission === "full_access" || (input.effectivePermission === undefined && input.dangerous)) {
|
|
33
33
|
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
34
34
|
}
|
|
35
|
+
if (input.effectivePermission === "workspace_write" && input.networkAccess === true) {
|
|
36
|
+
args.push("-c", "sandbox_workspace_write.network_access=true");
|
|
37
|
+
}
|
|
35
38
|
for (const imagePath of input.imagePaths ?? [])
|
|
36
39
|
args.push("--image", imagePath);
|
|
37
40
|
// `-` instructs codex exec to read the prompt from stdin. Keeping the complete prompt out of argv
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, relative, sep } from "node:path";
|
|
3
|
+
/** Delete only a concrete child of this Agent's tasks root; never follow an ambiguous target. */
|
|
4
|
+
export async function cleanupSelfTrainingRunDir(tasksRoot, runDir) {
|
|
5
|
+
const child = relative(tasksRoot, runDir);
|
|
6
|
+
if (child === "" || isAbsolute(child) || child === ".." || child.startsWith(`..${sep}`))
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
await rm(runDir, { recursive: true, force: true });
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
13
|
+
process.stderr.write(`[execution] failed to remove self-training run dir ${runDir}: ${detail}\n`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -46,6 +46,7 @@ export function supervisorLaunch(request) {
|
|
|
46
46
|
effectivePermission: request.effectivePermission,
|
|
47
47
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
48
48
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
49
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
49
50
|
...(request.sessionId === undefined ? {} : { sessionId: request.sessionId }),
|
|
50
51
|
...(request.imagePaths === undefined ? {} : { imagePaths: request.imagePaths }),
|
|
51
52
|
...(request.agentRoot === undefined ? {} : {
|
package/dist/workspace.js
CHANGED
|
@@ -39,11 +39,12 @@ export async function prepareWorkspace(input) {
|
|
|
39
39
|
const crewDir = join(dir, ".crew");
|
|
40
40
|
await mkdir(crewDir, { recursive: true });
|
|
41
41
|
// 预先创建明细目录,让收尾流程可以按功能创建 notes/<topic>.md。
|
|
42
|
-
|
|
42
|
+
if (!input.skipLocalMemory)
|
|
43
|
+
await mkdir(join(dir, "notes"), { recursive: true });
|
|
43
44
|
// MEMORY.md:首次创建"持久身份 + 知识索引"骨架,之后由 agent 自己维护
|
|
44
45
|
const memoryPath = join(dir, "MEMORY.md");
|
|
45
|
-
const memorySeedCreated = !(await exists(memoryPath));
|
|
46
|
-
if (memorySeedCreated) {
|
|
46
|
+
const memorySeedCreated = !input.skipLocalMemory && !(await exists(memoryPath));
|
|
47
|
+
if (!input.skipLocalMemory && memorySeedCreated) {
|
|
47
48
|
try {
|
|
48
49
|
await writeFile(memoryPath, memorySeed(input.handle, input.description), "utf8");
|
|
49
50
|
}
|
|
@@ -52,13 +53,15 @@ export async function prepareWorkspace(input) {
|
|
|
52
53
|
throw error;
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
let memory;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
let memory = "";
|
|
57
|
+
if (!input.skipLocalMemory) {
|
|
58
|
+
try {
|
|
59
|
+
memory = await readFile(memoryPath, "utf8");
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
logMemoryPrepareFailure(input, "memory_read", error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
62
65
|
}
|
|
63
66
|
// 系统提示词按 execution 隔离;省略 identity 的兼容调用每次使用随机路径。
|
|
64
67
|
const promptDir = join(crewDir, "prompts");
|