@nowcrew/daemon 0.6.80-beta.1 → 0.6.80-beta.3
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/resolver.js +10 -7
- package/dist/computer-cli.js +3 -9
- package/dist/computer-service.js +14 -0
- package/dist/daemon-local-home.js +23 -0
- package/dist/daemon-update-eligibility.js +7 -4
- package/dist/execution-protocol.js +1 -0
- package/dist/execution-runner.js +9 -3
- package/dist/local-execution-prompt.js +3 -2
- package/dist/local-executor.js +49 -28
- package/dist/machine-info.js +3 -0
- package/dist/main.js +5 -9
- package/dist/managed-service-lifecycle.js +6 -2
- package/dist/project-skills/capability.js +109 -0
- package/dist/project-skills/controller-convergence.js +57 -0
- package/dist/project-skills/projection-set-switch.js +419 -0
- package/dist/project-skills/projection-state-domain.js +170 -0
- package/dist/project-skills/projection-state-store.js +842 -0
- package/dist/project-skills/projection-state-transaction.js +322 -0
- package/dist/project-skills/projection-state.js +3 -0
- package/dist/project-skills/runtime-root-bootstrap.js +47 -0
- package/dist/project-skills/runtime-root-domain.js +268 -0
- package/dist/project-skills/runtime-root-gc.js +293 -0
- package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
- package/dist/project-skills/runtime-root-leases.js +487 -0
- package/dist/project-skills/runtime-root-source-identity.js +60 -0
- package/dist/project-skills/runtime-root-startup.js +49 -0
- package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
- package/dist/project-skills/runtime-root-state-index.js +356 -0
- package/dist/project-skills/runtime-root-store.js +722 -0
- package/dist/project-skills/serve-capability.js +28 -0
- package/dist/project-skills/serve-startup.js +37 -0
- package/dist/provider-env.js +21 -3
- package/dist/runtimes/codex-app-server-runner.js +12 -4
- 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/serve.js +1 -1
- package/dist/supervised-runtime.js +1 -0
- package/dist/workspace.js +13 -10
- package/package.json +9 -10
|
@@ -10,8 +10,8 @@ import { PROJECT_MANIFEST_SOURCE_PATH, ProjectDependencyManifestSchema, } from "
|
|
|
10
10
|
import { normalizeRepositoryIdentity } from "../repository-identity.js";
|
|
11
11
|
const runFile = promisify(execFile);
|
|
12
12
|
const MAX_FILES = 5_000;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
export const AGENT_ABILITY_MAX_TOTAL_BYTES = 200 * 1024 * 1024;
|
|
14
|
+
export const AGENT_ABILITY_MAX_FILE_BYTES = 100 * 1024 * 1024;
|
|
15
15
|
const MAX_INSTRUCTIONS_BYTES = 96 * 1024;
|
|
16
16
|
const MAX_MEMORY_BYTES = 64 * 1024;
|
|
17
17
|
const AUTHORING_ONLY_ROOTS = new Set([".claude"]);
|
|
@@ -125,10 +125,11 @@ export const collectAbilityFiles = async (root) => {
|
|
|
125
125
|
if (info.isDirectory())
|
|
126
126
|
await visit(absolute);
|
|
127
127
|
else if (info.isFile()) {
|
|
128
|
-
if (info.size >
|
|
128
|
+
if (info.size > AGENT_ABILITY_MAX_FILE_BYTES) {
|
|
129
129
|
throw new Error(`ability_file_size_exceeded:${relative(root, absolute)}`);
|
|
130
|
+
}
|
|
130
131
|
totalBytes += info.size;
|
|
131
|
-
if (totalBytes >
|
|
132
|
+
if (totalBytes > AGENT_ABILITY_MAX_TOTAL_BYTES)
|
|
132
133
|
throw new Error("ability_total_size_exceeded");
|
|
133
134
|
if (files.length >= MAX_FILES)
|
|
134
135
|
throw new Error("ability_file_count_exceeded");
|
|
@@ -262,7 +263,7 @@ export const validateAbilityDependencyDeclarations = (manifest, lock) => {
|
|
|
262
263
|
throw new Error("ability_lock_has_undeclared_dependency");
|
|
263
264
|
}
|
|
264
265
|
};
|
|
265
|
-
const
|
|
266
|
+
export const scanAbilityFiles = (files, manifest) => {
|
|
266
267
|
const findings = [];
|
|
267
268
|
const secrets = [
|
|
268
269
|
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/u,
|
|
@@ -270,7 +271,9 @@ const scan = (files, manifest) => {
|
|
|
270
271
|
/\bAK(?:ID|IA)[A-Z0-9]{12,}\b/u,
|
|
271
272
|
];
|
|
272
273
|
const executable = new Set([".exe", ".dll", ".dylib", ".so", ".bin", ".class", ".jar"]);
|
|
273
|
-
const allowedBinaryDocuments = new Set([
|
|
274
|
+
const allowedBinaryDocuments = new Set([
|
|
275
|
+
".pdf", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".xlsx", ".xls", ".docx", ".pptx", ".ttf", ".otf",
|
|
276
|
+
]);
|
|
274
277
|
for (const file of files) {
|
|
275
278
|
let text;
|
|
276
279
|
try {
|
|
@@ -396,7 +399,7 @@ export async function resolveAbilityRelease(agentsRoot, command) {
|
|
|
396
399
|
}
|
|
397
400
|
const externalPaths = await verifyExternalDependencies(cacheRoot, resolved.root, lock);
|
|
398
401
|
files = await collectAbilityFiles(resolved.root);
|
|
399
|
-
const security =
|
|
402
|
+
const security = scanAbilityFiles(files, manifest);
|
|
400
403
|
const kinds = new Set(lock.assets.map((asset) => asset.kind));
|
|
401
404
|
const memory = [];
|
|
402
405
|
for (const [layer, mounts] of [["release-policy", manifest.spec.memory.policy], ["release-seed", manifest.spec.memory.seeds]]) {
|
package/dist/computer-cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { posix, resolve, win32 } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { assertProfileAgentsRootUnique, daemonHome, inspectProfileAgentsRoot, listProfiles, loadProfile, PROFILE_AGENTS_ROOT_CONFLICT_MESSAGE, ProfileAgentsRootConflictError, profileIsPrivate, profilePath, publicProfile, removeProfile, resolveAgentsRoot, saveProfile, windowsDpapiProtector, } from "./computer-profile.js";
|
|
6
|
-
import { buildServiceSpec, doctorService, hardenWindowsProfile, installService, readServiceDescriptor, serviceAction, serviceStatus, systemCommandRunner, uninstallService, upgradeDaemon, windowsProfileIsPrivate, } from "./computer-service.js";
|
|
6
|
+
import { buildServiceSpec, doctorService, hardenWindowsProfile, installService, readServiceDescriptor, serviceAction, serviceStatus, systemCommandRunner, uninstallService, upgradeDaemon, windowsProfileIsPrivate, storageServiceEnvironment, } from "./computer-service.js";
|
|
7
7
|
import { releaseTagForServerUrl, assertReleaseTagForServerUrl } from "./automatic-install-target.js";
|
|
8
8
|
import { normalizeDaemonNpmRegistry } from "./npm-registry.js";
|
|
9
9
|
import { npmInvocation } from "./npm-command.js";
|
|
@@ -89,14 +89,6 @@ function translatedError(error, td) {
|
|
|
89
89
|
}
|
|
90
90
|
return td(error.message);
|
|
91
91
|
}
|
|
92
|
-
function storageServiceEnvironment(env) {
|
|
93
|
-
return {
|
|
94
|
-
...(env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: env.NOWWORK_HOME }),
|
|
95
|
-
...(env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: env.NOWWORK_ENV_TAG }),
|
|
96
|
-
...(env.NOWWORK_STORAGE_POLICY_PATH === undefined
|
|
97
|
-
? {} : { NOWWORK_STORAGE_POLICY_PATH: env.NOWWORK_STORAGE_POLICY_PATH }),
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
92
|
export async function runComputerCommand(argv, overrides = {}) {
|
|
101
93
|
const command = argv[0];
|
|
102
94
|
if (!command || !COMPUTER_COMMANDS.has(command))
|
|
@@ -192,6 +184,8 @@ export async function runComputerCommand(argv, overrides = {}) {
|
|
|
192
184
|
platform: deps.platform,
|
|
193
185
|
explicit: deps.env.NOWWORK_LOCAL_HOME,
|
|
194
186
|
mode: configuredLocalHomeMode,
|
|
187
|
+
...(resolvedProfile === undefined ? {} : { profile: resolvedProfile }),
|
|
188
|
+
...(home === undefined ? {} : { daemonHome: home }),
|
|
195
189
|
});
|
|
196
190
|
const profileStorage = {
|
|
197
191
|
platform: deps.platform,
|
package/dist/computer-service.js
CHANGED
|
@@ -32,6 +32,18 @@ export const systemCommandRunner = async (command, args) => {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
|
+
/** Build the non-secret storage environment persisted in managed services. */
|
|
36
|
+
export function storageServiceEnvironment(env) {
|
|
37
|
+
return {
|
|
38
|
+
...(env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: env.NOWWORK_HOME }),
|
|
39
|
+
...(env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: env.NOWWORK_ENV_TAG }),
|
|
40
|
+
...(env.NOWWORK_STORAGE_POLICY_PATH === undefined
|
|
41
|
+
? {} : { NOWWORK_STORAGE_POLICY_PATH: env.NOWWORK_STORAGE_POLICY_PATH }),
|
|
42
|
+
...(env.NOWWORK_LOCAL_HOME === undefined ? {} : { NOWWORK_LOCAL_HOME: env.NOWWORK_LOCAL_HOME }),
|
|
43
|
+
...(env.NOWWORK_LOCAL_HOME_MODE === undefined && env.NOWWORK_STORAGE_MODE === undefined
|
|
44
|
+
? {} : { NOWWORK_LOCAL_HOME_MODE: env.NOWWORK_LOCAL_HOME_MODE ?? env.NOWWORK_STORAGE_MODE }),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
35
47
|
function serviceIdentitySuffix(profileHome, userHome) {
|
|
36
48
|
if (profileHome === undefined || resolve(profileHome) === resolve(userHome, ".crew", "daemon"))
|
|
37
49
|
return "";
|
|
@@ -80,6 +92,8 @@ export function buildServiceSpec(input) {
|
|
|
80
92
|
["NOWWORK_HOME", environment.NOWWORK_HOME],
|
|
81
93
|
["NOWWORK_ENV_TAG", environment.NOWWORK_ENV_TAG],
|
|
82
94
|
["NOWWORK_STORAGE_POLICY_PATH", environment.NOWWORK_STORAGE_POLICY_PATH],
|
|
95
|
+
["NOWWORK_LOCAL_HOME", environment.NOWWORK_LOCAL_HOME],
|
|
96
|
+
["NOWWORK_LOCAL_HOME_MODE", environment.NOWWORK_LOCAL_HOME_MODE],
|
|
83
97
|
].flatMap(([key, value]) => value === undefined
|
|
84
98
|
? []
|
|
85
99
|
: [` <key>${key}</key><string>${xml(assertSingleLine(value, key))}</string>`]);
|
|
@@ -4,6 +4,13 @@ import { posix, win32 } from "node:path";
|
|
|
4
4
|
const PROFILE = /^[a-z0-9][a-z0-9_-]{0,47}$/u;
|
|
5
5
|
const CONTROL = /[\0\r\n]/u;
|
|
6
6
|
const pathsFor = (platform) => platform === "win32" ? win32 : posix;
|
|
7
|
+
function samePath(left, right, platform) {
|
|
8
|
+
const normalizedLeft = pathsFor(platform).resolve(left);
|
|
9
|
+
const normalizedRight = pathsFor(platform).resolve(right);
|
|
10
|
+
return platform === "win32"
|
|
11
|
+
? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase()
|
|
12
|
+
: normalizedLeft === normalizedRight;
|
|
13
|
+
}
|
|
7
14
|
export function parseDaemonLocalHomeMode(value) {
|
|
8
15
|
if (value === undefined || value.trim() === "")
|
|
9
16
|
return "auto";
|
|
@@ -86,6 +93,22 @@ export function resolveDaemonLocalStateRoot(input) {
|
|
|
86
93
|
return legacyRoot;
|
|
87
94
|
if (input.explicit !== undefined || mode === "nowwork")
|
|
88
95
|
return nowworkRoot;
|
|
96
|
+
// Managed service descriptors created before local-home was persisted carry
|
|
97
|
+
// only --daemon-home. Do not treat every --daemon-home as legacy: migrated
|
|
98
|
+
// services also use that argument, but point it at ~/.nowwork/config.
|
|
99
|
+
// Prefer the descriptor's exact home when it identifies either layout.
|
|
100
|
+
if (input.daemonHome !== undefined && input.profile !== undefined) {
|
|
101
|
+
const nowworkDaemonHome = resolution("nowwork", nowworkRoot, input.profile, input.platform, "auto_nowwork_existing").daemonHome;
|
|
102
|
+
const legacyDaemonHome = resolution("legacy", legacyRoot, input.profile, input.platform, "auto_legacy_existing").daemonHome;
|
|
103
|
+
if (samePath(input.daemonHome, nowworkDaemonHome, input.platform))
|
|
104
|
+
return nowworkRoot;
|
|
105
|
+
if (samePath(input.daemonHome, legacyDaemonHome, input.platform))
|
|
106
|
+
return legacyRoot;
|
|
107
|
+
// An arbitrary explicit daemon home has historically implied the legacy
|
|
108
|
+
// local state root. Preserve that behavior unless the descriptor declares
|
|
109
|
+
// a mode explicitly.
|
|
110
|
+
return legacyRoot;
|
|
111
|
+
}
|
|
89
112
|
const exists = input.exists ?? existsSync;
|
|
90
113
|
// Once the new root exists, shared state must follow it even while the
|
|
91
114
|
// legacy root is retained as a rollback source.
|
|
@@ -4,7 +4,7 @@ import { realpathSync } from "node:fs";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { daemonHome, loadProfile, resolveAgentsRoot } from "./computer-profile.js";
|
|
6
6
|
import { builtDaemonEntry } from "./computer-cli.js";
|
|
7
|
-
import { buildServiceSpec, readServiceDescriptor, serviceStatus, } from "./computer-service.js";
|
|
7
|
+
import { buildServiceSpec, readServiceDescriptor, storageServiceEnvironment, serviceStatus, } from "./computer-service.js";
|
|
8
8
|
import { daemonGlobalInstallation } from "./daemon-installation.js";
|
|
9
9
|
import { daemonInstallationLeaseHeld } from "./daemon-installation-lease.js";
|
|
10
10
|
import { inspectLegacyServiceInstallation } from "./legacy-service-installation.js";
|
|
@@ -21,9 +21,9 @@ export async function managedDaemonCapabilities(eligibility) {
|
|
|
21
21
|
return [];
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
function defaults(profileName) {
|
|
24
|
+
function defaults(profileName, selectedLocalHome) {
|
|
25
25
|
const userHome = homedir();
|
|
26
|
-
const localHome = resolveDaemonLocalStateRoot({
|
|
26
|
+
const localHome = selectedLocalHome ?? resolveDaemonLocalStateRoot({
|
|
27
27
|
userHome,
|
|
28
28
|
platform: process.platform,
|
|
29
29
|
explicit: process.env.NOWWORK_LOCAL_HOME,
|
|
@@ -31,6 +31,7 @@ function defaults(profileName) {
|
|
|
31
31
|
});
|
|
32
32
|
return {
|
|
33
33
|
platform: process.platform,
|
|
34
|
+
localHome,
|
|
34
35
|
profileHome: daemonHome(process.env, profileName),
|
|
35
36
|
userHome,
|
|
36
37
|
uid: process.getuid?.(),
|
|
@@ -65,7 +66,7 @@ async function registryCoversUpdateScope(services, descriptorPaths, targetDescri
|
|
|
65
66
|
return true;
|
|
66
67
|
}
|
|
67
68
|
export async function detectDaemonUpdateEligibility(profileName, overrides = {}) {
|
|
68
|
-
const deps = { ...defaults(profileName), ...overrides };
|
|
69
|
+
const deps = { ...defaults(profileName, overrides.localHome), ...overrides };
|
|
69
70
|
if (deps.platform !== "darwin" && deps.platform !== "linux" && deps.platform !== "win32") {
|
|
70
71
|
return { eligible: false, reason: "unsupported_platform" };
|
|
71
72
|
}
|
|
@@ -92,6 +93,7 @@ export async function detectDaemonUpdateEligibility(profileName, overrides = {})
|
|
|
92
93
|
nodePath: deps.nodePath,
|
|
93
94
|
entryPath: deps.entryPath,
|
|
94
95
|
profileHome: deps.profileHome,
|
|
96
|
+
environment: storageServiceEnvironment(process.env),
|
|
95
97
|
});
|
|
96
98
|
if (deps.platform === "win32") {
|
|
97
99
|
const scope = await deps.inspectWindowsServiceUpdateScope(spec, installation.npmPrefix)
|
|
@@ -169,6 +171,7 @@ export async function detectDaemonUpdateEligibility(profileName, overrides = {})
|
|
|
169
171
|
entryPath: deps.entryPath,
|
|
170
172
|
profileHome: deps.profileHome,
|
|
171
173
|
legacyServiceId: true,
|
|
174
|
+
environment: storageServiceEnvironment(process.env),
|
|
172
175
|
});
|
|
173
176
|
const legacyDescriptor = await deps.readServiceDescriptor(legacySpec).catch(() => null);
|
|
174
177
|
if (legacyDescriptor !== legacySpec.descriptor) {
|
|
@@ -200,6 +200,7 @@ export const ExecutionStartSchema = z.object({
|
|
|
200
200
|
language: z.enum(["zh", "en"]),
|
|
201
201
|
systemPrompt: z.string(),
|
|
202
202
|
wakePrompt: z.string(),
|
|
203
|
+
maintenancePurpose: z.literal("self_training").optional(),
|
|
203
204
|
}).strict(),
|
|
204
205
|
runtime: z.object({
|
|
205
206
|
name: RuntimeSchema,
|
package/dist/execution-runner.js
CHANGED
|
@@ -567,8 +567,9 @@ export async function runExecution(config, input, dependencies) {
|
|
|
567
567
|
projectContext = await cancellable(resolveProjectContext(projectSnapshot, projectWorkspaceRegistry), dependencies.cancellation);
|
|
568
568
|
sessionContextFingerprint = projectSessionContextFingerprint(spec.runtime.name, projectSnapshot);
|
|
569
569
|
}
|
|
570
|
+
const selfTraining = spec.instructions.maintenancePurpose === "self_training";
|
|
570
571
|
let recalledMemory = "";
|
|
571
|
-
if (spec.agent.memoryEnabled === true && dependencies.agentMemory !== undefined) {
|
|
572
|
+
if (!selfTraining && spec.agent.memoryEnabled === true && dependencies.agentMemory !== undefined) {
|
|
572
573
|
try {
|
|
573
574
|
recalledMemory = await cancellable(dependencies.agentMemory.recall(spec.agent.handle, spec.instructions.wakePrompt, spec.executionId, spec.agent.memoryAgentId ?? null), dependencies.cancellation);
|
|
574
575
|
}
|
|
@@ -827,7 +828,10 @@ export async function runExecution(config, input, dependencies) {
|
|
|
827
828
|
const baseSystemPromptBudget = spec.agent.abilityRelease === undefined
|
|
828
829
|
? systemPromptBudget
|
|
829
830
|
: Math.max(0, Math.min(systemPromptBudget, Math.max(32 * 1024, systemPromptBudget - 168 * 1024)));
|
|
830
|
-
const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget, {
|
|
831
|
+
const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget, {
|
|
832
|
+
...(selfTraining ? { isolateLocalMemory: true } : {}),
|
|
833
|
+
collaborationTools: config.codexCollaborationTools && spec.runtime.name === "codex",
|
|
834
|
+
});
|
|
831
835
|
const boundedSystemPrompt = (context) => appendAgentMemoryContext(typeof systemPromptWithLocalFacts === "string"
|
|
832
836
|
? systemPromptWithLocalFacts
|
|
833
837
|
: systemPromptWithLocalFacts(context), recalledMemory, baseSystemPromptBudget);
|
|
@@ -876,12 +880,13 @@ export async function runExecution(config, input, dependencies) {
|
|
|
876
880
|
} : {}),
|
|
877
881
|
},
|
|
878
882
|
session: {
|
|
879
|
-
enabled: config.resume,
|
|
883
|
+
enabled: selfTraining ? false : config.resume,
|
|
880
884
|
warmMs: config.resumeWarmMs,
|
|
881
885
|
budgetTokens: config.sessionBudgetTokens,
|
|
882
886
|
softTokens: config.sessionSoftTokens,
|
|
883
887
|
maxTurns: config.sessionMaxTurns,
|
|
884
888
|
},
|
|
889
|
+
...(selfTraining ? { maintenancePurpose: "self_training" } : {}),
|
|
885
890
|
localMemoryV2Enabled: config.localMemoryV2Handles?.has(spec.agent.handle) === true,
|
|
886
891
|
};
|
|
887
892
|
const result = await Promise.race([
|
|
@@ -1048,6 +1053,7 @@ export async function runExecution(config, input, dependencies) {
|
|
|
1048
1053
|
}
|
|
1049
1054
|
await dependencies.journal.complete(spec.executionId, completion);
|
|
1050
1055
|
if (completion.outcome === "succeeded"
|
|
1056
|
+
&& spec.instructions.maintenancePurpose !== "self_training"
|
|
1051
1057
|
&& spec.agent.memoryEnabled === true
|
|
1052
1058
|
&& memoryCaptureFinalText !== null
|
|
1053
1059
|
&& dependencies.agentMemory !== undefined) {
|
|
@@ -47,12 +47,13 @@ function takeCompletePromptSections(sections, maxBytes) {
|
|
|
47
47
|
/** v1 server instructions opt in to daemon-owned paths and bounded local workspace context. */
|
|
48
48
|
export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
|
|
49
49
|
return ({ workspace, resuming, projectContext, trainingProjectContext }) => {
|
|
50
|
-
const memory = capMemoryForInject(workspace.memory);
|
|
50
|
+
const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory);
|
|
51
51
|
const workLog = resuming ? "" : capWorkLogForInject(workspace.workLog);
|
|
52
52
|
const collaborationPrompt = options.collaborationTools === true ? buildCollaborationToolsPrompt() : "";
|
|
53
53
|
const mandatoryWorkspacePrompt = `${buildRuntimeWorkspacePrompt(projectContext)}${buildTrainingGitProjectPrompt(trainingProjectContext)}`;
|
|
54
|
+
const crewHome = options.isolateLocalMemory ? workspace.runDir : workspace.dir;
|
|
54
55
|
const localFactSections = [`\n\n## Local execution facts
|
|
55
|
-
- $CREW_HOME: ${
|
|
56
|
+
- $CREW_HOME: ${crewHome}
|
|
56
57
|
- $CREW_TASK_LOG: ${workspace.workLogPath}`,
|
|
57
58
|
...(memory ? [`\n\n## Injected MEMORY.md (bounded local context)\n${memory}`] : []),
|
|
58
59
|
...(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";
|
|
@@ -111,6 +112,7 @@ async function launchLegacyRuntime(request) {
|
|
|
111
112
|
effectivePermission: request.effectivePermission,
|
|
112
113
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
113
114
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
115
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
114
116
|
};
|
|
115
117
|
if (request.runtime === "claude") {
|
|
116
118
|
const additionalDirectories = request.claudeAdditionalDirectories
|
|
@@ -200,6 +202,7 @@ export async function executeLocal(input, callbacks = {}, dependencies = {}) {
|
|
|
200
202
|
async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
201
203
|
const providerConfig = input.launch.providerConfig ?? {};
|
|
202
204
|
const { runtime } = input;
|
|
205
|
+
const isolatedMaintenance = input.maintenancePurpose === "self_training";
|
|
203
206
|
const isDeepSeekCodex = runtime.name === "codex" && providerConfig.provider === "deepseek";
|
|
204
207
|
const currentModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model ?? null;
|
|
205
208
|
const launchModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model;
|
|
@@ -224,6 +227,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
224
227
|
? {}
|
|
225
228
|
: { sessionContextFingerprint: input.sessionContextFingerprint }),
|
|
226
229
|
...(input.launch.description ? { description: input.launch.description } : {}),
|
|
230
|
+
...(isolatedMaintenance ? { skipLocalMemory: true } : {}),
|
|
227
231
|
}), dependencies.cancellation);
|
|
228
232
|
}
|
|
229
233
|
catch (error) {
|
|
@@ -254,12 +258,23 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
254
258
|
}
|
|
255
259
|
const sourceCodexHome = inheritedEnv.CODEX_HOME
|
|
256
260
|
?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
|
|
261
|
+
let materializedDeepSeekHome;
|
|
257
262
|
try {
|
|
258
263
|
if (isDeepSeekCodex && !providerConfig.providerApiKey) {
|
|
259
264
|
throw new Error("DeepSeek API key is not configured for this Agent");
|
|
260
265
|
}
|
|
261
266
|
if (isDeepSeekCodex) {
|
|
262
|
-
|
|
267
|
+
const providerBaseUrl = providerConfig.providerBaseUrl
|
|
268
|
+
? new URL(providerConfig.providerBaseUrl, input.launch.serverUrl).toString()
|
|
269
|
+
: undefined;
|
|
270
|
+
const materialize = dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome;
|
|
271
|
+
const materializeOptions = {
|
|
272
|
+
...(providerBaseUrl === undefined ? {} : { baseUrl: providerBaseUrl }),
|
|
273
|
+
...(isolatedMaintenance ? { selfTraining: true } : {}),
|
|
274
|
+
};
|
|
275
|
+
materializedDeepSeekHome = await awaitWithCancellation(providerBaseUrl === undefined && !isolatedMaintenance
|
|
276
|
+
? materialize(workspace.homeDir)
|
|
277
|
+
: materialize(workspace.homeDir, materializeOptions), dependencies.cancellation);
|
|
263
278
|
}
|
|
264
279
|
const supportsNativeResume = runtime.name === "claude"
|
|
265
280
|
|| (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
|
|
@@ -387,29 +402,30 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
387
402
|
const systemPrompt = localMemoryResultPath === null
|
|
388
403
|
? baseSystemPrompt
|
|
389
404
|
: `${baseSystemPrompt}${structuredMemoryInstructions ?? ""}`;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
405
|
+
if (!isolatedMaintenance)
|
|
406
|
+
try {
|
|
407
|
+
localMemoryTelemetry = createLocalMemoryTelemetry({
|
|
408
|
+
executionId: input.executionId,
|
|
409
|
+
agentHandle: input.handle,
|
|
410
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
411
|
+
runtime: runtime.name,
|
|
412
|
+
stableProtocolRuntime: dependencies.launchRuntime !== undefined,
|
|
413
|
+
agentDir: executionWorkspace.dir,
|
|
414
|
+
runDir: executionWorkspace.runDir,
|
|
415
|
+
isMemoryPrune: memoryPruneTraceId !== null,
|
|
416
|
+
diagnosticsTimeoutMs: dependencies.localMemoryDiagnosticsTimeoutMs
|
|
417
|
+
?? LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS,
|
|
418
|
+
});
|
|
419
|
+
await localMemoryTelemetry.captureBefore();
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
localMemoryTelemetry = null;
|
|
423
|
+
logLocalMemoryDiagnosticsFailure({
|
|
424
|
+
executionId: input.executionId,
|
|
425
|
+
agentHandle: input.handle,
|
|
426
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
427
|
+
}, "initialize", error);
|
|
428
|
+
}
|
|
413
429
|
if (memoryPruneTraceId !== null) {
|
|
414
430
|
memoryPruneSharedWriteKey = JSON.stringify([input.launch.agentsRoot, input.handle]);
|
|
415
431
|
const activePruneCount = (activeMemoryPrunes.get(memoryPruneSharedWriteKey) ?? 0) + 1;
|
|
@@ -443,6 +459,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
443
459
|
}
|
|
444
460
|
}
|
|
445
461
|
memoryPruneFailurePhase = "runtime_prepare";
|
|
462
|
+
const crewHome = isolatedMaintenance ? executionWorkspace.runDir : workspace.dir;
|
|
446
463
|
const baseEnv = {
|
|
447
464
|
...inheritedEnv,
|
|
448
465
|
...sanitizeEnvVars(providerConfig.envVars),
|
|
@@ -454,7 +471,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
454
471
|
CREW_SERVER_URL: input.launch.serverUrl,
|
|
455
472
|
CREW_TOKEN: input.launch.token,
|
|
456
473
|
CREW_CHANNEL: input.channelId,
|
|
457
|
-
CREW_HOME:
|
|
474
|
+
CREW_HOME: crewHome,
|
|
458
475
|
// Keep the daemon-selected storage root authoritative for runtimes.
|
|
459
476
|
// In profile-backed NowWork sessions the daemon config may differ from
|
|
460
477
|
// the daemon process environment, so inheriting CREW_AGENTS_ROOT is not
|
|
@@ -464,7 +481,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
464
481
|
...(input.launch.qaReportRoot === undefined ? {} : { QA_REPORT_HUB: input.launch.qaReportRoot }),
|
|
465
482
|
// Expose only the training root; ability-specific workspace layout stays
|
|
466
483
|
// owned by the training package rather than the NowWork daemon.
|
|
467
|
-
QA_RUNTIME_ROOT: join(
|
|
484
|
+
QA_RUNTIME_ROOT: join(crewHome, "training"),
|
|
468
485
|
CREW_TASK_DIR: executionWorkspace.runDir,
|
|
469
486
|
CREW_TASK_LOG: executionWorkspace.workLogPath,
|
|
470
487
|
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
@@ -480,7 +497,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
480
497
|
};
|
|
481
498
|
const providerEnv = applyProviderEnv(baseEnv, runtime.name, input.launch.codexHomeMode === undefined
|
|
482
499
|
? providerConfig
|
|
483
|
-
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir);
|
|
500
|
+
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir, materializedDeepSeekHome);
|
|
484
501
|
const childEnv = runtime.name === "deepseek-harness"
|
|
485
502
|
? applyDeepSeekHarnessMachineEnv(providerEnv, inheritedEnv)
|
|
486
503
|
: providerEnv;
|
|
@@ -492,7 +509,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
492
509
|
qa_runtime_root_present: childEnv.QA_RUNTIME_ROOT !== undefined,
|
|
493
510
|
inherited_qa_runtime_root_present: inheritedEnv.QA_RUNTIME_ROOT !== undefined,
|
|
494
511
|
...packageManagerEnvFacts(childEnv),
|
|
495
|
-
training_root: join(
|
|
512
|
+
training_root: join(crewHome, "training"),
|
|
496
513
|
project_context_present: input.projectContext !== undefined,
|
|
497
514
|
project_ids: JSON.stringify(input.projectContext === undefined
|
|
498
515
|
? []
|
|
@@ -624,6 +641,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
624
641
|
...(input.projectContext === undefined ? {} : {
|
|
625
642
|
projectContext: input.projectContext,
|
|
626
643
|
}),
|
|
644
|
+
...(isolatedMaintenance && runtime.name === "codex" ? { networkAccess: true } : {}),
|
|
627
645
|
...(input.projectContext === undefined && workspaceRoots.length === 0
|
|
628
646
|
? {}
|
|
629
647
|
: { workspaceRoots: Object.freeze(workspaceRoots) }),
|
|
@@ -1107,5 +1125,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
1107
1125
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1108
1126
|
process.stderr.write(`[execution] failed to remove prompt ${workspace.systemPromptPath}: ${detail}\n`);
|
|
1109
1127
|
}
|
|
1128
|
+
if (isolatedMaintenance) {
|
|
1129
|
+
await cleanupSelfTrainingRunDir(join(workspace.dir, "tasks"), executionWorkspace.runDir);
|
|
1130
|
+
}
|
|
1110
1131
|
}
|
|
1111
1132
|
}
|
package/dist/machine-info.js
CHANGED
|
@@ -25,7 +25,9 @@ export { executableRuntimes } from "./runtime-capabilities.js";
|
|
|
25
25
|
const execFileP = promisify(execFile);
|
|
26
26
|
export const PROJECT_WORKSPACES_CAPABILITY = "project_workspaces_v1";
|
|
27
27
|
export const TRAINING_PROJECT_DEPENDENCIES_CAPABILITY = "training_project_dependencies_v1";
|
|
28
|
+
export const SELF_TRAINING_EXECUTION_CAPABILITY = "self_training_execution_v1";
|
|
28
29
|
export const EXECUTION_LARGE_ATTACHMENTS_CAPABILITY = "execution_attachments_300m_v1";
|
|
30
|
+
export const PROJECT_SKILL_PROJECTION_CAPABILITY = "project_skill_projection_v2";
|
|
29
31
|
export const NATIVE_PROJECT_WORKSPACE_READINESS = Object.freeze({
|
|
30
32
|
resolverReady: true,
|
|
31
33
|
nativeRuntimeAdaptersReady: true,
|
|
@@ -42,6 +44,7 @@ export const DAEMON_CAPABILITIES = Object.freeze([
|
|
|
42
44
|
"execution_machine_queue_v1",
|
|
43
45
|
"execution_agent_memory_policy_v1",
|
|
44
46
|
"execution_agent_memory_mapping_v1",
|
|
47
|
+
SELF_TRAINING_EXECUTION_CAPABILITY,
|
|
45
48
|
"project_skills_v1",
|
|
46
49
|
"project_skill_projection_v2",
|
|
47
50
|
PROJECT_WORKSPACES_CAPABILITY,
|
package/dist/main.js
CHANGED
|
@@ -31,6 +31,7 @@ import { runStorageWorkerCommand } from "./storage-worker-command.js";
|
|
|
31
31
|
import { probeStorageMigrationRuntime } from "./storage-runtime-readiness.js";
|
|
32
32
|
import { createStorageTargetConfirmationHandler } from "./storage-target-confirmation.js";
|
|
33
33
|
import { resolveDaemonLocalStateRoot } from "./daemon-local-home.js";
|
|
34
|
+
import { storageServiceEnvironment } from "./computer-service.js";
|
|
34
35
|
async function main() {
|
|
35
36
|
const workerResult = await runStorageWorkerCommand(process.argv.slice(2));
|
|
36
37
|
if (workerResult !== null) {
|
|
@@ -78,9 +79,9 @@ async function main() {
|
|
|
78
79
|
userHome: homedir(),
|
|
79
80
|
platform: process.platform,
|
|
80
81
|
explicit: process.env.NOWWORK_LOCAL_HOME,
|
|
81
|
-
mode: process.env.NOWWORK_LOCAL_HOME_MODE
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
mode: process.env.NOWWORK_LOCAL_HOME_MODE ?? process.env.NOWWORK_STORAGE_MODE,
|
|
83
|
+
...(values.profile === undefined ? {} : { profile: values.profile }),
|
|
84
|
+
...(values["daemon-home"] === undefined ? {} : { daemonHome: values["daemon-home"] }),
|
|
84
85
|
});
|
|
85
86
|
if (values.profile) {
|
|
86
87
|
const home = daemonHome(process.env, values.profile);
|
|
@@ -98,12 +99,7 @@ async function main() {
|
|
|
98
99
|
agentsRoot: resolveAgentsRoot(profile.agentsRoot, homedir(), process.platform),
|
|
99
100
|
nodePath: process.execPath,
|
|
100
101
|
entryPath: builtDaemonEntry(),
|
|
101
|
-
environment:
|
|
102
|
-
...(process.env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: process.env.NOWWORK_HOME }),
|
|
103
|
-
...(process.env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: process.env.NOWWORK_ENV_TAG }),
|
|
104
|
-
...(process.env.NOWWORK_STORAGE_POLICY_PATH === undefined
|
|
105
|
-
? {} : { NOWWORK_STORAGE_POLICY_PATH: process.env.NOWWORK_STORAGE_POLICY_PATH }),
|
|
106
|
-
},
|
|
102
|
+
environment: storageServiceEnvironment(process.env),
|
|
107
103
|
});
|
|
108
104
|
}
|
|
109
105
|
applyProfileToEnv(profile, process.env);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { acquireDaemonInstallationLease } from "./daemon-installation-lease.js";
|
|
2
|
+
import { acquireDaemonInstallationLease, } from "./daemon-installation-lease.js";
|
|
3
3
|
import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction, replaceInstalledServiceDescriptor, 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) {
|
|
@@ -43,7 +43,7 @@ function sameManagedResource(left, right) {
|
|
|
43
43
|
function descriptorDiffIsStorageEnvironmentOnly(previous, next) {
|
|
44
44
|
if (!next.includes("\n <key>EnvironmentVariables</key>\n"))
|
|
45
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");
|
|
46
|
+
const strip = (descriptor) => descriptor.replace(/\n <key>EnvironmentVariables<\/key>\n <dict>\n(?: <key>(?:NOWWORK_HOME|NOWWORK_ENV_TAG|NOWWORK_STORAGE_POLICY_PATH|NOWWORK_LOCAL_HOME|NOWWORK_LOCAL_HOME_MODE)<\/key><string>[^<]*<\/string>\n)+ <\/dict>\n/u, "\n");
|
|
47
47
|
return strip(previous) === strip(next);
|
|
48
48
|
}
|
|
49
49
|
function assertRecordDescribesSpec(input) {
|
|
@@ -90,6 +90,7 @@ export async function installManagedService(input) {
|
|
|
90
90
|
}
|
|
91
91
|
const lease = await acquire(input.record.npmPrefix, {
|
|
92
92
|
...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
|
|
93
|
+
...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
|
|
93
94
|
});
|
|
94
95
|
try {
|
|
95
96
|
await installService(input.spec, input.runner);
|
|
@@ -136,6 +137,7 @@ export async function installManagedService(input) {
|
|
|
136
137
|
}
|
|
137
138
|
const lease = await acquire(input.record.npmPrefix, {
|
|
138
139
|
...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
|
|
140
|
+
...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
|
|
139
141
|
});
|
|
140
142
|
try {
|
|
141
143
|
const descriptor = await readServiceDescriptor(input.spec);
|
|
@@ -170,6 +172,7 @@ export async function uninstallUnregisteredManagedService(input) {
|
|
|
170
172
|
const acquire = input.acquireInstallationLease ?? acquireDaemonInstallationLease;
|
|
171
173
|
const lease = await acquire(input.npmPrefix, {
|
|
172
174
|
...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
|
|
175
|
+
...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
|
|
173
176
|
});
|
|
174
177
|
try {
|
|
175
178
|
await uninstallService(input.spec, input.runner);
|
|
@@ -199,6 +202,7 @@ export async function uninstallManagedService(input) {
|
|
|
199
202
|
}
|
|
200
203
|
const lease = await acquire(input.record.npmPrefix, {
|
|
201
204
|
...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
|
|
205
|
+
...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
|
|
202
206
|
});
|
|
203
207
|
try {
|
|
204
208
|
if (descriptor !== null)
|