@nowcrew/daemon 0.6.91-beta.9 → 0.6.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/execution-journal.js +1 -3
- package/dist/execution-protocol.js +1 -1
- package/dist/execution-runner.js +1 -3
- package/dist/local-execution-prompt.js +3 -8
- package/dist/local-executor.js +35 -14
- package/dist/local-memory/index-budget.js +3 -9
- package/dist/local-memory/prompt.js +4 -20
- package/dist/local-memory/read-cli.js +1 -36
- package/dist/local-memory/rollout.js +8 -53
- package/dist/local-memory/runtime.js +2 -5
- package/dist/local-memory/write.js +17 -108
- package/dist/local-runtime-environment.js +6 -1
- 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 +0 -3
- package/dist/message-file-preview.js +1 -11
- package/dist/prompt.js +1 -5
- package/dist/runner.js +1 -1
- package/dist/serve.js +3 -36
- package/dist/token.js +0 -1
- package/dist/workspace-control-handler.js +3 -16
- package/package.json +1 -1
- package/dist/local-memory/execution-workspace.js +0 -36
- package/dist/local-memory/full-index-search.js +0 -47
- package/dist/local-memory/migration-access.js +0 -118
- package/dist/local-memory/migration-cli.js +0 -54
- package/dist/local-memory/migration-io.js +0 -199
- package/dist/local-memory/migration-plan.js +0 -278
- package/dist/local-memory/migration-schema.js +0 -55
- package/dist/local-memory/migration-store.js +0 -424
- package/dist/local-memory/migration-tool-entry.js +0 -21
- package/dist/local-memory/storage-format.js +0 -152
- package/dist/memory-migration-controller.js +0 -84
- package/dist/memory-migration-inspect.js +0 -74
- package/dist/memory-migration-journal.js +0 -14
- package/dist/memory-migration-protocol.js +0 -14
- package/dist/memory-migration-runtime.js +0 -305
|
@@ -6,7 +6,6 @@ import { dslog } from "../slog.js";
|
|
|
6
6
|
import { LocalMemoryChangeSchema, LocalMemorySingleLineText, validateLocalMemoryChange, } from "./candidate.js";
|
|
7
7
|
import { LOCAL_MEMORY_INDEX_CODE_POINT_CAP, LOCAL_MEMORY_INDEX_ENTRY_CAP, LOCAL_MEMORY_TOTAL_CODE_POINT_CAP, renderGeneratedIndex, selectLocalMemoryIndexEntries, } from "./index-budget.js";
|
|
8
8
|
import { persistPendingMemoryCandidate, readPendingMemoryCandidates, removePendingMemoryCandidate, } from "./pending.js";
|
|
9
|
-
import { assertLocalMemoryStorageSize, detectLocalMemoryStorageVersion, LOCAL_MEMORY_GENERATED_INDEX_PATTERN, LOCAL_MEMORY_NOTE_BYTE_CAP, LOCAL_MEMORY_READ_BYTE_CAP, parseFullMemoryIndex, replaceFullMemoryIndex, selectLocalMemoryPreview, } from "./storage-format.js";
|
|
10
9
|
const StatusChangeSchema = z.object({
|
|
11
10
|
memoryId: LocalMemorySingleLineText,
|
|
12
11
|
expectedRevision: z.number().int().positive(),
|
|
@@ -91,14 +90,12 @@ function parseCards(section) {
|
|
|
91
90
|
});
|
|
92
91
|
}
|
|
93
92
|
export function parseLocalMemoryIndex(memory) {
|
|
94
|
-
if (detectLocalMemoryStorageVersion(memory) === 2)
|
|
95
|
-
return parseFullMemoryIndex(memory);
|
|
96
93
|
const section = indexSection(memory);
|
|
97
94
|
const generated = GENERATED_INDEX_PATTERN.exec(section)?.[0];
|
|
98
95
|
return parseCards(generated ?? section);
|
|
99
96
|
}
|
|
100
97
|
function parseUnmarkedMemoryIndex(memory) {
|
|
101
|
-
const section = indexSection(memory).replace(
|
|
98
|
+
const section = indexSection(memory).replace(GENERATED_INDEX_PATTERN, "");
|
|
102
99
|
return parseCards(section);
|
|
103
100
|
}
|
|
104
101
|
function replaceGeneratedIndex(memory, entries) {
|
|
@@ -125,36 +122,11 @@ function noteSection(id, revision, title, body) {
|
|
|
125
122
|
}
|
|
126
123
|
function notePattern(id) {
|
|
127
124
|
const escaped = id.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
128
|
-
return new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}
|
|
129
|
-
}
|
|
130
|
-
function uniqueNoteSection(content, id) {
|
|
131
|
-
const escaped = id.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
132
|
-
const begins = [...content.matchAll(new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}(?=\\s|-->)`, "gu"))];
|
|
133
|
-
const ends = [...content.matchAll(new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:END\\s+id=${escaped}\\s*-->`, "gu"))];
|
|
134
|
-
if (begins.length === 0 && ends.length === 0)
|
|
135
|
-
return null;
|
|
136
|
-
const section = notePattern(id).exec(content)?.[0];
|
|
137
|
-
if (begins.length !== 1 || ends.length !== 1 || section === undefined
|
|
138
|
-
|| (section.match(/NOWWORK:MEMORY_ENTRY:(?:BEGIN|END)/gu) ?? []).length !== 2) {
|
|
139
|
-
throw new Error("local_memory_note_anchor_invalid");
|
|
140
|
-
}
|
|
141
|
-
return section;
|
|
125
|
+
return new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}[^>]*-->[\\s\\S]*?<!--\\s*NOWWORK:MEMORY_ENTRY:END\\s+id=${escaped}\\s*-->\\s*`, "u");
|
|
142
126
|
}
|
|
143
127
|
function replaceNote(content, id, replacement) {
|
|
144
128
|
const pattern = notePattern(id);
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
function updateNoteRevision(content, id, revision) {
|
|
148
|
-
const section = uniqueNoteSection(content, id);
|
|
149
|
-
const beginning = `<!-- NOWWORK:MEMORY_ENTRY:BEGIN id=${id} `;
|
|
150
|
-
const ending = `<!-- NOWWORK:MEMORY_ENTRY:END id=${id} -->`;
|
|
151
|
-
if (section === null || content.split(beginning).length !== 2 || content.split(ending).length !== 2) {
|
|
152
|
-
throw new Error("local_memory_note_anchor_missing");
|
|
153
|
-
}
|
|
154
|
-
const updated = section.replace(/^(<!--\s*NOWWORK:MEMORY_ENTRY:BEGIN\s+id=[A-Za-z0-9_-]+\s+revision=)[1-9][0-9]*(\s*-->)/u, (_match, prefix, suffix) => `${prefix}${revision}${suffix}`);
|
|
155
|
-
if (!updated.startsWith(`${beginning}revision=${revision} -->`))
|
|
156
|
-
throw new Error("local_memory_note_anchor_missing");
|
|
157
|
-
return content.replace(notePattern(id), () => updated);
|
|
129
|
+
return pattern.test(content) ? content.replace(pattern, replacement) : null;
|
|
158
130
|
}
|
|
159
131
|
function validateCandidate(result) {
|
|
160
132
|
if (result.outcome === "no_change" && (result.changes.length > 0 || result.statusChanges.length > 0)) {
|
|
@@ -227,14 +199,14 @@ function detailCategoryFromFile(file) {
|
|
|
227
199
|
return DETAIL_CATEGORIES.has(category) ? category : null;
|
|
228
200
|
}
|
|
229
201
|
function readNoteBody(content, id) {
|
|
230
|
-
const
|
|
231
|
-
if (
|
|
202
|
+
const begin = content.indexOf(`<!-- NOWWORK:MEMORY_ENTRY:BEGIN id=${id}`);
|
|
203
|
+
if (begin < 0)
|
|
232
204
|
return null;
|
|
233
|
-
const bodyStart =
|
|
234
|
-
const bodyEnd =
|
|
205
|
+
const bodyStart = content.indexOf("\n\n", begin);
|
|
206
|
+
const bodyEnd = content.indexOf(`\n<!-- NOWWORK:MEMORY_ENTRY:END id=${id}`, bodyStart + 2);
|
|
235
207
|
if (bodyStart < 0 || bodyEnd < 0)
|
|
236
208
|
return null;
|
|
237
|
-
return
|
|
209
|
+
return content.slice(bodyStart + 2, bodyEnd).trim();
|
|
238
210
|
}
|
|
239
211
|
function archivedChange(entry, detailBody) {
|
|
240
212
|
const detailCategory = detailCategoryFromFile(entry.file);
|
|
@@ -277,12 +249,7 @@ async function readNote(path) {
|
|
|
277
249
|
const stat = await lstat(path);
|
|
278
250
|
if (!stat.isFile() || stat.isSymbolicLink())
|
|
279
251
|
throw new Error("local_memory_note_path_invalid");
|
|
280
|
-
|
|
281
|
-
throw new Error("local_memory_note_byte_limit");
|
|
282
|
-
const content = await readFile(path, "utf8");
|
|
283
|
-
if (Buffer.byteLength(content, "utf8") > LOCAL_MEMORY_NOTE_BYTE_CAP)
|
|
284
|
-
throw new Error("local_memory_note_byte_limit");
|
|
285
|
-
return content;
|
|
252
|
+
return await readFile(path, "utf8");
|
|
286
253
|
}
|
|
287
254
|
catch (error) {
|
|
288
255
|
if (error.code === "ENOENT")
|
|
@@ -302,11 +269,6 @@ async function atomicWrite(path, content) {
|
|
|
302
269
|
}
|
|
303
270
|
}
|
|
304
271
|
function projectIndex(memory, entries, protectedIds) {
|
|
305
|
-
if (detectLocalMemoryStorageVersion(memory) === 2) {
|
|
306
|
-
const updated = replaceFullMemoryIndex(memory, entries);
|
|
307
|
-
selectLocalMemoryPreview(updated);
|
|
308
|
-
return { memory: updated, omittedIds: [], reason: null };
|
|
309
|
-
}
|
|
310
272
|
const empty = replaceGeneratedIndex(memory, []);
|
|
311
273
|
if (codePointLength(empty) > LOCAL_MEMORY_TOTAL_CODE_POINT_CAP)
|
|
312
274
|
throw new Error("local_memory_memory_over_budget");
|
|
@@ -349,12 +311,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
349
311
|
const memoryPath = join(input.agentDir, "MEMORY.md");
|
|
350
312
|
await assertWritablePaths(input.agentDir, memoryPath);
|
|
351
313
|
return withApplyLease(input.agentDir, async () => {
|
|
352
|
-
const memoryStat = await lstat(memoryPath);
|
|
353
|
-
if (memoryStat.size > 16 * 1024 * 1024)
|
|
354
|
-
throw new Error("local_memory_storage_byte_limit");
|
|
355
314
|
const memory = await readFile(memoryPath, "utf8");
|
|
356
|
-
assertLocalMemoryStorageSize(memory);
|
|
357
|
-
const fullStorage = detectLocalMemoryStorageVersion(memory) === 2;
|
|
358
315
|
if (parseUnmarkedMemoryIndex(memory).length > 0)
|
|
359
316
|
throw new Error("local_memory_legacy_index_requires_migration");
|
|
360
317
|
let entries = [...parseLocalMemoryIndex(memory)];
|
|
@@ -385,26 +342,6 @@ export async function applyLocalMemoryResult(input) {
|
|
|
385
342
|
const pendingCandidates = [];
|
|
386
343
|
const resolvedPendingIds = new Set();
|
|
387
344
|
const noteUpdates = new Map();
|
|
388
|
-
const noteCache = new Map();
|
|
389
|
-
let totalReadBytes = Buffer.byteLength(memory, "utf8");
|
|
390
|
-
const readStoredNote = async (path) => {
|
|
391
|
-
const cached = noteUpdates.get(path) ?? noteCache.get(path);
|
|
392
|
-
if (cached !== undefined)
|
|
393
|
-
return cached;
|
|
394
|
-
const stat = await lstat(path).catch((error) => {
|
|
395
|
-
if (error.code === "ENOENT")
|
|
396
|
-
return null;
|
|
397
|
-
throw error;
|
|
398
|
-
});
|
|
399
|
-
if (stat !== null && totalReadBytes + stat.size > LOCAL_MEMORY_READ_BYTE_CAP)
|
|
400
|
-
throw new Error("local_memory_read_byte_limit");
|
|
401
|
-
const content = await readNote(path);
|
|
402
|
-
totalReadBytes += Buffer.byteLength(content, "utf8");
|
|
403
|
-
if (totalReadBytes > LOCAL_MEMORY_READ_BYTE_CAP)
|
|
404
|
-
throw new Error("local_memory_read_byte_limit");
|
|
405
|
-
noteCache.set(path, content);
|
|
406
|
-
return content;
|
|
407
|
-
};
|
|
408
345
|
let allowActiveEviction = false;
|
|
409
346
|
const randomId = input.randomId ?? randomUUID;
|
|
410
347
|
const executionId = input.executionId ?? "unknown";
|
|
@@ -446,21 +383,10 @@ export async function applyLocalMemoryResult(input) {
|
|
|
446
383
|
if (existingCreate !== undefined && !matchesCreateCandidate(existingCreate, change)) {
|
|
447
384
|
throw new Error("local_memory_duplicate_candidate");
|
|
448
385
|
}
|
|
449
|
-
if (existingCreate === undefined && entries.some((entry) => (
|
|
450
|
-
|
|
386
|
+
if (existingCreate === undefined && entries.some((entry) => normalize(entry.title) === normalize(change.title)
|
|
387
|
+
|| normalize(entry.summary) === normalize(change.summary))) {
|
|
451
388
|
throw new Error("local_memory_duplicate_candidate");
|
|
452
389
|
}
|
|
453
|
-
if (existingCreate !== undefined) {
|
|
454
|
-
if (existingCreate.revision !== 1 || existingCreate.status !== "active") {
|
|
455
|
-
throw new Error("local_memory_create_replay_conflict");
|
|
456
|
-
}
|
|
457
|
-
const stored = await readStoredNote(resolve(input.agentDir, existingCreate.file));
|
|
458
|
-
if (uniqueNoteSection(stored, id)?.trimEnd() !== noteSection(id, 1, change.title, change.detailBody).trimEnd()) {
|
|
459
|
-
throw new Error("local_memory_create_replay_conflict");
|
|
460
|
-
}
|
|
461
|
-
// A proven duplicate is a no-op; it must never rewrite a newer body or status.
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
390
|
}
|
|
465
391
|
const revision = existingCreate?.revision ?? (current === undefined ? 1 : current.revision + 1);
|
|
466
392
|
const entry = existingCreate ?? entryFromChange(change, id, revision, current?.status ?? "active");
|
|
@@ -474,7 +400,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
474
400
|
: item),
|
|
475
401
|
entry,
|
|
476
402
|
];
|
|
477
|
-
const allowEvictionForChange =
|
|
403
|
+
const allowEvictionForChange = change.operation === "create" && change.priority === "high";
|
|
478
404
|
const protectedIds = new Set(allowEvictionForChange ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
479
405
|
if (allowEvictionForChange)
|
|
480
406
|
allowActiveEviction = true;
|
|
@@ -491,7 +417,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
491
417
|
const notePath = resolve(input.agentDir, entry.file);
|
|
492
418
|
if (!isWithin(resolve(input.agentDir, "notes"), notePath))
|
|
493
419
|
throw new Error("local_memory_note_path_invalid");
|
|
494
|
-
const previous = await
|
|
420
|
+
const previous = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
495
421
|
const replacement = noteSection(id, revision, change.title, change.detailBody);
|
|
496
422
|
const existingSection = replaceNote(previous, id, replacement);
|
|
497
423
|
const next = existingSection ?? (current === undefined
|
|
@@ -509,7 +435,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
509
435
|
// Pending create candidates are durable overflow, not dead ends. Promote only
|
|
510
436
|
// when the current catalog has room; supersede/update candidates stay pending
|
|
511
437
|
// until a fresh revision-aware decision can safely replay them.
|
|
512
|
-
const pendingCandidatesOnDisk =
|
|
438
|
+
const pendingCandidatesOnDisk = await readPendingMemoryCandidates({ agentDir: input.agentDir }).catch((error) => {
|
|
513
439
|
if (!(error instanceof Error) || error.message !== "memory_pending_directory_over_budget")
|
|
514
440
|
throw error;
|
|
515
441
|
// 自动提升是可选工作,目录查询预算不应阻断本轮已验证的更新或腾位操作。
|
|
@@ -534,7 +460,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
534
460
|
|| normalize(entry.summary) === normalize(change.summary)));
|
|
535
461
|
if (duplicate !== undefined) {
|
|
536
462
|
const duplicatePath = resolve(input.agentDir, duplicate.file);
|
|
537
|
-
const duplicateBody = readNoteBody(await
|
|
463
|
+
const duplicateBody = readNoteBody(noteUpdates.get(duplicatePath) ?? await readNote(duplicatePath), duplicate.id);
|
|
538
464
|
if (duplicateBody === change.detailBody
|
|
539
465
|
|| duplicateBody === unwrappedNoteBody(change.title, change.detailBody).trim()) {
|
|
540
466
|
resolvedPendingIds.add(pending.pendingId);
|
|
@@ -552,7 +478,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
552
478
|
const notePath = resolve(input.agentDir, entry.file);
|
|
553
479
|
if (!isWithin(resolve(input.agentDir, "notes"), notePath))
|
|
554
480
|
throw new Error("local_memory_note_path_invalid");
|
|
555
|
-
const previous = await
|
|
481
|
+
const previous = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
556
482
|
const replacement = noteSection(id, 1, change.title, change.detailBody);
|
|
557
483
|
const existingSection = replaceNote(previous, id, replacement);
|
|
558
484
|
const next = existingSection ?? `${previous.trimEnd()}${previous.trimEnd() ? "\n\n" : ""}${replacement}`;
|
|
@@ -561,17 +487,6 @@ export async function applyLocalMemoryResult(input) {
|
|
|
561
487
|
createdIds.push(id);
|
|
562
488
|
resolvedPendingIds.add(pending.pendingId);
|
|
563
489
|
}
|
|
564
|
-
if (fullStorage) {
|
|
565
|
-
const changedMetadataIds = new Set([
|
|
566
|
-
...statusChangedIds,
|
|
567
|
-
...input.result.changes.flatMap(change => change.supersedesMemoryId === null ? [] : [change.supersedesMemoryId]),
|
|
568
|
-
]);
|
|
569
|
-
for (const id of changedMetadataIds) {
|
|
570
|
-
const entry = entries.find(item => item.id === id);
|
|
571
|
-
const notePath = resolve(input.agentDir, entry.file);
|
|
572
|
-
noteUpdates.set(notePath, updateNoteRevision(await readStoredNote(notePath), id, entry.revision));
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
490
|
let finalProjection = projectIndex(memory, entries, allowActiveEviction ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
576
491
|
if (finalProjection.reason !== null
|
|
577
492
|
&& (!allowActiveEviction || finalProjection.reason === "index_over_budget_pinned")) {
|
|
@@ -583,7 +498,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
583
498
|
.filter((entry) => entry?.status === "active");
|
|
584
499
|
for (const entry of omittedActiveEntries) {
|
|
585
500
|
const notePath = resolve(input.agentDir, entry.file);
|
|
586
|
-
const content = await
|
|
501
|
+
const content = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
587
502
|
const detailBody = readNoteBody(content, entry.id);
|
|
588
503
|
const change = detailBody === null ? null : archivedChange(entry, detailBody);
|
|
589
504
|
if (change === null)
|
|
@@ -604,12 +519,6 @@ export async function applyLocalMemoryResult(input) {
|
|
|
604
519
|
if (finalProjection.reason !== null)
|
|
605
520
|
throw new Error(finalProjection.reason);
|
|
606
521
|
}
|
|
607
|
-
// Complete every resource check before the first persistent write.
|
|
608
|
-
assertLocalMemoryStorageSize(finalProjection.memory);
|
|
609
|
-
for (const content of noteUpdates.values()) {
|
|
610
|
-
if (Buffer.byteLength(content, "utf8") > LOCAL_MEMORY_NOTE_BYTE_CAP)
|
|
611
|
-
throw new Error("local_memory_note_byte_limit");
|
|
612
|
-
}
|
|
613
522
|
for (const candidate of pendingCandidates)
|
|
614
523
|
await persistPendingMemoryCandidate({ agentDir: input.agentDir, candidate });
|
|
615
524
|
for (const [path, content] of noteUpdates)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { delimiter, join } from "node:path";
|
|
1
|
+
import { delimiter, join, resolve } from "node:path";
|
|
2
2
|
import { environmentForExecution } from "./agent-environments.js";
|
|
3
3
|
import { augmentedPath } from "./runtime-path.js";
|
|
4
4
|
import { sanitizeEnvVars } from "./runtime-env.js";
|
|
@@ -10,6 +10,7 @@ export function buildLocalRuntimeEnvironment(context) {
|
|
|
10
10
|
const { input, workspace, executionWorkspace, inheritedEnv, providerConfig, crewHome, contextThreadId, nativeThreadContextPath, reportContext, runtime } = context;
|
|
11
11
|
const agentEnvironment = sanitizeEnvVars(providerConfig.environmentPlan === undefined ? providerConfig.envVars
|
|
12
12
|
: environmentForExecution(providerConfig.environmentPlan, workspace.dir, input.projectContext?.primary?.root ?? executionWorkspace.runDir, input.projectContext?.primary?.projectId));
|
|
13
|
+
const boundImDecisionFile = input.launch.systemEnv?.CREW_BOUND_IM_DECISION_FILE;
|
|
13
14
|
return mergeRuntimeEnvironments([
|
|
14
15
|
inheritedEnv, agentEnvironment,
|
|
15
16
|
input.trainingProjectContext === undefined ? {} : machinePackageManagerEnv(inheritedEnv),
|
|
@@ -35,6 +36,10 @@ export function buildLocalRuntimeEnvironment(context) {
|
|
|
35
36
|
QA_REPORT_DELIVERY_CONTEXT: JSON.stringify(reportContext),
|
|
36
37
|
CREW_TASK_DIR: executionWorkspace.runDir,
|
|
37
38
|
CREW_TASK_LOG: executionWorkspace.workLogPath,
|
|
39
|
+
// CLI 可以从 Agent 主目录或项目目录调用,标记始终写回完成阶段读取的任务目录。
|
|
40
|
+
...(boundImDecisionFile ? {
|
|
41
|
+
CREW_BOUND_IM_DECISION_FILE: resolve(executionWorkspace.runDir, boundImDecisionFile),
|
|
42
|
+
} : {}),
|
|
38
43
|
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
39
44
|
XDG_CONFIG_HOME: join(workspace.homeDir, ".config"),
|
|
40
45
|
XDG_DATA_HOME: join(workspace.homeDir, ".local", "share"),
|
|
@@ -31,6 +31,7 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
31
31
|
let lastFailureTime = -Infinity;
|
|
32
32
|
let stopped = false;
|
|
33
33
|
let unsafeCleanup = false;
|
|
34
|
+
let isCleanupComplete;
|
|
34
35
|
let flight;
|
|
35
36
|
let controller;
|
|
36
37
|
let captureEpoch = 0;
|
|
@@ -50,8 +51,19 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
50
51
|
return module.captureWindowsMachineEnvironment(request);
|
|
51
52
|
});
|
|
52
53
|
const refresh = async (force, signal) => {
|
|
53
|
-
if (!selected.source || stopped
|
|
54
|
+
if (!selected.source || stopped)
|
|
54
55
|
return snapshot;
|
|
56
|
+
if (unsafeCleanup) {
|
|
57
|
+
try {
|
|
58
|
+
if (!isCleanupComplete?.())
|
|
59
|
+
return snapshot;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return snapshot;
|
|
63
|
+
}
|
|
64
|
+
unsafeCleanup = false;
|
|
65
|
+
isCleanupComplete = undefined;
|
|
66
|
+
}
|
|
55
67
|
if (!force && now() - lastFailureTime < (options.backoffMs ?? 30_000))
|
|
56
68
|
return snapshot;
|
|
57
69
|
const attemptedAt = new Date(now()).toISOString();
|
|
@@ -64,7 +76,7 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
64
76
|
return snapshot;
|
|
65
77
|
publish(snapshot.env, { state: "loading", attemptedAt, errorCode: null });
|
|
66
78
|
capturing = true;
|
|
67
|
-
const deadline = Date.now() + (options.captureTimeoutMs ?? (selected.source === "windows-registry" ? 8_000 :
|
|
79
|
+
const deadline = Date.now() + (options.captureTimeoutMs ?? (selected.source === "windows-registry" ? 8_000 : 10_000));
|
|
68
80
|
const capture = selected.source === "windows-registry" ? captureWindows : options.captureShell ?? captureShellMachineEnvironment;
|
|
69
81
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
70
82
|
try {
|
|
@@ -111,8 +123,10 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
111
123
|
}
|
|
112
124
|
catch (error) {
|
|
113
125
|
const errorCode = error instanceof MachineEnvironmentCaptureError ? error.code : selected.source === "windows-registry" ? "windows_environment_unavailable" : "shell_capture_failed";
|
|
114
|
-
if (errorCode === "shell_cleanup_failed")
|
|
126
|
+
if (errorCode === "shell_cleanup_failed") {
|
|
115
127
|
unsafeCleanup = true;
|
|
128
|
+
isCleanupComplete = error instanceof MachineEnvironmentCaptureError ? error.isCleanupComplete : undefined;
|
|
129
|
+
}
|
|
116
130
|
lastFailureTime = now();
|
|
117
131
|
return publish(lastGood?.env ?? baseline, { state: lastGood ? "stale" : "fallback", errorCode, attemptedAt });
|
|
118
132
|
}
|
|
@@ -129,7 +143,7 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
129
143
|
}
|
|
130
144
|
return flight;
|
|
131
145
|
}
|
|
132
|
-
if (stopped ||
|
|
146
|
+
if (stopped || selected.source === null)
|
|
133
147
|
return Promise.resolve(snapshot);
|
|
134
148
|
controller = new AbortController();
|
|
135
149
|
flight = refresh(acquireOptions?.force === true, controller.signal).finally(() => { flight = undefined; controller = undefined; });
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export class MachineEnvironmentCaptureError extends Error {
|
|
2
2
|
code;
|
|
3
|
-
|
|
3
|
+
isCleanupComplete;
|
|
4
|
+
constructor(code, isCleanupComplete) {
|
|
4
5
|
super(code);
|
|
5
6
|
this.code = code;
|
|
7
|
+
this.isCleanupComplete = isCleanupComplete;
|
|
6
8
|
this.name = "MachineEnvironmentCaptureError";
|
|
7
9
|
}
|
|
8
10
|
}
|
|
@@ -44,7 +44,11 @@ export async function runMachineEnvironmentProcess(options) {
|
|
|
44
44
|
stdio[collectFd] = "pipe";
|
|
45
45
|
if (options.metadataFd !== undefined)
|
|
46
46
|
stdio[options.metadataFd] = "pipe";
|
|
47
|
-
|
|
47
|
+
// launchd Background 的 I/O 策略会让 shell 初始化停滞;只调整短命采集子进程。
|
|
48
|
+
// taskpolicy 原地 exec,保持同一 PID/进程组、环境和私有管道。
|
|
49
|
+
const file = process.platform === "darwin" ? "/usr/sbin/taskpolicy" : options.file;
|
|
50
|
+
const args = process.platform === "darwin" ? ["-a", "--", options.file, ...options.args] : [...options.args];
|
|
51
|
+
const child = spawn(file, args, { cwd: options.cwd, env: options.env, detached: true, stdio });
|
|
48
52
|
const chunks = [];
|
|
49
53
|
let size = 0;
|
|
50
54
|
const metadata = [];
|
|
@@ -102,6 +106,22 @@ export async function runMachineEnvironmentProcess(options) {
|
|
|
102
106
|
try {
|
|
103
107
|
await cleanupOnce();
|
|
104
108
|
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const pid = child.pid;
|
|
111
|
+
if (error instanceof MachineEnvironmentCaptureError && error.code === "shell_cleanup_failed" && pid !== undefined) {
|
|
112
|
+
// 后续只验证旧进程组消失,不对可能复用的 PID 再发送终止信号。
|
|
113
|
+
throw new MachineEnvironmentCaptureError("shell_cleanup_failed", () => {
|
|
114
|
+
try {
|
|
115
|
+
process.kill(-pid, 0);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
return cause.code === "ESRCH";
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
105
125
|
finally {
|
|
106
126
|
for (const stream of child.stdio)
|
|
107
127
|
stream?.destroy();
|
package/dist/machine-info.js
CHANGED
|
@@ -25,7 +25,6 @@ import { RUNTIME_HEALTH_PROBE_CAPABILITY } from "./runtime-health.js";
|
|
|
25
25
|
import { probeDeepSeekHarnessAcp, deepSeekHarnessLauncher } from "./runtimes/deepseek-harness.js";
|
|
26
26
|
import { daemonHome } from "./computer-profile.js";
|
|
27
27
|
import { daemonReleaseProvenance } from "./release-provenance.js";
|
|
28
|
-
import { MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY } from "./memory-migration-protocol.js";
|
|
29
28
|
export { executableRuntimes } from "./runtime-capabilities.js";
|
|
30
29
|
const execFileP = promisify(execFile);
|
|
31
30
|
export const PROJECT_WORKSPACES_CAPABILITY = "project_workspaces_v1";
|
|
@@ -40,7 +39,6 @@ export const NATIVE_PROJECT_WORKSPACE_READINESS = Object.freeze({
|
|
|
40
39
|
});
|
|
41
40
|
export const DAEMON_CAPABILITIES = Object.freeze([
|
|
42
41
|
MARKETPLACE_CAPABILITY,
|
|
43
|
-
MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY,
|
|
44
42
|
"scheduled_job_v1",
|
|
45
43
|
"reply_origin_v1",
|
|
46
44
|
"origin_decision_v1",
|
|
@@ -75,7 +73,6 @@ export const daemonCapabilities = (runtimePlatform = process.platform, readiness
|
|
|
75
73
|
return Object.freeze(DAEMON_CAPABILITIES.filter((capability) => ((capability !== MARKETPLACE_CAPABILITY || nativeWorkspacePlatform)
|
|
76
74
|
&& (capability !== PROJECT_WORKSPACES_CAPABILITY || nativeWorkspaceReady)
|
|
77
75
|
&& (capability !== "message_file_preview_v1" || nativeWorkspacePlatform)
|
|
78
|
-
&& (nativeWorkspacePlatform || ![MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY].includes(capability))
|
|
79
76
|
&& (nativeWorkspacePlatform
|
|
80
77
|
|| (capability !== "project_skills_v1"
|
|
81
78
|
&& capability !== "project_skill_projection_v2"
|
|
@@ -9,7 +9,6 @@ import { z } from "zod";
|
|
|
9
9
|
import { isInternalEnvironmentPath } from "./workspace-fs.js";
|
|
10
10
|
import { opaqueKey, safeKey } from "./workspace.js";
|
|
11
11
|
import { AgentHandleSchema } from "./execution-protocol.js";
|
|
12
|
-
import { withMemoryAccess } from "./local-memory/migration-access.js";
|
|
13
12
|
const MAX_BYTES = 256 * 1024;
|
|
14
13
|
const MAX_BINARY_BYTES = 8 * 1024 * 1024;
|
|
15
14
|
const MAX_CONCURRENT_READS = 4;
|
|
@@ -232,16 +231,7 @@ export async function readSharedMessageFile(agentsRoot, rawRequest, registry) {
|
|
|
232
231
|
throw new SharedFileError("file_preview_busy");
|
|
233
232
|
activeReads++;
|
|
234
233
|
try {
|
|
235
|
-
|
|
236
|
-
const agentRoot = await realpath(resolve(agentsRoot, parsed.data.handle)).catch((error) => {
|
|
237
|
-
if (error.code === "ENOENT")
|
|
238
|
-
return undefined;
|
|
239
|
-
throw error;
|
|
240
|
-
});
|
|
241
|
-
// 记忆保护只用于该Agent根内资料,独立项目文件仍按原执行授权读取。
|
|
242
|
-
return await (agentRoot && inside(agentRoot, candidate.path)
|
|
243
|
-
? withMemoryAccess(agentRoot, () => readCandidate(candidate))
|
|
244
|
-
: readCandidate(candidate));
|
|
234
|
+
return await readCandidate(await resolveCandidate(agentsRoot, parsed.data, registry));
|
|
245
235
|
}
|
|
246
236
|
catch (error) {
|
|
247
237
|
if (error instanceof SharedFileError)
|
package/dist/prompt.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* claim-before-work、freshness/draft、"做完所有事再停"、inbox notice 语义、
|
|
6
6
|
* 分层记忆与压缩安全、协作礼仪。措辞为本项目原创。
|
|
7
7
|
*/
|
|
8
|
-
import { detectLocalMemoryStorageVersion, selectLocalMemoryPreview } from "./local-memory/storage-format.js";
|
|
9
8
|
/** work-log 注入上限(字符):防冷启动注入随文件膨胀。超限保头(目标/结论)+尾(最近进展),
|
|
10
9
|
* 截中段并提示读全文——全文仍在盘上,只限"默认推送量"不限"可获取量"。纯函数。 */
|
|
11
10
|
export const WORKLOG_INJECT_CAP = 8000;
|
|
@@ -68,10 +67,7 @@ export function capWorkLogForInject(workLog, cap = WORKLOG_INJECT_CAP) {
|
|
|
68
67
|
const tail = workLog.slice(-WORKLOG_TAIL);
|
|
69
68
|
return `${head}\n…(work-log 过长已截去中段,全文用 Read 读 $CREW_TASK_LOG)\n${tail}`;
|
|
70
69
|
}
|
|
71
|
-
export function capMemoryForInject(memory, cap = MEMORY_INJECT_CAP, maxBytes = Number.POSITIVE_INFINITY
|
|
72
|
-
if (detectLocalMemoryStorageVersion(memory) === 2) {
|
|
73
|
-
return selectLocalMemoryPreview(memory, query, { maxCodePoints: cap, maxBytes }).memory;
|
|
74
|
-
}
|
|
70
|
+
export function capMemoryForInject(memory, cap = MEMORY_INJECT_CAP, maxBytes = Number.POSITIVE_INFINITY) {
|
|
75
71
|
if (memory.length <= cap && Buffer.byteLength(memory, "utf8") <= maxBytes)
|
|
76
72
|
return memory;
|
|
77
73
|
const notice = "[MEMORY.md preview truncated; use crew-memory search/read for more.]\n";
|
package/dist/runner.js
CHANGED
|
@@ -80,7 +80,7 @@ async function runAgentWithMachineEnvironment(config, input, onActivity = defaul
|
|
|
80
80
|
? { scheduledExternalNotificationPolicy: input.scheduled.externalNotificationPolicy }
|
|
81
81
|
: {}),
|
|
82
82
|
...(input.wakeOrigin ? { wakeOrigin: input.wakeOrigin } : {}),
|
|
83
|
-
memory: capMemoryForInject(workspace.memory
|
|
83
|
+
memory: capMemoryForInject(workspace.memory),
|
|
84
84
|
...(resuming ? {} : { workLog: capWorkLogForInject(workspace.workLog) }),
|
|
85
85
|
collaborationToolsEnabled: config.codexCollaborationTools && runtime === "codex",
|
|
86
86
|
localMemoryV2Enabled: workspace.localMemoryV2Enabled ?? localMemoryV2Enabled,
|
package/dist/serve.js
CHANGED
|
@@ -14,7 +14,6 @@ import { reconcileExecutionJournal } from "./execution-recovery.js";
|
|
|
14
14
|
import { initReleaseSlog, reportDaemonConnectionClose, reportReleaseError } from "./release-channel-guard.js";
|
|
15
15
|
import { handleWorkspaceControlRequest } from "./workspace-control-handler.js";
|
|
16
16
|
import { inspectRaftWorkspace, importRaftWorkspace } from "./workspace-import.js";
|
|
17
|
-
import { withMemoryAccess } from "./local-memory/migration-access.js";
|
|
18
17
|
import { normalizeScheduledContext } from "./scheduled-report.js";
|
|
19
18
|
import { formatDaemonLogLine } from "./log-format.js";
|
|
20
19
|
import { reportAgentRunComplete } from "./scheduled-run-report.js";
|
|
@@ -51,8 +50,6 @@ import { probeRuntimeHealth } from "./runtime-health.js";
|
|
|
51
50
|
import { createServeMarketplace } from "./marketplace-serve.js";
|
|
52
51
|
import { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
53
52
|
import { resolveDaemonLocalStateRootFromEnv } from "./daemon-local-home.js";
|
|
54
|
-
import { createMemoryMigrationController } from "./memory-migration-controller.js";
|
|
55
|
-
import { createMemoryMigrationJournal, replayExecutionJournals } from "./memory-migration-journal.js";
|
|
56
53
|
import { bindStorageServerIdentity, createStorageIdentityAdmission, } from "./storage-serve-adapter.js";
|
|
57
54
|
export { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
58
55
|
// normalize.ts 的活动种类 → activity 枚举
|
|
@@ -74,8 +71,6 @@ export function serve(config, opts = {}) {
|
|
|
74
71
|
let reconnectTimer = null;
|
|
75
72
|
let stopPromise = null;
|
|
76
73
|
let executionJournal;
|
|
77
|
-
let migrationExecutionJournal;
|
|
78
|
-
let migrationJournalReady = false;
|
|
79
74
|
const injectedExecutionJournal = opts.execution?.journal;
|
|
80
75
|
const agentMemory = opts.execution?.dependencies?.agentMemory
|
|
81
76
|
?? (config.agentMemory === null ? undefined : createAgentMemoryBridge(config.agentMemory));
|
|
@@ -90,15 +85,6 @@ export function serve(config, opts = {}) {
|
|
|
90
85
|
startupGapMs: config.executionLimits.startupGapMs,
|
|
91
86
|
});
|
|
92
87
|
const localSlots = createSharedSlotManager(config.executionLimits);
|
|
93
|
-
const memoryMigrations = createMemoryMigrationController({ config, coordinator: hostCoordinator,
|
|
94
|
-
isAdmissionClosed: () => stopped || storageIdentityAdmission.isAdmissionClosed(),
|
|
95
|
-
journal: () => { if (!migrationJournalReady)
|
|
96
|
-
throw new Error("memory_migration_journal_unavailable"); return migrationExecutionJournal; }, send: frame => {
|
|
97
|
-
if (frame.type === "execution:completed")
|
|
98
|
-
completionRetransmitter.track(frame);
|
|
99
|
-
else if (ws?.readyState === WebSocket.OPEN)
|
|
100
|
-
ws.send(JSON.stringify(frame));
|
|
101
|
-
} });
|
|
102
88
|
const sharedSlots = hostCoordinatedSlotManager(localSlots, hostCoordinator);
|
|
103
89
|
const concurrencyTelemetry = createExecutionConcurrencyTelemetry(sharedSlots, {
|
|
104
90
|
...(opts.execution?.concurrencySnapshotIntervalMs === undefined
|
|
@@ -246,7 +232,7 @@ export function serve(config, opts = {}) {
|
|
|
246
232
|
return null;
|
|
247
233
|
};
|
|
248
234
|
const sendSnapshot = async (reqId) => {
|
|
249
|
-
const entries = (await
|
|
235
|
+
const entries = (await executionJournal.replay()).map(snapshotEntry).filter((entry) => entry !== null);
|
|
250
236
|
safeExecutionSend(ExecutionSnapshotSchema.parse({
|
|
251
237
|
type: "execution:snapshot", protocolVersion: 1, reqId, entries,
|
|
252
238
|
}));
|
|
@@ -327,12 +313,6 @@ export function serve(config, opts = {}) {
|
|
|
327
313
|
machineRuntime.open(openedSocket, { capabilities: capabilities.machineHello, beforePublish: Promise.resolve(), connectionId });
|
|
328
314
|
opts.onOpen?.(ws);
|
|
329
315
|
void sendSnapshot(`reconnect-${randomUUID()}`);
|
|
330
|
-
// 普通回执不等待维护日志IO;两类记录分别重放到同一发送器。
|
|
331
|
-
void migrationExecutionJournal.replay().then(entries => {
|
|
332
|
-
for (const entry of entries)
|
|
333
|
-
if (entry.completion && !entry.completionAcknowledged)
|
|
334
|
-
completionRetransmitter.track(entry.completion);
|
|
335
|
-
}).catch(() => dslog("memory_migration.journal_unavailable", "维护回执重放失败,普通回执继续", { level: "ERROR" }));
|
|
336
316
|
void executionJournal.replay()
|
|
337
317
|
.then((entries) => {
|
|
338
318
|
for (const entry of entries) {
|
|
@@ -407,8 +387,7 @@ export function serve(config, opts = {}) {
|
|
|
407
387
|
return;
|
|
408
388
|
if (frame.type === "execution:completion-ack") {
|
|
409
389
|
try {
|
|
410
|
-
|
|
411
|
-
await journal.acknowledgeCompletion(frame.executionId);
|
|
390
|
+
await executionJournal.acknowledgeCompletion(frame.executionId);
|
|
412
391
|
const attempts = completionRetransmitter.acknowledge(frame.executionId);
|
|
413
392
|
dslog("execution.completion_acknowledged", "execution completion ACK 已持久化", {
|
|
414
393
|
execution_id: frame.executionId,
|
|
@@ -434,8 +413,6 @@ export function serve(config, opts = {}) {
|
|
|
434
413
|
return;
|
|
435
414
|
}
|
|
436
415
|
if (frame.type === "execution:cancel") {
|
|
437
|
-
if (memoryMigrations.cancel(frame.executionId))
|
|
438
|
-
return;
|
|
439
416
|
if (!knownExecutionHashes.has(frame.executionId)
|
|
440
417
|
&& !executionRuns.has(frame.executionId)
|
|
441
418
|
&& await executionJournal.get(frame.executionId) === null)
|
|
@@ -758,8 +735,6 @@ export function serve(config, opts = {}) {
|
|
|
758
735
|
// ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
|
|
759
736
|
const r = msg;
|
|
760
737
|
setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
|
|
761
|
-
if (r.workspaceId && r.machineId)
|
|
762
|
-
memoryMigrations.setIdentity({ workspaceId: r.workspaceId, machineId: r.machineId });
|
|
763
738
|
if (opts.storageRuntime !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
|
|
764
739
|
const bindingSocket = ws;
|
|
765
740
|
bindStorageServerIdentity(opts.storageRuntime, { workspaceId: r.workspaceId, machineId: r.machineId }, {
|
|
@@ -826,8 +801,6 @@ export function serve(config, opts = {}) {
|
|
|
826
801
|
}
|
|
827
802
|
catch { /* server 会按请求超时处理 */ } }))
|
|
828
803
|
return;
|
|
829
|
-
if (await memoryMigrations.handle(msg))
|
|
830
|
-
return;
|
|
831
804
|
if (await agentAbilityRuntime.tryHandleControlMessage(msg, serverCapabilities, (frame) => ws?.send(JSON.stringify(frame))))
|
|
832
805
|
return;
|
|
833
806
|
// 导入 raft agent 工作区:inspect 反填 name/description;import 复制用户内容
|
|
@@ -842,7 +815,7 @@ export function serve(config, opts = {}) {
|
|
|
842
815
|
try {
|
|
843
816
|
const data2 = req.type === "raft:inspect"
|
|
844
817
|
? await inspectRaftWorkspace(req.path)
|
|
845
|
-
: await
|
|
818
|
+
: await importRaftWorkspace(req.path, join(config.agentsRoot, req.handle));
|
|
846
819
|
if (req.type === "raft:import") {
|
|
847
820
|
log(`📦 已导入 raft 工作区 → ${req.handle}: 复制 ${data2.copied.length} 项`);
|
|
848
821
|
}
|
|
@@ -1286,9 +1259,6 @@ export function serve(config, opts = {}) {
|
|
|
1286
1259
|
writeStderr: (line) => process.stderr.write(line),
|
|
1287
1260
|
});
|
|
1288
1261
|
await runProjectSkillStartupCleanup(config.agentsRoot, executionJournal, dslog);
|
|
1289
|
-
migrationExecutionJournal = createMemoryMigrationJournal(config.agentsRoot);
|
|
1290
|
-
await migrationExecutionJournal.reconcileAfterRestart().then(() => { migrationJournalReady = true; })
|
|
1291
|
-
.catch(() => dslog("memory_migration.journal_unavailable", "迁移日志恢复失败,普通执行继续,迁移请求单独报错", { level: "ERROR" }));
|
|
1292
1262
|
await connect();
|
|
1293
1263
|
})();
|
|
1294
1264
|
const stop = () => {
|
|
@@ -1306,7 +1276,6 @@ export function serve(config, opts = {}) {
|
|
|
1306
1276
|
const webSocketClosed = closeWebSocketWithinDeadline(ws, deadline.signal);
|
|
1307
1277
|
const pending = [
|
|
1308
1278
|
machineRuntime.stop(),
|
|
1309
|
-
memoryMigrations.stop(),
|
|
1310
1279
|
webSocketClosed,
|
|
1311
1280
|
updateController.drain(),
|
|
1312
1281
|
marketplace.stop(),
|
|
@@ -1325,8 +1294,6 @@ export function serve(config, opts = {}) {
|
|
|
1325
1294
|
if (executionJournal !== undefined) {
|
|
1326
1295
|
await executionJournal.close({ signal: deadline.signal });
|
|
1327
1296
|
}
|
|
1328
|
-
if (migrationExecutionJournal !== undefined)
|
|
1329
|
-
await migrationExecutionJournal.close({ signal: deadline.signal });
|
|
1330
1297
|
}
|
|
1331
1298
|
finally {
|
|
1332
1299
|
deadline.dispose();
|
package/dist/token.js
CHANGED
|
@@ -7,7 +7,6 @@ export async function mintAgentToken(serverUrl, machineToken, handle, displayNam
|
|
|
7
7
|
},
|
|
8
8
|
// 把本轮唤醒的线程根钉到 per-launch 令牌:服务端据此让 task/消息默认绑/回当前线程。
|
|
9
9
|
body: JSON.stringify({
|
|
10
|
-
memoryStateGuardVersion: 1,
|
|
11
10
|
handle,
|
|
12
11
|
...(displayName ? { displayName } : {}),
|
|
13
12
|
...(options.wakeThreadRoot ? { wakeThreadRoot: options.wakeThreadRoot } : {}),
|
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
/** 控制面文件/技能查看的按需处理,独立于执行生命周期。 */
|
|
2
|
-
import {
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
2
|
+
import { join } from "node:path";
|
|
4
3
|
import { readSharedMessageFile } from "./message-file-preview.js";
|
|
5
4
|
import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
|
|
6
5
|
import { listSkills } from "./skills.js";
|
|
7
6
|
import { listRuntimeModels } from "./list-models.js";
|
|
8
|
-
import { withMemoryAccess } from "./local-memory/migration-access.js";
|
|
9
|
-
async function listProtectedWorkspace(root, path) {
|
|
10
|
-
try {
|
|
11
|
-
await lstat(root);
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
if (error.code !== "ENOENT")
|
|
15
|
-
throw error;
|
|
16
|
-
return { root: resolve(root), path: path.replace(/^\/+/, ""), entries: [] };
|
|
17
|
-
}
|
|
18
|
-
return withMemoryAccess(root, () => listWorkspace(root, path));
|
|
19
|
-
}
|
|
20
7
|
export async function handleWorkspaceControlRequest(raw, agentsRoot, registry, models = listRuntimeModels) {
|
|
21
8
|
if (!raw || typeof raw !== "object")
|
|
22
9
|
return null;
|
|
@@ -28,8 +15,8 @@ export async function handleWorkspaceControlRequest(raw, agentsRoot, registry, m
|
|
|
28
15
|
try {
|
|
29
16
|
const root = join(agentsRoot, request.handle);
|
|
30
17
|
const data = request.type === "fs:read-shared" ? await readSharedMessageFile(agentsRoot, raw, registry)
|
|
31
|
-
: request.type === "fs:list" ? await
|
|
32
|
-
: request.type === "fs:read" ? await
|
|
18
|
+
: request.type === "fs:list" ? await listWorkspace(root, request.path)
|
|
19
|
+
: request.type === "fs:read" ? await readWorkspaceFile(root, request.path)
|
|
33
20
|
: request.type === "skills:list" ? await listSkills(request.runtime)
|
|
34
21
|
: { models: await models(request.runtime ?? "") };
|
|
35
22
|
return { type: "fs:result", reqId: request.reqId, ok: true, data };
|