@nowcrew/daemon 0.6.90 → 0.6.91-beta.10
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/types.js +1 -1
- package/dist/console.js +5 -4
- package/dist/daemon-update-controller.js +16 -8
- package/dist/daemon-updater.js +10 -22
- package/dist/execution-concurrency-telemetry.js +1 -0
- package/dist/execution-journal.js +3 -1
- package/dist/execution-protocol.js +2 -2
- package/dist/execution-runner.js +6 -3
- package/dist/external-output.js +2 -1
- package/dist/host-execution-coordinator.js +12 -6
- package/dist/i18n.js +4 -3
- package/dist/local-execution-prompt.js +8 -3
- package/dist/local-executor.js +16 -37
- package/dist/local-memory/execution-workspace.js +36 -0
- package/dist/local-memory/full-index-search.js +47 -0
- package/dist/local-memory/index-budget.js +9 -3
- package/dist/local-memory/migration-access.js +118 -0
- package/dist/local-memory/migration-cli.js +54 -0
- package/dist/local-memory/migration-io.js +199 -0
- package/dist/local-memory/migration-plan.js +283 -0
- package/dist/local-memory/migration-schema.js +55 -0
- package/dist/local-memory/migration-store.js +424 -0
- package/dist/local-memory/migration-tool-entry.js +21 -0
- package/dist/local-memory/prompt.js +20 -4
- package/dist/local-memory/read-cli.js +36 -1
- package/dist/local-memory/rollout.js +53 -8
- package/dist/local-memory/runtime.js +5 -2
- package/dist/local-memory/storage-format.js +152 -0
- package/dist/local-memory/write.js +108 -17
- package/dist/machine-environment-cache.js +18 -4
- package/dist/machine-environment-contract.js +3 -1
- package/dist/machine-environment-process.js +21 -1
- package/dist/machine-info.js +3 -0
- package/dist/marketplace-serve.js +25 -1
- package/dist/memory-migration-controller.js +84 -0
- package/dist/memory-migration-inspect.js +74 -0
- package/dist/memory-migration-journal.js +14 -0
- package/dist/memory-migration-protocol.js +14 -0
- package/dist/memory-migration-runtime.js +305 -0
- package/dist/message-file-preview.js +11 -1
- package/dist/normalize.js +9 -5
- package/dist/pre-admission-rejection.js +5 -4
- package/dist/prompt.js +5 -1
- package/dist/runner.js +1 -1
- package/dist/runtime-capabilities.js +2 -1
- package/dist/runtime-startup-gate.js +1 -0
- package/dist/runtimes/pi-runner.js +28 -0
- package/dist/runtimes/pi.js +17 -0
- package/dist/serve.js +37 -6
- package/dist/shared-execution-slots.js +2 -0
- package/dist/supervised-runtime.js +20 -0
- package/dist/token.js +1 -0
- package/dist/workspace-control-handler.js +16 -3
- package/package.json +1 -1
|
@@ -81,7 +81,7 @@ export const DaemonAbilityManifestSchema = z.object({
|
|
|
81
81
|
cases: z.array(SafeAbilityPathSchema).max(1_000).default([]),
|
|
82
82
|
}).strict().default({ cases: [] }),
|
|
83
83
|
compatibility: z.object({
|
|
84
|
-
providers: z.array(z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness"])).min(1),
|
|
84
|
+
providers: z.array(z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "pi"])).min(1),
|
|
85
85
|
minNowCrewVersion: z.string().regex(/^\d+\.\d+\.\d+$/u),
|
|
86
86
|
}).strict().optional(),
|
|
87
87
|
permissions: z.object({
|
package/dist/console.js
CHANGED
|
@@ -281,17 +281,18 @@ export function toConsoleLines(event, runtime) {
|
|
|
281
281
|
return text ? [{ stream: "tool_result", text: clip(text, TOOL_RESULT_CAP) }] : [];
|
|
282
282
|
}
|
|
283
283
|
if (e.type === "system" && e.subtype === "init") {
|
|
284
|
-
return [{ stream: "system", text: `● ${td("Claude session started")}` }];
|
|
284
|
+
return [{ stream: "system", text: `● ${td("Claude Code session started")}` }];
|
|
285
285
|
}
|
|
286
286
|
if (e.type === "thread.started") {
|
|
287
287
|
// thread.started is shared by Codex and ACP adapters; its shape does not
|
|
288
288
|
// identify the runtime. Use the executor-owned selection, not event fields.
|
|
289
289
|
const messages = {
|
|
290
|
-
claude: "Claude session started",
|
|
291
|
-
codex: "Codex session started",
|
|
292
|
-
kimi: "Kimi session started",
|
|
290
|
+
claude: "Claude Code session started",
|
|
291
|
+
codex: "Codex CLI session started",
|
|
292
|
+
kimi: "Kimi Code session started",
|
|
293
293
|
hermes: "Hermes session started",
|
|
294
294
|
opencode: "OpenCode session started",
|
|
295
|
+
pi: "Pi session started",
|
|
295
296
|
"deepseek-harness": "DeepSeek Harness session started",
|
|
296
297
|
};
|
|
297
298
|
const message = runtime && Object.hasOwn(messages, runtime)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dslog } from "./slog.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { isDaemonReleaseVersion } from "./daemon-release-version.js";
|
|
3
4
|
const DaemonUpdateMessageSchema = z.object({
|
|
@@ -42,15 +43,16 @@ export function createDaemonUpdateController(deps) {
|
|
|
42
43
|
failed(message.updateId, preparation.errorCode);
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
|
-
let
|
|
46
|
-
const releaseOnce =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
let releaseTask;
|
|
47
|
+
const releaseOnce = () => {
|
|
48
|
+
releaseTask ??= preparation.release().catch(error => {
|
|
49
|
+
releaseTask = undefined;
|
|
50
|
+
throw error;
|
|
51
|
+
});
|
|
52
|
+
return releaseTask;
|
|
51
53
|
};
|
|
52
|
-
deps.sendStatus({ type: "daemon:update-status", updateId: message.updateId, status: "restarting" });
|
|
53
54
|
try {
|
|
55
|
+
deps.sendStatus({ type: "daemon:update-status", updateId: message.updateId, status: "restarting" });
|
|
54
56
|
const handoff = deps.scheduleRestart(eligibility.serviceSpec);
|
|
55
57
|
const state = {
|
|
56
58
|
release: releaseOnce,
|
|
@@ -92,7 +94,13 @@ export function createDaemonUpdateController(deps) {
|
|
|
92
94
|
failed(message.updateId, "update_in_progress");
|
|
93
95
|
return true;
|
|
94
96
|
}
|
|
95
|
-
const done = execute(message).
|
|
97
|
+
const done = execute(message).catch(error => {
|
|
98
|
+
dslog("daemon.update_failed", "Daemon 更新或重启准备失败", {
|
|
99
|
+
level: "ERROR", update_id: message.updateId, operation: message.type,
|
|
100
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
101
|
+
});
|
|
102
|
+
failed(message.updateId, message.type === "daemon:restart" ? "restart_failed" : "install_failed");
|
|
103
|
+
}).finally(() => {
|
|
96
104
|
handled.add(message.updateId);
|
|
97
105
|
if (running?.id === message.updateId)
|
|
98
106
|
running = null;
|
package/dist/daemon-updater.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dslog } from "./slog.js";
|
|
1
2
|
import { readFile } from "node:fs/promises";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import { systemCommandRunner } from "./computer-service.js";
|
|
@@ -11,15 +12,14 @@ function registryArgs(registry) {
|
|
|
11
12
|
function isRegistryAuthFailure(stderr) {
|
|
12
13
|
return /(?:\bE401\b|\b401\b.*\bUnauthorized\b)/iu.test(stderr);
|
|
13
14
|
}
|
|
15
|
+
// Managed eligibility guarantees an exclusive installation and native service per profile.
|
|
16
|
+
// Other profiles share compute capacity, but do not share the package being replaced.
|
|
14
17
|
export async function prepareDaemonRestart(input) {
|
|
15
18
|
const local = input.localSlots.tryAcquireExclusive();
|
|
16
19
|
if (local === null)
|
|
17
20
|
return { ok: false, errorCode: "runtime_busy" };
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
local.release();
|
|
21
|
-
return { ok: false, errorCode: "runtime_busy" };
|
|
22
|
-
}
|
|
21
|
+
const acquiredAt = Date.now();
|
|
22
|
+
dslog("daemon.maintenance_barrier_acquired", "Daemon 已暂停接收执行,准备维护");
|
|
23
23
|
let released = false;
|
|
24
24
|
return {
|
|
25
25
|
ok: true,
|
|
@@ -27,8 +27,8 @@ export async function prepareDaemonRestart(input) {
|
|
|
27
27
|
if (released)
|
|
28
28
|
return;
|
|
29
29
|
released = true;
|
|
30
|
-
await host.release();
|
|
31
30
|
local.release();
|
|
31
|
+
dslog("daemon.maintenance_barrier_released", "维护屏障已释放", { held_ms: Date.now() - acquiredAt });
|
|
32
32
|
},
|
|
33
33
|
};
|
|
34
34
|
}
|
|
@@ -71,22 +71,10 @@ export async function installExactDaemonUpdate(input) {
|
|
|
71
71
|
|| installation.npmPrefix !== input.npmPrefix) {
|
|
72
72
|
return { ok: false, errorCode: "ineligible" };
|
|
73
73
|
}
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
return
|
|
77
|
-
const
|
|
78
|
-
if (host === null) {
|
|
79
|
-
local.release();
|
|
80
|
-
return { ok: false, errorCode: "runtime_busy" };
|
|
81
|
-
}
|
|
82
|
-
let released = false;
|
|
83
|
-
const release = async () => {
|
|
84
|
-
if (released)
|
|
85
|
-
return;
|
|
86
|
-
released = true;
|
|
87
|
-
await host.release();
|
|
88
|
-
local.release();
|
|
89
|
-
};
|
|
74
|
+
const barrier = await prepareDaemonRestart(input);
|
|
75
|
+
if (!barrier.ok)
|
|
76
|
+
return barrier;
|
|
77
|
+
const release = barrier.release;
|
|
90
78
|
const lockedInstallation = daemonGlobalInstallation(input.currentEntryPath ?? process.argv[1] ?? "", input.platform ?? process.platform);
|
|
91
79
|
if (lockedInstallation === null
|
|
92
80
|
|| lockedInstallation.packageRoot !== input.packageRoot
|
|
@@ -8,7 +8,7 @@ import { provePosixProcessGroupStopped } from "./execution-posix-stop-proof.js";
|
|
|
8
8
|
export { JournalLockedError, JournalLockCorruptionError } from "./execution-journal-lock.js";
|
|
9
9
|
const ExecutionIdSchema = z.string().uuid();
|
|
10
10
|
const TimestampSchema = z.string().datetime({ offset: true });
|
|
11
|
-
const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness"]);
|
|
11
|
+
const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "memory_migration", "pi"]);
|
|
12
12
|
const RUNTIME_READY_SUFFIX = ".runtime-ready";
|
|
13
13
|
const RawJournalEntrySchema = z.object({
|
|
14
14
|
executionId: ExecutionIdSchema,
|
|
@@ -523,6 +523,8 @@ export function createExecutionJournal(agentsRoot, options = {}) {
|
|
|
523
523
|
}
|
|
524
524
|
};
|
|
525
525
|
const pruneInternal = async () => {
|
|
526
|
+
if (options.retainCompleted)
|
|
527
|
+
return;
|
|
526
528
|
const protectedExecutionIds = new Set(options.protectedExecutionIds?.() ?? []);
|
|
527
529
|
const entries = await readAll();
|
|
528
530
|
const acknowledged = entries
|
|
@@ -13,7 +13,7 @@ const ExitCodeSchema = z.number().int().min(MIN_SIGNED_32_INTEGER).max(MAX_PG_IN
|
|
|
13
13
|
const ProviderErrorCategorySchema = z.enum(["auth", "quota", "rate_limit", "timeout", "network", "upstream", "cli", "unknown"]);
|
|
14
14
|
const ProviderHttpStatusSchema = z.number().int().min(100).max(599);
|
|
15
15
|
const ProviderErrorCodeSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9_.:-]+$/u);
|
|
16
|
-
const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness"]);
|
|
16
|
+
const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "pi"]);
|
|
17
17
|
const UnsafePathCharacterSchema = /[\p{Cc}<>:"/\\|?*]/u;
|
|
18
18
|
const WindowsReservedNameSchema = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i;
|
|
19
19
|
export const AgentHandleSchema = z.string().min(1).max(64).refine((handle) => handle !== "."
|
|
@@ -319,7 +319,7 @@ const RawExecutionCompletedSchema = z.object({
|
|
|
319
319
|
providerErrorCategory: ProviderErrorCategorySchema.optional(),
|
|
320
320
|
providerHttpStatus: ProviderHttpStatusSchema.optional(),
|
|
321
321
|
providerErrorCode: ProviderErrorCodeSchema.optional(),
|
|
322
|
-
runtime: RuntimeSchema,
|
|
322
|
+
runtime: z.union([RuntimeSchema, z.literal("memory_migration")]),
|
|
323
323
|
model: z.string().optional(),
|
|
324
324
|
resumed: z.boolean(),
|
|
325
325
|
finalText: z.string().optional(),
|
package/dist/execution-runner.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { isLocalMemoryV2Enabled, localMemoryV2Environment, resolveReposRoot } from "./config.js";
|
|
4
|
+
import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
|
|
4
5
|
import { DaemonToServerExecutionFrameSchema, ExecutionCompletedSchema, ExecutionRejectedSchema, ExecutionStartSchema, } from "./execution-protocol.js";
|
|
5
6
|
import { JournalConflictError } from "./execution-journal.js";
|
|
6
7
|
import { boundExecutionFrame } from "./execution-event-limit.js";
|
|
@@ -285,11 +286,12 @@ function admission(spec, config, dependencies, at) {
|
|
|
285
286
|
return { rejected: rejection(spec.executionId, "resource_limit", "Local execution capacity is exhausted", at) };
|
|
286
287
|
}
|
|
287
288
|
const permission = effectivePermission(spec, config);
|
|
288
|
-
if ((spec.runtime.name === "kimi" || spec.runtime.name === "hermes" || spec.runtime.name === "opencode")
|
|
289
|
+
if ((spec.runtime.name === "kimi" || spec.runtime.name === "hermes" || spec.runtime.name === "opencode" || spec.runtime.name === "pi")
|
|
289
290
|
&& permission !== "full_access") {
|
|
290
291
|
const displayName = spec.runtime.name === "kimi"
|
|
291
292
|
? "Kimi"
|
|
292
|
-
: spec.runtime.name === "hermes" ? "Hermes"
|
|
293
|
+
: spec.runtime.name === "hermes" ? "Hermes"
|
|
294
|
+
: spec.runtime.name === "pi" ? "Pi" : "OpenCode";
|
|
293
295
|
return {
|
|
294
296
|
rejected: rejection(spec.executionId, "local_policy_denied", `${displayName} cannot enforce ${permission} permission`, at),
|
|
295
297
|
};
|
|
@@ -837,8 +839,9 @@ export async function runExecution(config, input, dependencies) {
|
|
|
837
839
|
const localMemoryGuardInBase = localMemoryV2Environment(config.serverUrl) === "custom";
|
|
838
840
|
const boundedSystemPrompt = (context) => {
|
|
839
841
|
const enabled = context.workspace.localMemoryV2Enabled ?? localMemoryV2Enabled;
|
|
840
|
-
const systemPromptWithLocalFacts = withLocalExecutionFacts(`${spec.instructions.systemPrompt}${enabled && localMemoryGuardInBase ? renderLocalMemoryV2WriteGuard() : ""}`, baseSystemPromptBudget, {
|
|
842
|
+
const systemPromptWithLocalFacts = withLocalExecutionFacts(`${spec.instructions.systemPrompt}${enabled && localMemoryGuardInBase && detectLocalMemoryStorageVersion(context.workspace.memory) !== 2 ? renderLocalMemoryV2WriteGuard() : ""}`, baseSystemPromptBudget, {
|
|
841
843
|
...(selfTraining ? { isolateLocalMemory: true } : {}),
|
|
844
|
+
memoryQuery: spec.instructions.wakePrompt,
|
|
842
845
|
collaborationTools: config.codexCollaborationTools && spec.runtime.name === "codex",
|
|
843
846
|
});
|
|
844
847
|
const base = typeof systemPromptWithLocalFacts === "string"
|
package/dist/external-output.js
CHANGED
|
@@ -212,7 +212,8 @@ export function decodeExternalOutputEvent(runtime, event, decoder) {
|
|
|
212
212
|
}
|
|
213
213
|
if ((runtime === "hermes" && candidate.type === "hermes.acp.text_delta")
|
|
214
214
|
|| (runtime === "deepseek-harness" && candidate.type === "deepseek-harness.acp.text_delta")
|
|
215
|
-
|| (runtime === "opencode" && candidate.type === "opencode.text_delta")
|
|
215
|
+
|| (runtime === "opencode" && candidate.type === "opencode.text_delta")
|
|
216
|
+
|| (runtime === "pi" && candidate.type === "pi.text_delta")) {
|
|
216
217
|
return typeof candidate.text === "string" ? decoder.push(candidate.text) : [];
|
|
217
218
|
}
|
|
218
219
|
return candidate.type === undefined
|
|
@@ -286,13 +286,19 @@ export function createHostExecutionCoordinator(options = {}) {
|
|
|
286
286
|
...Array.from({ length: executionSlots }, (_, slot) => join(root, "slots", String(slot))),
|
|
287
287
|
join(root, "memory-prune-slots", "0"),
|
|
288
288
|
];
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
289
|
+
try {
|
|
290
|
+
for (const target of targets) {
|
|
291
|
+
const lease = await acquireLease(target);
|
|
292
|
+
if (lease === null) {
|
|
293
|
+
await retryRelease("exclusive_execution_partial", () => Promise.all(leases.map((owned) => owned.close())).then(() => undefined));
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
leases.push(lease);
|
|
294
297
|
}
|
|
295
|
-
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
await retryRelease("exclusive_execution_partial", () => Promise.all(leases.map(owned => owned.close())).then(() => undefined));
|
|
301
|
+
throw error;
|
|
296
302
|
}
|
|
297
303
|
let releasePromise = null;
|
|
298
304
|
return {
|
package/dist/i18n.js
CHANGED
|
@@ -16,12 +16,13 @@ export function detectDaemonLang(env = process.env) {
|
|
|
16
16
|
?? "en";
|
|
17
17
|
}
|
|
18
18
|
const zh = {
|
|
19
|
-
"Claude session started": "Claude 会话启动",
|
|
20
|
-
"Codex session started": "Codex 会话启动",
|
|
21
|
-
"Kimi session started": "Kimi 会话启动",
|
|
19
|
+
"Claude Code session started": "Claude Code 会话启动",
|
|
20
|
+
"Codex CLI session started": "Codex CLI 会话启动",
|
|
21
|
+
"Kimi Code session started": "Kimi Code 会话启动",
|
|
22
22
|
"Hermes session started": "Hermes 会话启动",
|
|
23
23
|
"OpenCode session started": "OpenCode 会话启动",
|
|
24
24
|
"DeepSeek Harness session started": "DeepSeek Harness 会话启动",
|
|
25
|
+
"Pi session started": "Pi 会话启动",
|
|
25
26
|
"Session started": "会话启动",
|
|
26
27
|
"Plan updated": "计划更新",
|
|
27
28
|
"Thinking (~{{tokens}} tokens, content not streamed by Claude Code)": "思考(约 {{tokens}} tokens,Claude Code 未输出思考正文)",
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* 本机执行提示词的合成与预算裁剪。
|
|
3
3
|
*
|
|
4
4
|
* server 提示词是权威部分,绝不裁剪;项目工作区提示词是绑定执行的强制部分,放不下就直接失败;
|
|
5
|
-
*
|
|
5
|
+
* 本机事实与旧格式预览可按预算省略;schema2身份和置顶规则必须保留,放不下则拒绝执行。
|
|
6
6
|
*/
|
|
7
|
+
import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
|
|
7
8
|
import { buildCollaborationToolsPrompt, buildRuntimeWorkspacePrompt, capMemoryForInject, capWorkLogForInject, } from "./prompt.js";
|
|
8
9
|
const trainingProjectJson = (project) => JSON.stringify({
|
|
9
10
|
repositoryKey: project.repositoryKey,
|
|
@@ -57,10 +58,14 @@ export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
|
|
|
57
58
|
}
|
|
58
59
|
const remaining = maxBytes - requiredBytes - Math.max(0, reservedPromptBytes);
|
|
59
60
|
const factsBytes = Buffer.byteLength(localFacts, "utf8");
|
|
60
|
-
if (factsBytes > remaining)
|
|
61
|
+
if (factsBytes > remaining) {
|
|
62
|
+
if (!options.isolateLocalMemory && detectLocalMemoryStorageVersion(workspace.memory) === 2) {
|
|
63
|
+
throw new Error("local_memory_identity_budget_exceeded");
|
|
64
|
+
}
|
|
61
65
|
return `${serverPrompt}${collaborationPrompt}${mandatoryWorkspacePrompt}`;
|
|
66
|
+
}
|
|
62
67
|
const memoryHeading = "\n\n## Injected MEMORY.md (bounded local context)\n";
|
|
63
|
-
const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory, undefined, remaining - factsBytes - Buffer.byteLength(memoryHeading, "utf8"));
|
|
68
|
+
const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory, undefined, remaining - factsBytes - Buffer.byteLength(memoryHeading, "utf8"), options.memoryQuery);
|
|
64
69
|
const memorySection = memory === "" ? "" : memoryHeading + memory;
|
|
65
70
|
const workLogSection = workLog === "" ? "" : `\n\n## Injected work-log (bounded local context)\n${workLog}`;
|
|
66
71
|
const workLogBudget = remaining - factsBytes - Buffer.byteLength(memorySection, "utf8");
|
package/dist/local-executor.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createInterface } from "node:readline";
|
|
|
6
6
|
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { cleanupSelfTrainingRunDir } from "./self-training-workspace.js";
|
|
9
|
-
import {
|
|
9
|
+
import { rotateAgentSession, safeKey, } from "./workspace.js";
|
|
10
10
|
import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, spawnClaude, } from "./runtimes/claude.js";
|
|
11
11
|
import { spawnCodex } from "./runtimes/codex.js";
|
|
12
12
|
import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
@@ -42,10 +42,12 @@ import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLoc
|
|
|
42
42
|
import { CodexStartupStageParser } from "./codex-startup-stage.js";
|
|
43
43
|
import { formatRuntimeStartupError } from "./runtime-startup-error.js";
|
|
44
44
|
import { logRuntimeEnvironmentContract } from "./runtime-environment-telemetry.js";
|
|
45
|
-
import { commitLocalMemoryResultAfterExit,
|
|
45
|
+
import { commitLocalMemoryResultAfterExit, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, } from "./local-memory/runtime.js";
|
|
46
46
|
import { prepareLocalMemoryPrompt } from "./local-memory/prompt.js";
|
|
47
|
-
import { reportLocalMemoryV2Deferred
|
|
47
|
+
import { reportLocalMemoryV2Deferred } from "./local-memory/rollout.js";
|
|
48
|
+
import { prepareExecutionMemoryWorkspace } from "./local-memory/execution-workspace.js";
|
|
48
49
|
import { localMemoryV2Environment } from "./config.js";
|
|
50
|
+
import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
|
|
49
51
|
export class RuntimeRunningCallbackTimeoutError extends Error {
|
|
50
52
|
constructor(runtime, timeoutMs) {
|
|
51
53
|
super(`${runtime} runtime-running callback timed out after ${timeoutMs}ms`);
|
|
@@ -89,7 +91,7 @@ export function awaitExit(child) {
|
|
|
89
91
|
}
|
|
90
92
|
export function exitActivity(runtime, exitCode, stderrTail) {
|
|
91
93
|
if ((runtime === "codex" || runtime === "kimi" || runtime === "hermes"
|
|
92
|
-
|| runtime === "opencode" || runtime === "deepseek-harness" || exitCode === -1) && exitCode !== 0) {
|
|
94
|
+
|| runtime === "opencode" || runtime === "deepseek-harness" || runtime === "pi" || exitCode === -1) && exitCode !== 0) {
|
|
93
95
|
return {
|
|
94
96
|
kind: "error",
|
|
95
97
|
label: "运行出错",
|
|
@@ -97,7 +99,7 @@ export function exitActivity(runtime, exitCode, stderrTail) {
|
|
|
97
99
|
};
|
|
98
100
|
}
|
|
99
101
|
if ((runtime === "kimi" || runtime === "hermes" || runtime === "opencode"
|
|
100
|
-
|| runtime === "deepseek-harness") && exitCode === 0) {
|
|
102
|
+
|| runtime === "deepseek-harness" || runtime === "pi") && exitCode === 0) {
|
|
101
103
|
return { kind: "done", label: "本轮结束" };
|
|
102
104
|
}
|
|
103
105
|
return null;
|
|
@@ -211,37 +213,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
211
213
|
: "high"
|
|
212
214
|
: runtime.reasoning;
|
|
213
215
|
const providerFp = providerFingerprint(runtime.name, providerConfig);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
agentsRoot: input.launch.agentsRoot,
|
|
218
|
-
handle: input.handle,
|
|
219
|
-
cliPath: input.launch.cliPath,
|
|
220
|
-
executionId: input.executionId,
|
|
221
|
-
...(input.keyMode === undefined ? {} : { keyMode: input.keyMode }),
|
|
222
|
-
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
223
|
-
...(input.resumeKey === undefined ? {} : { resumeKey: input.resumeKey }),
|
|
224
|
-
...(input.sessionContextFingerprint === undefined
|
|
225
|
-
? {}
|
|
226
|
-
: { sessionContextFingerprint: input.sessionContextFingerprint }),
|
|
227
|
-
...(input.launch.description ? { description: input.launch.description } : {}),
|
|
228
|
-
...(isolatedMaintenance ? { skipLocalMemory: true } : {}),
|
|
229
|
-
}), dependencies.cancellation);
|
|
230
|
-
}
|
|
231
|
-
catch (error) {
|
|
232
|
-
logLocalMemoryContextPrepareFailure({
|
|
233
|
-
executionId: input.executionId,
|
|
234
|
-
agentHandle: input.handle,
|
|
235
|
-
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
236
|
-
}, "workspace_prepare", error);
|
|
237
|
-
throw error;
|
|
238
|
-
}
|
|
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 };
|
|
216
|
+
const preparedMemory = await prepareExecutionMemoryWorkspace(input, isolatedMaintenance, dependencies.cancellation);
|
|
217
|
+
let workspace = preparedMemory.workspace;
|
|
218
|
+
const memoryRollout = preparedMemory.mode;
|
|
243
219
|
input = { ...input, localMemoryV2Enabled: memoryRollout.enabled };
|
|
244
|
-
const localMemoryV2Initialized =
|
|
220
|
+
const localMemoryV2Initialized = preparedMemory.initialized;
|
|
245
221
|
let materialized = null;
|
|
246
222
|
let knownAttachmentDirectory = null;
|
|
247
223
|
const startupReservationState = { current: null };
|
|
@@ -391,7 +367,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
391
367
|
let baseWakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
392
368
|
memoryPruneTraceId = isolatedMaintenance ? null : parseMemoryPruneTraceId(baseWakePrompt);
|
|
393
369
|
if (memoryRollout.deferred)
|
|
394
|
-
reportLocalMemoryV2Deferred(input.executionId, input.handle, memoryPruneTraceId !== null || input.maintenancePurpose === "memory_prune");
|
|
370
|
+
reportLocalMemoryV2Deferred(input.executionId, input.handle, memoryPruneTraceId !== null || input.maintenancePurpose === "memory_prune", memoryRollout.legacyFallback);
|
|
395
371
|
if (memoryPruneTraceId !== null) {
|
|
396
372
|
executionWorkspace = await resolveMemoryPruneWorkLog(executionWorkspace, input);
|
|
397
373
|
promptContext = { ...promptContext, workspace: executionWorkspace };
|
|
@@ -411,8 +387,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
411
387
|
agentDir: executionWorkspace.dir, executionId: input.executionId, agentHandle: input.handle,
|
|
412
388
|
enabled: input.localMemoryV2Enabled === true, query: baseWakePrompt,
|
|
413
389
|
resultPath: localMemoryResultPath, maxBytes: input.maxSystemPromptBytes,
|
|
414
|
-
|
|
390
|
+
storageVersion: detectLocalMemoryStorageVersion(executionWorkspace.memory),
|
|
391
|
+
strictRollout: !isolatedMaintenance && !memoryRollout.legacyFallback && (localMemoryV2Environment(input.launch.serverUrl) === "dev"
|
|
392
|
+
|| detectLocalMemoryStorageVersion(executionWorkspace.memory) === 2),
|
|
415
393
|
deferred: memoryRollout.deferred,
|
|
394
|
+
...(memoryRollout.legacyFallback ? { legacyFallback: true } : {}),
|
|
416
395
|
renderBase: (reservedPromptBytes) => resolvePrompt(input.systemPrompt, { ...promptContext, reservedPromptBytes }),
|
|
417
396
|
});
|
|
418
397
|
localMemoryResultPath = memoryPrompt.resultPath;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { prepareWorkspace } from "../workspace.js";
|
|
2
|
+
import { awaitWithCancellation } from "../runtime-cancellation.js";
|
|
3
|
+
import { logLocalMemoryContextPrepareFailure } from "../local-memory-telemetry.js";
|
|
4
|
+
import { resolveLocalMemoryV2Rollout } from "./rollout.js";
|
|
5
|
+
import { initializeLocalMemoryV2ForWorkspace } from "./runtime.js";
|
|
6
|
+
import { withMemoryAccess } from "./migration-access.js";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
export async function prepareExecutionMemoryWorkspace(input, isolated, cancellation) {
|
|
9
|
+
try {
|
|
10
|
+
const prepare = async (context) => {
|
|
11
|
+
const prepared = await prepareWorkspace({
|
|
12
|
+
agentsRoot: input.launch.agentsRoot, handle: input.handle, cliPath: input.launch.cliPath,
|
|
13
|
+
executionId: input.executionId,
|
|
14
|
+
...(input.keyMode === undefined ? {} : { keyMode: input.keyMode }),
|
|
15
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
16
|
+
...(input.resumeKey === undefined ? {} : { resumeKey: input.resumeKey }),
|
|
17
|
+
...(input.sessionContextFingerprint === undefined ? {} : { sessionContextFingerprint: input.sessionContextFingerprint }),
|
|
18
|
+
...(input.launch.description ? { description: input.launch.description } : {}),
|
|
19
|
+
...(isolated ? { skipLocalMemory: true } : {}),
|
|
20
|
+
});
|
|
21
|
+
const workspace = context?.created ? { ...prepared, agentHomePreexisted: false } : prepared;
|
|
22
|
+
const mode = await resolveLocalMemoryV2Rollout({ workspace, handle: input.handle, serverUrl: input.launch.serverUrl,
|
|
23
|
+
requested: input.localMemoryV2Enabled === true, selfTraining: isolated,
|
|
24
|
+
...(input.launch.description ? { description: input.launch.description } : {}) });
|
|
25
|
+
const initialized = await initializeLocalMemoryV2ForWorkspace(workspace, mode.enabled);
|
|
26
|
+
return { workspace: { ...workspace, localMemoryV2Enabled: mode.enabled }, mode, initialized };
|
|
27
|
+
};
|
|
28
|
+
return await awaitWithCancellation(isolated ? prepare()
|
|
29
|
+
: withMemoryAccess(join(input.launch.agentsRoot, input.handle), prepare, { create: true }), cancellation);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logLocalMemoryContextPrepareFailure({ executionId: input.executionId, agentHandle: input.handle,
|
|
33
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }) }, "workspace_prepare", error);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { parseFullMemoryIndex, LOCAL_MEMORY_GENERATED_INDEX_PATTERN } from "./storage-format.js";
|
|
2
|
+
const excerpt = (text, size) => Array.from(text).slice(0, size).join("");
|
|
3
|
+
/** 查询完整元数据;只在明确的历史入口返回非有效记录。正文仍按引用和哈希分页读取。 */
|
|
4
|
+
export function searchFullMemoryIndex(memory, sha256, input, outputBytes) {
|
|
5
|
+
const records = parseFullMemoryIndex(memory);
|
|
6
|
+
const block = LOCAL_MEMORY_GENERATED_INDEX_PATTERN.exec(memory);
|
|
7
|
+
const prefix = memory.slice(0, block.index);
|
|
8
|
+
let line = (prefix.match(/\n/gu) ?? []).length + 1;
|
|
9
|
+
let offset = Array.from(prefix).length;
|
|
10
|
+
const positions = new Map();
|
|
11
|
+
for (const part of block[0].matchAll(/[^\n]*(?:\n|$)/gu)) {
|
|
12
|
+
const id = /^- id: ([A-Za-z0-9_-]+)\r?\n?$/u.exec(part[0])?.[1];
|
|
13
|
+
if (id)
|
|
14
|
+
positions.set(id, { line, offset });
|
|
15
|
+
line += 1;
|
|
16
|
+
offset += Array.from(part[0]).length;
|
|
17
|
+
}
|
|
18
|
+
const terms = input.query.normalize("NFKC").toLowerCase().split(/\s+/u).filter(Boolean);
|
|
19
|
+
const candidates = records.filter(entry => {
|
|
20
|
+
const active = entry.status === "active" || entry.status === "pinned";
|
|
21
|
+
const content = `${entry.id} ${entry.title} ${entry.summary} ${entry.triggers.join(" ")}`.normalize("NFKC").toLowerCase();
|
|
22
|
+
return active !== input.history && terms.every(term => content.includes(term));
|
|
23
|
+
});
|
|
24
|
+
const matches = [];
|
|
25
|
+
let bytes = 512, hasMore = false;
|
|
26
|
+
for (const entry of candidates) {
|
|
27
|
+
const position = positions.get(entry.id);
|
|
28
|
+
if (!position)
|
|
29
|
+
throw new Error("memory_storage_position_invalid");
|
|
30
|
+
if (position.line <= input.afterLine)
|
|
31
|
+
continue;
|
|
32
|
+
const item = { ...position, id: entry.id, status: entry.status, revision: entry.revision,
|
|
33
|
+
file: entry.file, anchor: entry.anchor, heading: excerpt(entry.title, 96),
|
|
34
|
+
title: excerpt(entry.title, 120), summary: excerpt(entry.summary, 240), text: excerpt(entry.summary, 240) };
|
|
35
|
+
const size = Buffer.byteLength(JSON.stringify(item), "utf8") + 1;
|
|
36
|
+
if (matches.length >= input.limit || bytes + size > outputBytes) {
|
|
37
|
+
hasMore = true;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
matches.push(item);
|
|
41
|
+
bytes += size;
|
|
42
|
+
}
|
|
43
|
+
if (hasMore && matches.length === 0)
|
|
44
|
+
throw new Error("memory_output_too_large");
|
|
45
|
+
return { file: "MEMORY.md", storageVersion: 2, sha256, matches, matchCount: candidates.length,
|
|
46
|
+
hasMore, complete: !hasMore, nextAfterLine: hasMore ? matches.at(-1).line : null };
|
|
47
|
+
}
|
|
@@ -50,14 +50,17 @@ export function selectLocalMemoryIndexEntries(entries, budget, options = {}) {
|
|
|
50
50
|
.map((entry) => entry.id);
|
|
51
51
|
const protectedIds = options.protectedIds ?? new Set();
|
|
52
52
|
const protectedEntries = projectable.filter((entry) => protectedIds.has(entry.id)).sort(compareEntries);
|
|
53
|
-
const candidates = projectable.filter((entry) => !protectedIds.has(entry.id)).sort(compareEntries);
|
|
53
|
+
const candidates = projectable.filter((entry) => !protectedIds.has(entry.id)).sort((left, right) => (options.relevance?.(right) ?? 0) - (options.relevance?.(left) ?? 0) || compareEntries(left, right));
|
|
54
54
|
const selected = [];
|
|
55
55
|
let renderedLength = 0;
|
|
56
|
+
let renderedBytes = 0;
|
|
56
57
|
for (const entry of protectedEntries) {
|
|
57
58
|
selected.push(entry);
|
|
58
59
|
renderedLength += cardLength(entry, selected.length > 1);
|
|
60
|
+
renderedBytes += Buffer.byteLength(renderLocalMemoryIndexCard(entry), "utf8") + (selected.length > 1 ? 1 : 0);
|
|
59
61
|
}
|
|
60
|
-
if (selected.length > budget.entryCap || renderedLength > budget.indexCodePointCap
|
|
62
|
+
if (selected.length > budget.entryCap || renderedLength > budget.indexCodePointCap
|
|
63
|
+
|| renderedBytes > (budget.indexByteCap ?? Infinity)) {
|
|
61
64
|
return {
|
|
62
65
|
entries: selected,
|
|
63
66
|
omittedIds: [...omittedIds, ...candidates.map((entry) => entry.id)],
|
|
@@ -65,12 +68,15 @@ export function selectLocalMemoryIndexEntries(entries, budget, options = {}) {
|
|
|
65
68
|
};
|
|
66
69
|
}
|
|
67
70
|
for (const entry of candidates) {
|
|
68
|
-
|
|
71
|
+
const bytes = Buffer.byteLength(renderLocalMemoryIndexCard(entry), "utf8") + (selected.length > 0 ? 1 : 0);
|
|
72
|
+
if (selected.length >= budget.entryCap || renderedLength + cardLength(entry, selected.length > 0) > budget.indexCodePointCap
|
|
73
|
+
|| renderedBytes + bytes > (budget.indexByteCap ?? Infinity)) {
|
|
69
74
|
omittedIds.push(entry.id);
|
|
70
75
|
continue;
|
|
71
76
|
}
|
|
72
77
|
selected.push(entry);
|
|
73
78
|
renderedLength += cardLength(entry, selected.length > 1);
|
|
79
|
+
renderedBytes += bytes;
|
|
74
80
|
}
|
|
75
81
|
return {
|
|
76
82
|
entries: selected,
|