@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.
@@ -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,
@@ -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: ${workspace.dir}
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}`] : []),
@@ -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
- await awaitWithCancellation((dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome)(workspace.homeDir), dependencies.cancellation);
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
- try {
409
- localMemoryTelemetry = createLocalMemoryTelemetry({
410
- executionId: input.executionId,
411
- agentHandle: input.handle,
412
- ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
413
- runtime: runtime.name,
414
- stableProtocolRuntime: dependencies.launchRuntime !== undefined,
415
- agentDir: executionWorkspace.dir,
416
- runDir: executionWorkspace.runDir,
417
- isMemoryPrune: memoryPruneTraceId !== null,
418
- diagnosticsTimeoutMs: dependencies.localMemoryDiagnosticsTimeoutMs
419
- ?? LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS,
420
- });
421
- await localMemoryTelemetry.captureBefore();
422
- }
423
- catch (error) {
424
- localMemoryTelemetry = null;
425
- logLocalMemoryDiagnosticsFailure({
426
- executionId: input.executionId,
427
- agentHandle: input.handle,
428
- ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
429
- }, "initialize", error);
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: workspace.dir,
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(workspace.dir, "training"),
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(workspace.dir, "training"),
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
  }
@@ -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,
@@ -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, sanitizeEnvVars } from "./local-executor.js";
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
- export function deepSeekCodexHome(homeDir) {
11
- return resolve(homeDir, PRIVATE_CODEX_HOME);
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
- export function renderDeepSeekConfig(codexHome) {
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(DEEPSEEK_CODEX_BASE_URL)}`,
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
  }
@@ -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
- await mkdir(join(dir, "notes"), { recursive: true });
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
- try {
57
- memory = await readFile(memoryPath, "utf8");
58
- }
59
- catch (error) {
60
- logMemoryPrepareFailure(input, "memory_read", error);
61
- throw error;
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");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.66",
3
+ "version": "0.6.68",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",