@nowcrew/daemon 0.6.88-beta.3 → 0.6.88-beta.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -14
- package/dist/agent-ability/controller.js +11 -1
- package/dist/agent-ability/git-errors.js +22 -0
- package/dist/agent-ability/materializer.js +21 -10
- package/dist/agent-ability/resolver.js +12 -4
- package/dist/agent-environment-contract.js +55 -0
- package/dist/agent-environment-control.js +24 -0
- package/dist/agent-environment-execution.js +98 -0
- package/dist/agent-environments.js +275 -0
- package/dist/agent-memory/policy.js +3 -1
- package/dist/automatic-daemon-installer.js +51 -2
- package/dist/automatic-install-target.js +8 -0
- package/dist/computer-main.js +18 -0
- package/dist/computer-profile.js +17 -4
- package/dist/computer-setup-client.js +318 -0
- package/dist/computer-setup-orchestration.js +203 -0
- package/dist/computer-setup.js +244 -0
- package/dist/config.js +30 -12
- package/dist/console-formatter.js +3 -3
- package/dist/console.js +15 -2
- package/dist/daemon-update-eligibility.js +1 -1
- package/dist/execution-journal.js +9 -6
- package/dist/execution-protocol.js +2 -1
- package/dist/execution-runner.js +16 -10
- package/dist/i18n.js +19 -0
- package/dist/list-models.js +13 -11
- package/dist/local-execution-lease.js +22 -0
- package/dist/local-executor.js +88 -92
- package/dist/local-memory/candidate.js +43 -0
- package/dist/local-memory/index-budget.js +8 -0
- package/dist/local-memory/inventory-cli.js +61 -0
- package/dist/local-memory/inventory-preview.js +159 -0
- package/dist/local-memory/inventory-source.js +159 -0
- package/dist/local-memory/pending-match.js +29 -0
- package/dist/local-memory/pending-query.js +100 -0
- package/dist/local-memory/pending-schema.js +29 -0
- package/dist/local-memory/pending-source.js +126 -0
- package/dist/local-memory/pending.js +34 -84
- package/dist/local-memory/prompt.js +20 -12
- package/dist/local-memory/read-cli.js +97 -12
- package/dist/local-memory/rollout.js +82 -0
- package/dist/local-memory/runtime.js +24 -17
- package/dist/local-memory/work-log-execution.js +31 -0
- package/dist/local-memory/work-log.js +58 -0
- package/dist/local-memory/write.js +27 -58
- package/dist/local-runtime-environment.js +50 -0
- package/dist/machine-environment-cache.js +147 -0
- package/dist/machine-environment-contract.js +10 -0
- package/dist/machine-environment-control.js +27 -0
- package/dist/machine-environment-policy.js +75 -0
- package/dist/machine-environment-process.js +110 -0
- package/dist/machine-info.js +70 -25
- package/dist/machine-runtime-environment.js +189 -0
- package/dist/machine-shell-environment-capture.js +135 -0
- package/dist/machine-windows-environment-capture.js +215 -0
- package/dist/machine-windows-environment-script.js +158 -0
- package/dist/main.js +4 -69
- package/dist/marketplace-controller.js +75 -0
- package/dist/marketplace-protocol.js +38 -0
- package/dist/marketplace-runner.js +234 -0
- package/dist/marketplace-serve.js +17 -0
- package/dist/message-file-preview.js +246 -0
- package/dist/native-thread-context.js +59 -0
- package/dist/pre-admission-rejection.js +64 -0
- package/dist/project-skills/execution-adapter.js +3 -2
- package/dist/project-skills/runtime-launch.js +14 -0
- package/dist/prompt.js +15 -15
- package/dist/provider-env.js +26 -15
- package/dist/runner.js +31 -19
- package/dist/runtime-environment-merge.js +16 -0
- package/dist/runtime-health-handler.js +10 -0
- package/dist/runtime-health.js +3 -1
- package/dist/runtime-path.js +22 -12
- package/dist/runtime-probe.js +35 -6
- package/dist/runtimes/acp-probe-transport.js +69 -0
- package/dist/runtimes/claude-models.js +20 -9
- package/dist/runtimes/claude-session-start-hook.js +24 -0
- package/dist/runtimes/claude-thread-runner.js +266 -0
- package/dist/runtimes/claude.js +23 -4
- package/dist/runtimes/codex-app-server-runner.js +43 -4
- package/dist/runtimes/deepseek-harness-launch.js +10 -0
- package/dist/runtimes/deepseek-harness-profile.js +92 -0
- package/dist/runtimes/deepseek-harness-runner.js +226 -0
- package/dist/runtimes/deepseek-harness.js +76 -15
- package/dist/runtimes/harness-probe-retry.js +42 -0
- package/dist/runtimes/hermes-models.js +2 -1
- package/dist/runtimes/kimi-acp-runner.js +84 -15
- package/dist/runtimes/opencode-runner.js +7 -1
- package/dist/runtimes/opencode.js +2 -2
- package/dist/serve.js +95 -184
- package/dist/server-url.js +25 -0
- package/dist/session.js +4 -0
- package/dist/slog.js +2 -1
- package/dist/storage-identity.js +0 -3
- package/dist/storage-runtime.js +2 -2
- package/dist/storage-serve-adapter.js +6 -72
- package/dist/supervised-runtime.js +28 -18
- package/dist/thread-input-delivery.js +217 -0
- package/dist/token.js +0 -1
- package/dist/workspace-control-handler.js +27 -0
- package/dist/workspace-fs.js +66 -15
- package/dist/workspace.js +1 -1
- package/package.json +5 -4
- package/dist/codex-home-launch.js +0 -7
- package/dist/codex-home-mode-controller.js +0 -68
- package/dist/daemon-migration-controller.js +0 -211
- package/dist/daemon-migration-wiring.js +0 -22
- package/dist/runtimes/codex-home-migration-cli.js +0 -26
- package/dist/runtimes/codex-home-migration.js +0 -112
- package/dist/runtimes/codex-home.js +0 -236
- package/dist/storage-control.js +0 -212
- package/dist/storage-copy.js +0 -628
- package/dist/storage-credential-guard.js +0 -21
- package/dist/storage-doctor.js +0 -228
- package/dist/storage-manifest.js +0 -1027
- package/dist/storage-migration-controller.js +0 -440
- package/dist/storage-migration-manifest.js +0 -105
- package/dist/storage-migration-policy.js +0 -100
- package/dist/storage-runtime-controller.js +0 -381
- package/dist/storage-runtime-readiness.js +0 -240
- package/dist/storage-safe-fs.js +0 -302
- package/dist/storage-status-delivery.js +0 -224
- package/dist/storage-status-replay.js +0 -140
- package/dist/storage-target-confirmation.js +0 -129
- package/dist/storage-worker-command.js +0 -37
- package/dist/storage-worker-executor.js +0 -449
- package/dist/storage-worker-handoff.js +0 -151
- package/dist/storage-worker-operation.js +0 -294
- package/dist/storage-worker-status.js +0 -259
package/dist/local-executor.js
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
|
+
import { deepSeekHarnessLauncher } from "./runtimes/deepseek-harness.js";
|
|
2
|
+
import { buildLocalRuntimeEnvironment } from "./local-runtime-environment.js";
|
|
3
|
+
import { environmentForExecution } from "./agent-environments.js";
|
|
4
|
+
import { prepareEnvironmentExecutionSnapshot } from "./agent-environment-execution.js";
|
|
1
5
|
import { createInterface } from "node:readline";
|
|
2
6
|
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import {
|
|
7
|
+
import { join } from "node:path";
|
|
4
8
|
import { cleanupSelfTrainingRunDir } from "./self-training-workspace.js";
|
|
5
9
|
import { prepareWorkspace, rotateAgentSession, safeKey, } from "./workspace.js";
|
|
6
10
|
import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, spawnClaude, } from "./runtimes/claude.js";
|
|
7
11
|
import { spawnCodex } from "./runtimes/codex.js";
|
|
8
12
|
import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
9
|
-
import {
|
|
10
|
-
import { materializePrivateCodexHome } from "./codex-home-launch.js";
|
|
11
|
-
import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
|
|
13
|
+
import { spawnKimi } from "./runtimes/kimi.js";
|
|
12
14
|
import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, } from "./provider-env.js";
|
|
13
|
-
import { augmentedPath } from "./runtime-path.js";
|
|
14
15
|
import { deliverCloudReports, prepareCloudReportExecution } from "./cloud-report-uploader.js";
|
|
15
16
|
import { reportDeliveryContext } from "./cloud-report-receipt.js";
|
|
16
17
|
import { extractFinalText, extractRunMeta, normalizeEvent, parseLine, } from "./normalize.js";
|
|
17
18
|
import { readSession, writeSession, pickResumeId, isNearBudget } from "./session.js";
|
|
19
|
+
import { NativeThreadContext, isolateMaintenanceSession } from "./native-thread-context.js";
|
|
20
|
+
import { prepareThreadDelivery } from "./thread-input-delivery.js";
|
|
21
|
+
import { resolveMemoryPruneWorkLog, workLogSha256 } from "./local-memory/work-log.js";
|
|
22
|
+
import { withKeyedLease } from "./local-execution-lease.js";
|
|
23
|
+
import { withWorkLogExecution, withWorkLogCommit, workLogExecutionKey } from "./local-memory/work-log-execution.js";
|
|
18
24
|
import { providerErrorFromExit } from "./provider-error.js";
|
|
19
25
|
import { createConsoleFormatter } from "./console-formatter.js";
|
|
20
26
|
import { withLocalExecutionFacts, } from "./local-execution-prompt.js";
|
|
@@ -35,11 +41,11 @@ import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPr
|
|
|
35
41
|
import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
|
|
36
42
|
import { CodexStartupStageParser } from "./codex-startup-stage.js";
|
|
37
43
|
import { formatRuntimeStartupError } from "./runtime-startup-error.js";
|
|
38
|
-
import { machinePackageManagerEnv } from "./package-manager-env.js";
|
|
39
44
|
import { logRuntimeEnvironmentContract } from "./runtime-environment-telemetry.js";
|
|
40
|
-
import { sanitizeEnvVars } from "./runtime-env.js";
|
|
41
45
|
import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, } from "./local-memory/runtime.js";
|
|
42
46
|
import { prepareLocalMemoryPrompt } from "./local-memory/prompt.js";
|
|
47
|
+
import { reportLocalMemoryV2Deferred, resolveLocalMemoryV2Rollout } from "./local-memory/rollout.js";
|
|
48
|
+
import { localMemoryV2Environment } from "./config.js";
|
|
43
49
|
export class RuntimeRunningCallbackTimeoutError extends Error {
|
|
44
50
|
constructor(runtime, timeoutMs) {
|
|
45
51
|
super(`${runtime} runtime-running callback timed out after ${timeoutMs}ms`);
|
|
@@ -108,6 +114,9 @@ async function launchLegacyRuntime(request) {
|
|
|
108
114
|
if (request.runtime === "deepseek-harness") {
|
|
109
115
|
throw new Error("DeepSeek Harness requires execution protocol v1");
|
|
110
116
|
}
|
|
117
|
+
if (request.runtime !== "claude")
|
|
118
|
+
request = { ...request, wakePrompt: (await prepareThreadDelivery({ env: request.env,
|
|
119
|
+
resumed: false, sessionId: "unverified-cold-legacy", wakePrompt: request.wakePrompt })).prompt };
|
|
111
120
|
const common = {
|
|
112
121
|
bin: request.bin,
|
|
113
122
|
cwd: request.cwd,
|
|
@@ -154,27 +163,12 @@ async function launchLegacyRuntime(request) {
|
|
|
154
163
|
function resolvePrompt(prompt, context) {
|
|
155
164
|
return typeof prompt === "string" ? prompt : prompt(context);
|
|
156
165
|
}
|
|
157
|
-
const nativeSessionLeaseTails = new Map();
|
|
158
166
|
const activeMemoryPrunes = new Map();
|
|
159
|
-
async function withKeyedLease(key, operation, cancellation) {
|
|
160
|
-
const predecessor = nativeSessionLeaseTails.get(key) ?? Promise.resolve();
|
|
161
|
-
let release;
|
|
162
|
-
const current = new Promise((resolve) => { release = resolve; });
|
|
163
|
-
const tail = predecessor.then(() => current);
|
|
164
|
-
nativeSessionLeaseTails.set(key, tail);
|
|
165
|
-
try {
|
|
166
|
-
await awaitWithCancellation(predecessor, cancellation);
|
|
167
|
-
return await operation();
|
|
168
|
-
}
|
|
169
|
-
finally {
|
|
170
|
-
release();
|
|
171
|
-
void tail.then(() => {
|
|
172
|
-
if (nativeSessionLeaseTails.get(key) === tail)
|
|
173
|
-
nativeSessionLeaseTails.delete(key);
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
167
|
export async function executeLocal(input, callbacks = {}, dependencies = {}) {
|
|
168
|
+
input = isolateMaintenanceSession(input);
|
|
169
|
+
return withWorkLogExecution(workLogExecutionKey(input.launch.agentsRoot, input.handle, input.taskKey), () => executeLocalWithSession(input, callbacks, dependencies), dependencies.cancellation);
|
|
170
|
+
}
|
|
171
|
+
async function executeLocalWithSession(input, callbacks, dependencies) {
|
|
178
172
|
const versionedProjectSkills = input.projectSkills !== undefined
|
|
179
173
|
&& !Array.isArray(input.projectSkills);
|
|
180
174
|
if (versionedProjectSkills && (input.runtime.name !== "codex" && input.runtime.name !== "claude")) {
|
|
@@ -242,7 +236,12 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
242
236
|
}, "workspace_prepare", error);
|
|
243
237
|
throw error;
|
|
244
238
|
}
|
|
245
|
-
const
|
|
239
|
+
const memoryRollout = await resolveLocalMemoryV2Rollout({ workspace, handle: input.handle,
|
|
240
|
+
serverUrl: input.launch.serverUrl, requested: input.localMemoryV2Enabled === true,
|
|
241
|
+
selfTraining: isolatedMaintenance, ...(input.launch.description ? { description: input.launch.description } : {}) });
|
|
242
|
+
workspace = { ...workspace, localMemoryV2Enabled: memoryRollout.enabled };
|
|
243
|
+
input = { ...input, localMemoryV2Enabled: memoryRollout.enabled };
|
|
244
|
+
const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, memoryRollout.enabled);
|
|
246
245
|
let materialized = null;
|
|
247
246
|
let knownAttachmentDirectory = null;
|
|
248
247
|
const startupReservationState = { current: null };
|
|
@@ -253,9 +252,12 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
253
252
|
let memoryPruneBeforeSnapshot = null;
|
|
254
253
|
let memoryPruneSharedWriteKey = null;
|
|
255
254
|
let localMemoryResultPath = null;
|
|
255
|
+
let environmentSnapshot;
|
|
256
256
|
let executionWorkspace = workspace;
|
|
257
257
|
let localMemoryTelemetry = null;
|
|
258
|
-
|
|
258
|
+
let nativeThreadContext = null;
|
|
259
|
+
const machineSnapshot = dependencies.machineEnvironment ? await awaitWithCancellation(dependencies.machineEnvironment.acquire(), dependencies.cancellation) : undefined;
|
|
260
|
+
const inheritedEnv = { ...(machineSnapshot?.env ?? process.env) };
|
|
259
261
|
const reportRoots = {
|
|
260
262
|
root: workspace.dir,
|
|
261
263
|
...(input.launch.qaReportRoot === undefined ? {} : { qaReportRoot: input.launch.qaReportRoot }),
|
|
@@ -267,8 +269,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
267
269
|
if (key.startsWith("CREW_AGENT_MEMORY_") || ["CREW_CLOUD_PROVIDER", "CREW_OSS_UPLOAD_SCRIPT", "CREW_OSS_REPORT_UPLOAD"].includes(key))
|
|
268
270
|
delete inheritedEnv[key];
|
|
269
271
|
}
|
|
270
|
-
const sourceCodexHome = inheritedEnv.CODEX_HOME
|
|
271
|
-
?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
|
|
272
272
|
let materializedDeepSeekHome;
|
|
273
273
|
try {
|
|
274
274
|
if (isDeepSeekCodex && !providerConfig.providerApiKey) {
|
|
@@ -294,12 +294,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
294
294
|
: null;
|
|
295
295
|
const currentPrior = storedCurrentPrior !== null
|
|
296
296
|
&& storedCurrentPrior.sessionContextFingerprint === input.sessionContextFingerprint
|
|
297
|
+
&& storedCurrentPrior.runtime === runtime.name
|
|
298
|
+
&& storedCurrentPrior.threadRoot === input.resumeKey
|
|
297
299
|
? storedCurrentPrior
|
|
298
300
|
: null;
|
|
299
|
-
//
|
|
300
|
-
// older legacy path stored metadata under tasks/<safe thread key>. Import
|
|
301
|
-
// that metadata once so an existing thread can cross the protocol boundary
|
|
302
|
-
// without losing its native runtime conversation.
|
|
301
|
+
// Import compatible legacy metadata across the protocol boundary.
|
|
303
302
|
const legacyRunDir = input.session.enabled
|
|
304
303
|
&& supportsNativeResume
|
|
305
304
|
&& input.keyMode === "opaque"
|
|
@@ -315,7 +314,9 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
315
314
|
const legacyPrior = legacyRunDir === null || (currentPrior !== null && !legacyCwdPinned)
|
|
316
315
|
? null
|
|
317
316
|
: await readSession(legacyRunDir);
|
|
318
|
-
const
|
|
317
|
+
const compatibleLegacyPrior = legacyPrior?.runtime === runtime.name && legacyPrior?.threadRoot === input.resumeKey
|
|
318
|
+
? legacyPrior : null;
|
|
319
|
+
const prior = currentPrior ?? compatibleLegacyPrior;
|
|
319
320
|
const sessionExists = workspace.sessionResume || legacyPrior !== null;
|
|
320
321
|
const usesLegacyCwd = legacyRunDir !== null
|
|
321
322
|
&& (legacyCwdPinned || (currentPrior === null && legacyPrior !== null));
|
|
@@ -336,11 +337,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
336
337
|
? prior?.model ?? currentModel
|
|
337
338
|
: currentModel;
|
|
338
339
|
const resumeSessionId = supportsNativeResume && sessionExists
|
|
339
|
-
? pickResumeId(prior, Date.now(), input.session.warmMs, resumeComparisonModel, input.session.budgetTokens, input.session.maxTurns, providerFp)
|
|
340
|
+
? pickResumeId(input.resumeKey && (!Number.isSafeInteger(prior?.threadContextSeq) || prior?.threadHistoryComplete !== true) ? null : prior, Date.now(), input.resumeKey ? 0 : input.session.warmMs, resumeComparisonModel, input.resumeKey ? 0 : input.session.budgetTokens, input.resumeKey ? 0 : input.session.maxTurns, providerFp)
|
|
340
341
|
: null;
|
|
341
342
|
const resuming = resumeSessionId !== null;
|
|
342
343
|
const rotatedForBudget = !resuming && supportsNativeResume && sessionExists
|
|
343
|
-
&& pickResumeId(prior, Date.now(), input.session.warmMs, resumeComparisonModel, 0, 0, providerFp) !== null;
|
|
344
|
+
&& pickResumeId(prior, Date.now(), input.resumeKey ? 0 : input.session.warmMs, resumeComparisonModel, 0, 0, providerFp) !== null;
|
|
344
345
|
const nearBudget = resuming && isNearBudget(prior, input.session.softTokens);
|
|
345
346
|
const rotated = Boolean(workspace.agentSessionId && sessionExists && !resuming);
|
|
346
347
|
// Codex and Kimi choose their own id on a fresh start, so the native id persisted in
|
|
@@ -348,8 +349,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
348
349
|
const launchSessionId = resumeSessionId ?? (rotated
|
|
349
350
|
? await rotateAgentSession(workspace.sessionDir)
|
|
350
351
|
: workspace.agentSessionId);
|
|
351
|
-
|
|
352
|
-
const promptContext = {
|
|
352
|
+
let promptContext = {
|
|
353
353
|
workspace: executionWorkspace,
|
|
354
354
|
resuming,
|
|
355
355
|
rotatedForBudget,
|
|
@@ -388,8 +388,15 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
|
|
391
|
-
|
|
392
|
-
memoryPruneTraceId = parseMemoryPruneTraceId(
|
|
391
|
+
let baseWakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
392
|
+
memoryPruneTraceId = isolatedMaintenance ? null : parseMemoryPruneTraceId(baseWakePrompt);
|
|
393
|
+
if (memoryRollout.deferred)
|
|
394
|
+
reportLocalMemoryV2Deferred(input.executionId, input.handle, memoryPruneTraceId !== null || input.maintenancePurpose === "memory_prune");
|
|
395
|
+
if (memoryPruneTraceId !== null) {
|
|
396
|
+
executionWorkspace = await resolveMemoryPruneWorkLog(executionWorkspace, input);
|
|
397
|
+
promptContext = { ...promptContext, workspace: executionWorkspace };
|
|
398
|
+
baseWakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
399
|
+
}
|
|
393
400
|
if (input.localMemoryV2Enabled === true && memoryPruneTraceId !== null
|
|
394
401
|
&& isLocalMemoryV2Eligible({
|
|
395
402
|
enabledForHandle: true,
|
|
@@ -402,12 +409,15 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
402
409
|
}
|
|
403
410
|
const memoryPrompt = await prepareLocalMemoryPrompt({
|
|
404
411
|
agentDir: executionWorkspace.dir, executionId: input.executionId, agentHandle: input.handle,
|
|
405
|
-
enabled: input.localMemoryV2Enabled === true, query:
|
|
412
|
+
enabled: input.localMemoryV2Enabled === true, query: baseWakePrompt,
|
|
406
413
|
resultPath: localMemoryResultPath, maxBytes: input.maxSystemPromptBytes,
|
|
414
|
+
strictRollout: !isolatedMaintenance && localMemoryV2Environment(input.launch.serverUrl) === "dev",
|
|
415
|
+
deferred: memoryRollout.deferred,
|
|
407
416
|
renderBase: (reservedPromptBytes) => resolvePrompt(input.systemPrompt, { ...promptContext, reservedPromptBytes }),
|
|
408
417
|
});
|
|
409
418
|
localMemoryResultPath = memoryPrompt.resultPath;
|
|
410
419
|
const systemPrompt = memoryPrompt.systemPrompt;
|
|
420
|
+
const wakePrompt = baseWakePrompt + memoryPrompt.wakePromptSuffix;
|
|
411
421
|
if (!isolatedMaintenance)
|
|
412
422
|
try {
|
|
413
423
|
localMemoryTelemetry = createLocalMemoryTelemetry({
|
|
@@ -466,45 +476,14 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
466
476
|
}
|
|
467
477
|
memoryPruneFailurePhase = "runtime_prepare";
|
|
468
478
|
const crewHome = isolatedMaintenance ? executionWorkspace.runDir : workspace.dir;
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
CREW_SERVER_URL: input.launch.serverUrl,
|
|
478
|
-
CREW_TOKEN: input.launch.token,
|
|
479
|
-
CREW_CHANNEL: input.channelId,
|
|
480
|
-
CREW_HOME: crewHome,
|
|
481
|
-
// Keep the daemon-selected storage root authoritative for runtimes.
|
|
482
|
-
// In profile-backed NowWork sessions the daemon config may differ from
|
|
483
|
-
// the daemon process environment, so inheriting CREW_AGENTS_ROOT is not
|
|
484
|
-
// sufficient for QA/project diagnostics.
|
|
485
|
-
CREW_AGENTS_ROOT: input.launch.agentsRoot,
|
|
486
|
-
...(input.launch.reposRoot === undefined ? {} : { NOWWORK_REPOS_ROOT: input.launch.reposRoot }),
|
|
487
|
-
...(input.launch.qaReportRoot === undefined ? {} : { QA_REPORT_HUB: input.launch.qaReportRoot }),
|
|
488
|
-
// Expose only the training root; ability-specific workspace layout stays
|
|
489
|
-
// owned by the training package rather than the NowWork daemon.
|
|
490
|
-
QA_RUNTIME_ROOT: join(crewHome, "training"),
|
|
491
|
-
QA_REPORT_DELIVERY_CONTEXT: JSON.stringify(reportContext),
|
|
492
|
-
CREW_TASK_DIR: executionWorkspace.runDir,
|
|
493
|
-
CREW_TASK_LOG: executionWorkspace.workLogPath,
|
|
494
|
-
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
495
|
-
XDG_CONFIG_HOME: join(workspace.homeDir, ".config"),
|
|
496
|
-
XDG_DATA_HOME: join(workspace.homeDir, ".local", "share"),
|
|
497
|
-
XDG_CACHE_HOME: join(workspace.homeDir, ".cache"),
|
|
498
|
-
GH_CONFIG_DIR: join(workspace.homeDir, ".config", "gh"),
|
|
499
|
-
CLOUDSDK_CONFIG: join(workspace.homeDir, ".config", "gcloud"),
|
|
500
|
-
...(runtime.name === "kimi" && runtime.reasoning
|
|
501
|
-
&& KIMI_EFFORT_LEVELS.includes(runtime.reasoning)
|
|
502
|
-
? { KIMI_MODEL_THINKING_EFFORT: runtime.reasoning }
|
|
503
|
-
: {}),
|
|
504
|
-
};
|
|
505
|
-
const providerEnv = applyProviderEnv(baseEnv, runtime.name, input.launch.codexHomeMode === undefined
|
|
506
|
-
? providerConfig
|
|
507
|
-
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir, materializedDeepSeekHome);
|
|
479
|
+
const contextThreadId = input.threadId ?? input.resumeKey;
|
|
480
|
+
if (contextThreadId && input.maintenancePurpose === undefined) {
|
|
481
|
+
nativeThreadContext = new NativeThreadContext(join(executionWorkspace.runDir, `.crew-context-${input.executionId}.json`), contextThreadId, runtime.name, resumeSessionId, prior);
|
|
482
|
+
await nativeThreadContext.initialize();
|
|
483
|
+
}
|
|
484
|
+
const baseEnv = buildLocalRuntimeEnvironment({ input, workspace, executionWorkspace, inheritedEnv, providerConfig,
|
|
485
|
+
crewHome, contextThreadId, nativeThreadContextPath: nativeThreadContext?.path, reportContext, runtime });
|
|
486
|
+
const providerEnv = applyProviderEnv(baseEnv, runtime.name, providerConfig, workspace.homeDir, materializedDeepSeekHome);
|
|
508
487
|
const childEnv = runtime.name === "deepseek-harness"
|
|
509
488
|
? applyDeepSeekHarnessMachineEnv(providerEnv, inheritedEnv)
|
|
510
489
|
: providerEnv;
|
|
@@ -521,11 +500,17 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
521
500
|
const abilityContext = input.abilityRelease !== undefined && dependencies.abilityRelease !== undefined
|
|
522
501
|
? await dependencies.abilityRelease.prepareAndLaunch(input.launch.agentsRoot, input.handle, input.abilityRelease, async (context) => context)
|
|
523
502
|
: undefined;
|
|
503
|
+
if (providerConfig.environmentPlan !== undefined && !isolatedMaintenance) {
|
|
504
|
+
environmentSnapshot = await prepareEnvironmentExecutionSnapshot({ plan: providerConfig.environmentPlan, agentRoot: workspace.dir, runDir: executionWorkspace.runDir, executionId: input.executionId, ambient: inheritedEnv, runtime: runtime.name, providerConfig, runtimeEnvironment: childEnv, authoritativeKeys: Object.keys(input.launch.systemEnv ?? {}),
|
|
505
|
+
executionBaseEnvironment: childEnv,
|
|
506
|
+
...(machineSnapshot === undefined ? {} : { machineEnvironmentVersion: { bootId: machineSnapshot.status.bootId, generation: machineSnapshot.status.generation } }),
|
|
507
|
+
selectedEnvironment: environmentForExecution(providerConfig.environmentPlan, workspace.dir, input.projectContext?.primary?.root ?? executionWorkspace.runDir, input.projectContext?.primary?.projectId),
|
|
508
|
+
projectRoots: [...(input.projectContext?.primary ? [input.projectContext.primary] : []), ...(input.projectContext?.secondary ?? [])] });
|
|
509
|
+
}
|
|
524
510
|
const prepareRuntimeLaunch = async (runtimeRoot) => {
|
|
525
|
-
const effectiveSystemPrompt = abilityContext
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (Buffer.byteLength(effectiveSystemPrompt, "utf8") > (input.maxSystemPromptBytes ?? Number.MAX_SAFE_INTEGER)) {
|
|
511
|
+
const effectiveSystemPrompt = [systemPrompt, abilityContext?.prompt, environmentSnapshot?.prompt].filter(Boolean).join("\n\n");
|
|
512
|
+
if (Buffer.byteLength(effectiveSystemPrompt, "utf8") + Buffer.byteLength(memoryPrompt.wakePromptSuffix, "utf8")
|
|
513
|
+
> (input.maxSystemPromptBytes ?? Number.MAX_SAFE_INTEGER)) {
|
|
529
514
|
throw new Error("ability_runtime_context_size_exceeded");
|
|
530
515
|
}
|
|
531
516
|
memoryPruneFailurePhase = "prompt_write";
|
|
@@ -559,7 +544,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
559
544
|
});
|
|
560
545
|
}
|
|
561
546
|
const trainingRoots = input.trainingProjectContext?.projects.map((project) => project.root) ?? [];
|
|
562
|
-
const { codexSkillRoots, claudeAdditionalDirectories } = await projectSkillRuntimeDirectories({
|
|
547
|
+
const { codexSkillRoots, claudeAdditionalDirectories, harnessSkillRoots } = await projectSkillRuntimeDirectories({
|
|
563
548
|
runtime: runtime.name,
|
|
564
549
|
...(runtimeRoot === undefined ? {} : { runtimeRoot }),
|
|
565
550
|
agentRoot: workspace.dir,
|
|
@@ -597,7 +582,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
597
582
|
const workspaceRoots = [...legacyRoots, ...trainingRoots];
|
|
598
583
|
const launchRequest = {
|
|
599
584
|
runtime: runtime.name,
|
|
600
|
-
bin: runtime.name === "deepseek-harness" ?
|
|
585
|
+
bin: runtime.name === "deepseek-harness" ? deepSeekHarnessLauncher(runtimeEnv).bin : runtime.name,
|
|
586
|
+
...(runtime.name === "deepseek-harness" ? { harnessStateRoot: executionWorkspace.runDir, harnessSkillRoots: harnessSkillRoots ?? [] } : {}),
|
|
601
587
|
cwd: input.projectContext?.primary?.root ?? executionWorkspace.runDir,
|
|
602
588
|
...(runtimeRoot !== undefined
|
|
603
589
|
|| (input.projectSkills === undefined && input.abilityRelease === undefined)
|
|
@@ -713,7 +699,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
713
699
|
// (incident: 2026-09-02, boss-alarm 排查结论未投递)。所有 runtime 的文本都喂给它。
|
|
714
700
|
const externalOutput = new ExternalAnswerDecoder();
|
|
715
701
|
// 每轮独立:tool_use/result 关联状态不能跨 execution 泄漏。
|
|
716
|
-
const consoleFormatter = createConsoleFormatter();
|
|
702
|
+
const consoleFormatter = createConsoleFormatter(runtime.name);
|
|
717
703
|
let runtimeReady = false;
|
|
718
704
|
let runtimeRunning = false;
|
|
719
705
|
let resolveRuntimeReady;
|
|
@@ -801,6 +787,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
801
787
|
const event = parseLine(line);
|
|
802
788
|
if (!event)
|
|
803
789
|
return;
|
|
790
|
+
nativeThreadContext?.observe(event);
|
|
804
791
|
localMemoryTelemetry?.observe(event);
|
|
805
792
|
if (isRuntimeReadyEvent(runtime.name, event))
|
|
806
793
|
markRuntimeReady();
|
|
@@ -918,14 +905,17 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
918
905
|
memoryPruneRuntimeExitCode = exitCode;
|
|
919
906
|
if (localMemoryResultPath !== null) {
|
|
920
907
|
memoryPruneFailurePhase = "memory_commit";
|
|
921
|
-
const
|
|
922
|
-
|
|
908
|
+
const resultPath = localMemoryResultPath;
|
|
909
|
+
const commit = () => commitLocalMemoryResultAfterExit({
|
|
910
|
+
resultPath,
|
|
923
911
|
agentDir: executionWorkspace.dir,
|
|
924
912
|
executionId: input.executionId,
|
|
925
913
|
pruneTraceId: memoryPruneTraceId,
|
|
926
914
|
exitCode,
|
|
927
915
|
workLogPath: executionWorkspace.workLogPath,
|
|
916
|
+
expectedWorkLogSha256: workLogSha256(executionWorkspace.workLog),
|
|
928
917
|
});
|
|
918
|
+
const committed = exitCode !== 0 ? await commit() : await withWorkLogCommit(workLogExecutionKey(input.launch.agentsRoot, input.handle, input.taskKey), commit, dependencies.cancellation);
|
|
929
919
|
if (committed.status === "failed" || committed.status === "missing") {
|
|
930
920
|
const reason = committed.error ?? "local_memory_result_missing";
|
|
931
921
|
dslog("local_memory.commit_failed", "结构化本地记忆提交或清理未完成,保留可用恢复材料", {
|
|
@@ -959,9 +949,12 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
959
949
|
? usage.inputTokens + usage.cacheReadTokens + usage.cacheCreationTokens
|
|
960
950
|
: (resuming ? prior?.contextTokens : undefined);
|
|
961
951
|
await writeSession(workspace.sessionDir, {
|
|
952
|
+
runtime: runtime.name,
|
|
953
|
+
...(input.resumeKey === undefined ? {} : { threadRoot: input.resumeKey }),
|
|
954
|
+
...(exitCode === 0 ? await nativeThreadContext?.checkpoint() ?? {} : {}),
|
|
962
955
|
sessionId,
|
|
963
956
|
lastRunAt: Date.now(),
|
|
964
|
-
turns:
|
|
957
|
+
turns: nativeThreadContext?.confirmed ? (prior?.turns ?? 0) + 1 : 1,
|
|
965
958
|
model: currentModel,
|
|
966
959
|
providerFingerprint: providerFp,
|
|
967
960
|
...(input.sessionContextFingerprint === undefined
|
|
@@ -995,7 +988,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
995
988
|
...(usage === undefined ? {} : { usage }),
|
|
996
989
|
model: observedModel,
|
|
997
990
|
runtime: runtime.name,
|
|
998
|
-
resumed:
|
|
991
|
+
resumed: nativeThreadContext?.confirmed ?? false,
|
|
999
992
|
sessionId,
|
|
1000
993
|
errorMessage: errorTail || null,
|
|
1001
994
|
...(providerError === undefined ? {} : { providerError }),
|
|
@@ -1007,6 +1000,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
1007
1000
|
};
|
|
1008
1001
|
}
|
|
1009
1002
|
finally {
|
|
1003
|
+
await nativeThreadContext?.cleanup().catch(() => undefined);
|
|
1010
1004
|
startupReservationState.current?.release();
|
|
1011
1005
|
try {
|
|
1012
1006
|
await localMemoryTelemetry?.finish();
|
|
@@ -1099,6 +1093,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
1099
1093
|
active_prune_count: activePruneCount,
|
|
1100
1094
|
});
|
|
1101
1095
|
}
|
|
1096
|
+
if (environmentSnapshot)
|
|
1097
|
+
await rm(environmentSnapshot.directory, { recursive: true, force: true }).catch(() => { });
|
|
1102
1098
|
const attachmentDirectories = new Set([
|
|
1103
1099
|
...(knownAttachmentDirectory === null ? [] : [knownAttachmentDirectory]),
|
|
1104
1100
|
...(materialized === null ? [] : [materialized.directory]),
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { containsLikelySecret } from "../agent-memory/policy.js";
|
|
3
|
+
const CategorySchema = z.enum([
|
|
4
|
+
"preferences", "project-rules", "testing", "debugging", "git", "incidents", "lessons",
|
|
5
|
+
]);
|
|
6
|
+
export const LocalMemorySingleLineText = z.string().trim().min(1).max(500).regex(/^[^\r\n]+$/u);
|
|
7
|
+
export const LocalMemoryChangeSchema = z.object({
|
|
8
|
+
operation: z.enum(["create", "update", "supersede"]),
|
|
9
|
+
memoryId: LocalMemorySingleLineText.regex(/^[^<>]+$/u).nullable(),
|
|
10
|
+
baseRevision: z.number().int().nonnegative().nullable(),
|
|
11
|
+
supersedesMemoryId: LocalMemorySingleLineText.regex(/^[^<>]+$/u).nullable(),
|
|
12
|
+
title: LocalMemorySingleLineText.max(120),
|
|
13
|
+
kind: z.enum(["procedural", "semantic", "episodic", "preference"]),
|
|
14
|
+
summary: LocalMemorySingleLineText,
|
|
15
|
+
triggers: z.array(LocalMemorySingleLineText.max(80)).min(1).max(16),
|
|
16
|
+
priority: z.enum(["high", "medium", "low"]),
|
|
17
|
+
detailCategory: CategorySchema,
|
|
18
|
+
detailBody: z.string().trim().min(1).max(12_000),
|
|
19
|
+
reviewAfter: LocalMemorySingleLineText.nullable(),
|
|
20
|
+
}).strict();
|
|
21
|
+
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u;
|
|
22
|
+
const RESERVED_MEMORY_MARKER = /NOWWORK:(?:MEMORY_ENTRY|MEMORY_INDEX):/iu;
|
|
23
|
+
export function validateLocalMemoryChange(change) {
|
|
24
|
+
const text = [change.title, change.summary, ...change.triggers, change.detailBody].join("\n");
|
|
25
|
+
if (containsLikelySecret(text))
|
|
26
|
+
throw new Error("local_memory_secret_candidate");
|
|
27
|
+
if (CONTROL_CHARACTER_PATTERN.test(text))
|
|
28
|
+
throw new Error("local_memory_control_character");
|
|
29
|
+
if (RESERVED_MEMORY_MARKER.test(text))
|
|
30
|
+
throw new Error("local_memory_reserved_marker");
|
|
31
|
+
if (change.operation === "create"
|
|
32
|
+
&& (change.memoryId !== null || change.baseRevision !== null || change.supersedesMemoryId !== null)) {
|
|
33
|
+
throw new Error("local_memory_create_revision_invalid");
|
|
34
|
+
}
|
|
35
|
+
if (change.operation === "update"
|
|
36
|
+
&& (change.memoryId === null || change.baseRevision === null || change.supersedesMemoryId !== null)) {
|
|
37
|
+
throw new Error("local_memory_update_revision_missing");
|
|
38
|
+
}
|
|
39
|
+
if (change.operation === "supersede"
|
|
40
|
+
&& (change.memoryId !== null || change.baseRevision === null || change.supersedesMemoryId === null)) {
|
|
41
|
+
throw new Error("local_memory_supersede_revision_missing");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -19,6 +19,14 @@ export function renderLocalMemoryIndexCard(entry) {
|
|
|
19
19
|
+ `- updated: ${entry.updated}\n`
|
|
20
20
|
+ (entry.reviewAfter === null ? "" : `- review_after: ${entry.reviewAfter}\n`);
|
|
21
21
|
}
|
|
22
|
+
/** 写入和预演共用完整索引,预算包含起止标记与分隔换行。 */
|
|
23
|
+
export function renderGeneratedIndex(entries) {
|
|
24
|
+
const cards = entries
|
|
25
|
+
.filter(entry => PROJECTABLE_STATUSES.has(entry.status))
|
|
26
|
+
.map(renderLocalMemoryIndexCard)
|
|
27
|
+
.join("\n");
|
|
28
|
+
return `<!-- NOWWORK:MEMORY_INDEX:BEGIN schema=1 -->\n${cards}<!-- NOWWORK:MEMORY_INDEX:END -->`;
|
|
29
|
+
}
|
|
22
30
|
function compareEntries(left, right) {
|
|
23
31
|
const statusRank = (status) => status === "pinned" ? 0 : 1;
|
|
24
32
|
const priorityRank = (priority) => (priority === "high" ? 0 : priority === "medium" ? 1 : 2);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const CommandSchema = z.object({
|
|
2
|
+
"agent-dir": z.string().min(1).refine(isAbsolute),
|
|
3
|
+
"dry-run": z.literal(true),
|
|
4
|
+
}).strict();
|
|
5
|
+
const USAGE = "只读记忆盘点与转换预演,不写文件,不支持 --apply。\n"
|
|
6
|
+
+ "pnpm --filter @nowcrew/daemon exec tsx src/local-memory/inventory-cli.ts --agent-dir <单个Agent绝对路径> --dry-run\n"
|
|
7
|
+
+ "报告包含原始记忆,需作为敏感本地资料保存;退出码0仅代表盘点及格式预演无阻塞,不代表批准迁移。\n";
|
|
8
|
+
export async function runMemoryInventoryCommand(args, io) {
|
|
9
|
+
if (args.length === 1 && args[0] === "--help") {
|
|
10
|
+
io.out(USAGE);
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
let command;
|
|
14
|
+
try {
|
|
15
|
+
const parsed = parseArgs({ args, allowPositionals: false, options: {
|
|
16
|
+
"agent-dir": { type: "string" }, "dry-run": { type: "boolean" },
|
|
17
|
+
} });
|
|
18
|
+
command = CommandSchema.parse(parsed.values);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
io.err(USAGE);
|
|
22
|
+
return 2;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const scan = await scanMemorySources(command["agent-dir"]);
|
|
26
|
+
const scanComplete = scan.issues.length === 0;
|
|
27
|
+
const analysis = previewMemoryInventory(scan.sources, scanComplete);
|
|
28
|
+
const complete = scanComplete && !analysis.issues.some((issue) => issue.code === "missing_detail");
|
|
29
|
+
const issues = [...scan.issues, ...analysis.issues];
|
|
30
|
+
const referenced = new Set(analysis.entries.map((entry) => entry.sourceFields.file));
|
|
31
|
+
const files = scan.sources.map(({ path, bytes, sha256 }) => ({ path, bytes, sha256 }));
|
|
32
|
+
const report = {
|
|
33
|
+
schemaVersion: 1, mode: "dry-run", agentDir: scan.root, applySupported: false,
|
|
34
|
+
complete, scanComplete, requiresHumanReview: true,
|
|
35
|
+
coverage: ["MEMORY.md", "notes/**", ".crew/memory/pending/**", "根目录的 .memory-result.v1-*.json"],
|
|
36
|
+
limitations: ["不扫描历史执行目录;result清单仅覆盖Agent根目录。", "两遍摘要比对不等同跨文件原子快照,正式迁移仍需停止写入并重新核对。"],
|
|
37
|
+
limits: inventoryLimits, totalBytes: scan.totalBytes, files, ...analysis, issues,
|
|
38
|
+
pending: files.filter((file) => file.path.startsWith(".crew/memory/pending/")).map((file) => file.path),
|
|
39
|
+
orphanNotes: files.filter((file) => file.path.startsWith("notes/") && !referenced.has(file.path)).map((file) => file.path),
|
|
40
|
+
resultFiles: files.filter((file) => file.path.startsWith(".memory-result.v1-")).map((file) => file.path),
|
|
41
|
+
};
|
|
42
|
+
io.out(JSON.stringify(report, null, 2) + "\n");
|
|
43
|
+
return issues.length ? 1 : 0;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const code = error instanceof Error ? error.code ?? error.message : "inventory_failed";
|
|
47
|
+
io.err(`记忆盘点失败:${code}\n`);
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
52
|
+
process.exitCode = await runMemoryInventoryCommand(process.argv.slice(2), {
|
|
53
|
+
out: (text) => process.stdout.write(text), err: (text) => process.stderr.write(text),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
import { isAbsolute, resolve } from "node:path";
|
|
57
|
+
import { pathToFileURL } from "node:url";
|
|
58
|
+
import { parseArgs } from "node:util";
|
|
59
|
+
import { z } from "zod";
|
|
60
|
+
import { inventoryLimits, scanMemorySources } from "./inventory-source.js";
|
|
61
|
+
import { previewMemoryInventory } from "./inventory-preview.js";
|