@evo-dev/core 0.0.1-alpha.1 → 0.0.1-alpha.11

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.
Files changed (66) hide show
  1. package/assets/skills/coding/knowledge-distillation/SKILL.md +117 -114
  2. package/assets/skills/coding/knowledge-distillation/references/knowledge-distillation-methods.md +11 -7
  3. package/assets/team/agents/code-reviewer.md +48 -0
  4. package/assets/team/agents/docs-maintainer.md +51 -0
  5. package/assets/team/agents/implementation-engineer.md +51 -0
  6. package/assets/team/agents/product-scope-analyst.md +58 -0
  7. package/assets/team/agents/release-engineer.md +55 -0
  8. package/assets/team/agents/security-boundary-reviewer.md +50 -0
  9. package/assets/team/agents/solution-architect.md +51 -0
  10. package/assets/team/agents/verification-engineer.md +51 -0
  11. package/assets/team/team.md +102 -0
  12. package/dist/config/index.js +925 -97
  13. package/dist/index.js +13107 -5618
  14. package/package.json +5 -1
  15. package/src/agents/index.ts +56 -264
  16. package/src/code-agent-traces/index.ts +520 -0
  17. package/src/config/index.ts +5 -0
  18. package/src/config/paths.ts +1 -1
  19. package/src/config/settings.ts +149 -0
  20. package/src/config/store.ts +2 -0
  21. package/src/daemon/index.ts +99 -50
  22. package/src/evolution/candidates/index.ts +564 -0
  23. package/src/evolution/control/index.ts +20 -0
  24. package/src/evolution/evidence/analysis.ts +533 -0
  25. package/src/evolution/evidence/index.ts +3 -0
  26. package/src/evolution/evidence/session-memory/analysis.ts +281 -0
  27. package/src/evolution/evidence/session-memory/constants.ts +9 -0
  28. package/src/evolution/evidence/session-memory/index.ts +7 -0
  29. package/src/evolution/evidence/session-memory/paths.ts +29 -0
  30. package/src/evolution/evidence/session-memory/policy.ts +39 -0
  31. package/src/evolution/evidence/session-memory/segment.ts +202 -0
  32. package/src/evolution/evidence/session-memory/sensitivity.ts +335 -0
  33. package/src/evolution/evidence/session-memory/state-machine.ts +249 -0
  34. package/src/evolution/evidence/session-memory/storage.ts +379 -0
  35. package/src/evolution/evidence/session-memory/types.ts +221 -0
  36. package/src/evolution/evidence/session-memory/updater.ts +191 -0
  37. package/src/evolution/formatters.ts +169 -0
  38. package/src/evolution/index.ts +16 -2356
  39. package/src/evolution/knowledge/index.ts +5427 -0
  40. package/src/evolution/paths.ts +44 -0
  41. package/src/evolution/processor/distillation.ts +518 -0
  42. package/src/evolution/processor/index.ts +3 -0
  43. package/src/evolution/processor/process.ts +528 -0
  44. package/src/{learning → evolution/review}/index.ts +10 -14
  45. package/src/evolution/schema.ts +568 -0
  46. package/src/evolution/shared.ts +758 -0
  47. package/src/evolution/triggers/classification.ts +102 -0
  48. package/src/evolution/triggers/index.ts +295 -0
  49. package/src/hooks/index.ts +438 -179
  50. package/src/index.ts +12 -3
  51. package/src/projects/index.ts +453 -0
  52. package/src/runtime-logs/index.ts +490 -24
  53. package/src/team/index.ts +1429 -185
  54. package/src/team/mcp.ts +9 -5
  55. package/src/team/prompts.ts +141 -0
  56. package/src/utils/errors.ts +13 -0
  57. package/src/utils/fs.ts +40 -0
  58. package/src/utils/hash.ts +9 -0
  59. package/src/utils/ids.ts +12 -0
  60. package/src/utils/index.ts +7 -0
  61. package/src/utils/parsing.ts +11 -0
  62. package/src/utils/text.ts +18 -0
  63. package/src/utils/time.ts +5 -0
  64. package/src/workflow/index.ts +3 -21
  65. package/src/project/index.ts +0 -507
  66. package/src/task/index.ts +0 -840
@@ -1,5 +1,12 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import {
3
+ type SessionMemoryPolicySnapshot,
4
+ createDefaultSessionMemoryPolicy,
5
+ parseSessionMemoryPolicy,
6
+ } from "../evolution/evidence/session-memory/index.ts";
1
7
  import { type HookSettings, createDefaultHookSettings, parseHookSettings } from "../hooks/index.ts";
2
8
  import { EvoDevConfigError, describeType } from "./errors.ts";
9
+ import { resolveEvoDevPaths } from "./paths.ts";
3
10
 
4
11
  export interface PluginSettings {
5
12
  enabled: boolean;
@@ -7,6 +14,22 @@ export interface PluginSettings {
7
14
  autoSyncAgents?: boolean;
8
15
  }
9
16
 
17
+ export interface MemorySettings {
18
+ autoAccept: boolean;
19
+ runtimeInjection: boolean;
20
+ staleReview: boolean;
21
+ lexicalIndex: boolean;
22
+ sessionMemory: SessionMemoryPolicySnapshot;
23
+ }
24
+
25
+ export interface EvolutionSettings {
26
+ automation: {
27
+ knowledge: boolean;
28
+ semanticKnowledge: boolean;
29
+ recommendations: boolean;
30
+ };
31
+ }
32
+
10
33
  export interface EvoDevSettings {
11
34
  version: 1;
12
35
  platform: {
@@ -29,6 +52,8 @@ export interface EvoDevSettings {
29
52
  };
30
53
  hooks: HookSettings;
31
54
  teamRuntime: TeamRuntimeSettings;
55
+ memory: MemorySettings;
56
+ evolution: EvolutionSettings;
32
57
  }
33
58
 
34
59
  export type SettingsInput = Partial<{
@@ -45,6 +70,10 @@ export type SettingsInput = Partial<{
45
70
  doctor: Partial<EvoDevSettings["doctor"]>;
46
71
  hooks: unknown;
47
72
  teamRuntime: Partial<TeamRuntimeSettings>;
73
+ memory: Partial<MemorySettings>;
74
+ evolution: Partial<{
75
+ automation: Partial<EvolutionSettings["automation"]>;
76
+ }>;
48
77
  }>;
49
78
 
50
79
  export interface TeamRuntimeSettings {
@@ -52,6 +81,7 @@ export interface TeamRuntimeSettings {
52
81
  defaultModel: string | null;
53
82
  defaultThinkingLevel: string | null;
54
83
  recordTranscript: boolean;
84
+ displayMode: "normal" | "development";
55
85
  }
56
86
 
57
87
  export function createDefaultSettings(os: string = process.platform): EvoDevSettings {
@@ -83,6 +113,8 @@ export function createDefaultSettings(os: string = process.platform): EvoDevSett
83
113
  },
84
114
  hooks: createDefaultHookSettings(),
85
115
  teamRuntime: createDefaultTeamRuntimeSettings(),
116
+ memory: createDefaultMemorySettings(),
117
+ evolution: createDefaultEvolutionSettings(),
86
118
  };
87
119
  }
88
120
 
@@ -92,6 +124,27 @@ export function createDefaultTeamRuntimeSettings(): TeamRuntimeSettings {
92
124
  defaultModel: null,
93
125
  defaultThinkingLevel: null,
94
126
  recordTranscript: false,
127
+ displayMode: "normal",
128
+ };
129
+ }
130
+
131
+ export function createDefaultMemorySettings(): MemorySettings {
132
+ return {
133
+ autoAccept: true,
134
+ runtimeInjection: true,
135
+ staleReview: true,
136
+ lexicalIndex: true,
137
+ sessionMemory: createDefaultSessionMemoryPolicy(),
138
+ };
139
+ }
140
+
141
+ export function createDefaultEvolutionSettings(): EvolutionSettings {
142
+ return {
143
+ automation: {
144
+ knowledge: true,
145
+ semanticKnowledge: false,
146
+ recommendations: false,
147
+ },
95
148
  };
96
149
  }
97
150
 
@@ -135,11 +188,33 @@ export function mergeSettings(
135
188
  ...defaults.teamRuntime,
136
189
  ...existing.teamRuntime,
137
190
  },
191
+ memory: {
192
+ ...defaults.memory,
193
+ ...existing.memory,
194
+ },
195
+ evolution: {
196
+ ...defaults.evolution,
197
+ ...existing.evolution,
198
+ automation: {
199
+ ...defaults.evolution.automation,
200
+ ...existing.evolution?.automation,
201
+ },
202
+ },
138
203
  };
139
204
 
140
205
  return parseSettings(merged);
141
206
  }
142
207
 
208
+ export async function readRuntimeInjectionSettings(homeDir?: string): Promise<MemorySettings> {
209
+ const paths = resolveEvoDevPaths(homeDir);
210
+ try {
211
+ return parseSettings(JSON.parse(await readFile(paths.settingsPath, "utf8"))).memory;
212
+ } catch (error) {
213
+ if (isNotFoundError(error)) return createDefaultMemorySettings();
214
+ throw error;
215
+ }
216
+ }
217
+
143
218
  export function parseSettings(value: unknown): EvoDevSettings {
144
219
  const root = expectRecord(value, "settings");
145
220
  const version = root.version;
@@ -184,11 +259,64 @@ export function parseSettings(value: unknown): EvoDevSettings {
184
259
  root.teamRuntime ?? createDefaultTeamRuntimeSettings(),
185
260
  "settings.teamRuntime",
186
261
  ),
262
+ memory: parseMemorySettings(root.memory ?? createDefaultMemorySettings(), "settings.memory"),
263
+ evolution: parseEvolutionSettings(
264
+ root.evolution ?? createDefaultEvolutionSettings(),
265
+ "settings.evolution",
266
+ ),
187
267
  };
188
268
 
189
269
  return parsed;
190
270
  }
191
271
 
272
+ function parseEvolutionSettings(value: unknown, path: string): EvolutionSettings {
273
+ const input = expectRecord(value, path);
274
+ const defaults = createDefaultEvolutionSettings();
275
+ const automation = expectRecord(input.automation ?? defaults.automation, `${path}.automation`);
276
+ return {
277
+ automation: {
278
+ knowledge:
279
+ automation.knowledge === undefined
280
+ ? defaults.automation.knowledge
281
+ : expectBoolean(automation.knowledge, `${path}.automation.knowledge`),
282
+ semanticKnowledge:
283
+ automation.semanticKnowledge === undefined
284
+ ? defaults.automation.semanticKnowledge
285
+ : expectBoolean(automation.semanticKnowledge, `${path}.automation.semanticKnowledge`),
286
+ recommendations:
287
+ automation.recommendations === undefined
288
+ ? defaults.automation.recommendations
289
+ : expectBoolean(automation.recommendations, `${path}.automation.recommendations`),
290
+ },
291
+ };
292
+ }
293
+
294
+ function parseMemorySettings(value: unknown, path: string): MemorySettings {
295
+ const input = expectRecord(value, path);
296
+ const defaults = createDefaultMemorySettings();
297
+ return {
298
+ autoAccept:
299
+ input.autoAccept === undefined
300
+ ? defaults.autoAccept
301
+ : expectBoolean(input.autoAccept, `${path}.autoAccept`),
302
+ runtimeInjection:
303
+ input.runtimeInjection === undefined
304
+ ? defaults.runtimeInjection
305
+ : expectBoolean(input.runtimeInjection, `${path}.runtimeInjection`),
306
+ staleReview:
307
+ input.staleReview === undefined
308
+ ? defaults.staleReview
309
+ : expectBoolean(input.staleReview, `${path}.staleReview`),
310
+ lexicalIndex:
311
+ input.lexicalIndex === undefined
312
+ ? defaults.lexicalIndex
313
+ : expectBoolean(input.lexicalIndex, `${path}.lexicalIndex`),
314
+ sessionMemory: parseSessionMemoryPolicy(
315
+ isPlainRecord(input.sessionMemory) ? input.sessionMemory : defaults.sessionMemory,
316
+ ),
317
+ };
318
+ }
319
+
192
320
  function parseTeamRuntimeSettings(value: unknown, path: string): TeamRuntimeSettings {
193
321
  const input = expectRecord(value, path);
194
322
  const defaults = createDefaultTeamRuntimeSettings();
@@ -211,9 +339,20 @@ function parseTeamRuntimeSettings(value: unknown, path: string): TeamRuntimeSett
211
339
  input.recordTranscript === undefined
212
340
  ? defaults.recordTranscript
213
341
  : expectBoolean(input.recordTranscript, `${path}.recordTranscript`),
342
+ displayMode: parseTeamRuntimeDisplayMode(input.displayMode, defaults.displayMode, path),
214
343
  };
215
344
  }
216
345
 
346
+ function parseTeamRuntimeDisplayMode(
347
+ value: unknown,
348
+ fallback: TeamRuntimeSettings["displayMode"],
349
+ path: string,
350
+ ): TeamRuntimeSettings["displayMode"] {
351
+ if (value === undefined) return fallback;
352
+ if (value === "normal" || value === "development") return value;
353
+ throw new EvoDevConfigError(`Invalid ${path}.displayMode; expected normal or development`);
354
+ }
355
+
217
356
  function parsePluginSettings(value: unknown, path: string): PluginSettings {
218
357
  const input = expectRecord(value, path);
219
358
  const parsed: PluginSettings = {
@@ -239,6 +378,16 @@ function expectRecord(value: unknown, path: string): Record<string, unknown> {
239
378
  return value as Record<string, unknown>;
240
379
  }
241
380
 
381
+ function isPlainRecord(value: unknown): value is Record<string, unknown> {
382
+ return typeof value === "object" && value !== null && !Array.isArray(value);
383
+ }
384
+
385
+ function isNotFoundError(error: unknown): boolean {
386
+ return (
387
+ error instanceof Error && "code" in error && (error as NodeJS.ErrnoException).code === "ENOENT"
388
+ );
389
+ }
390
+
242
391
  function expectString(value: unknown, path: string): string {
243
392
  if (typeof value !== "string" || value.length === 0) {
244
393
  throw new EvoDevConfigError(`Invalid ${path}; expected non-empty string`);
@@ -1,5 +1,6 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname } from "node:path";
3
+ import { ensureOkfKnowledgeBase } from "../evolution/knowledge/index.ts";
3
4
  import { EvoDevConfigError } from "./errors.ts";
4
5
  import { type EvoDevPaths, resolveEvoDevPaths } from "./paths.ts";
5
6
  import { type EvoDevRegistry, createDefaultRegistry, parseRegistry } from "./registry.ts";
@@ -102,6 +103,7 @@ export async function initializeCoreConfig(homeDir?: string): Promise<CoreConfig
102
103
  export async function ensureKnowledgeBaseFiles(paths: EvoDevPaths): Promise<void> {
103
104
  await mkdir(paths.knowledgeDir, { recursive: true });
104
105
  await mkdir(paths.evosCasesDir, { recursive: true });
106
+ await ensureOkfKnowledgeBase(paths.homeDir);
105
107
  await writeTextIfMissing(
106
108
  `${paths.knowledgeDir}/README.md`,
107
109
  [
@@ -2,16 +2,23 @@ import { randomBytes } from "node:crypto";
2
2
  import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
3
3
  import { type IncomingMessage, createServer } from "node:http";
4
4
  import { dirname, join } from "node:path";
5
- import { listEvolutionTriggers, processEvolutionTriggers } from "../evolution/index.ts";
5
+ import { listEvolutionTriggers, processEvolutionTriggers } from "../evolution/control/index.ts";
6
6
  import { listObservabilityEvents } from "../observability/index.ts";
7
7
  import {
8
8
  type TeamRuntimeAdapter,
9
+ isActiveTeamAgentStatus,
10
+ isIdleTeamAgentStatus,
11
+ isMidTurnTeamAgentStatus,
9
12
  listTeamRuns,
13
+ markTeamMessagesDelivered,
14
+ readPendingTeamMessagesForRole,
10
15
  reconcileTeamRun,
11
16
  resumeTeamRun,
17
+ schedulePendingTeamMessageDelivery,
12
18
  sendTeamMessage,
13
19
  spawnTeamRole,
14
20
  stopTeamRole,
21
+ updateTeamAgentHookState,
15
22
  } from "../team/index.ts";
16
23
 
17
24
  export interface DaemonPaths {
@@ -182,12 +189,42 @@ export async function handleDaemonRequest(
182
189
  const removed = await cleanupDaemonState(input.homeDir, input.token ?? "");
183
190
  return ok({ stopped: true, removed }, warnings);
184
191
  }
185
- if (input.path === "/teams/send" && input.method === "POST") {
192
+ if (
193
+ (input.path === "/team/message/enqueue" || input.path === "/teams/send") &&
194
+ input.method === "POST"
195
+ ) {
186
196
  return dashboardMutation(
187
197
  await sendDashboardTeamMessage(input.homeDir, input.body, input.runtimeAdapter),
188
198
  warnings,
189
199
  );
190
200
  }
201
+ if (input.path === "/team/agent/state" && input.method === "POST") {
202
+ return dashboardMutation(
203
+ await updateDashboardTeamAgentState(input.homeDir, input.body),
204
+ warnings,
205
+ );
206
+ }
207
+ if (input.path === "/team/message/claim" && input.method === "POST") {
208
+ return ok(await claimDashboardTeamMessages(input.homeDir, input.body), warnings);
209
+ }
210
+ if (input.path === "/team/message/delivered" && input.method === "POST") {
211
+ return dashboardMutation(
212
+ await markDashboardTeamMessagesDelivered(input.homeDir, input.body),
213
+ warnings,
214
+ );
215
+ }
216
+ if (
217
+ (input.path === "/team/reconcile" || input.path === "/teams/reconcile") &&
218
+ input.method === "POST"
219
+ ) {
220
+ await schedulePendingTeamMessageDelivery({
221
+ homeDir: input.homeDir,
222
+ runtimeAdapter: input.runtimeAdapter,
223
+ }).catch((error) =>
224
+ warnings.push(`Team delivery scheduling unavailable: ${describeError(error)}`),
225
+ );
226
+ return ok(await collectTeamStatus(input.homeDir, warnings, input.runtimeAdapter), warnings);
227
+ }
191
228
  if (input.path === "/teams/spawn" && input.method === "POST") {
192
229
  return dashboardMutation(
193
230
  await spawnDashboardTeamRole(input.homeDir, input.body, input.runtimeAdapter),
@@ -206,9 +243,6 @@ export async function handleDaemonRequest(
206
243
  warnings,
207
244
  );
208
245
  }
209
- if (input.path === "/teams/reconcile" && input.method === "POST") {
210
- return ok(await collectTeamStatus(input.homeDir, warnings, input.runtimeAdapter), warnings);
211
- }
212
246
  if (input.path === "/evolution/process" && input.method === "POST") {
213
247
  return dashboardMutation(
214
248
  await processDashboardEvolutionTriggers(input.homeDir, input.body),
@@ -217,8 +251,6 @@ export async function handleDaemonRequest(
217
251
  }
218
252
  if (input.method !== "GET") return notFound(warnings);
219
253
 
220
- if (input.path === "/tasks")
221
- return ok(await collectTaskSummaries(input.homeDir, warnings), warnings);
222
254
  if (input.path === "/observability/events")
223
255
  return ok(await collectObservabilitySummaries(input.homeDir, warnings), warnings);
224
256
  if (input.path === "/memory/candidates")
@@ -373,33 +405,6 @@ function isAllowedLocalOrigin(origin: string | null): boolean {
373
405
  }
374
406
  }
375
407
 
376
- async function collectTaskSummaries(homeDir: string, warnings: string[]): Promise<unknown[]> {
377
- const root = join(homeDir, ".evodev", "STATE", "tasks");
378
- if (!(await pathExists(root))) {
379
- warnings.push("Task store not found; returning empty tasks.");
380
- return [];
381
- }
382
- const contracts = await collectNamedFiles(root, "contract.json");
383
- const summaries: unknown[] = [];
384
- for (const file of contracts) {
385
- try {
386
- const contract = JSON.parse(await readFile(file, "utf8"));
387
- summaries.push(
388
- sanitizeMetadata({
389
- taskId: contract.taskId,
390
- status: contract.status,
391
- mode: contract.route?.mode ?? null,
392
- workflowId: contract.route?.workflowId ?? null,
393
- verificationStatus: contract.verification?.status ?? null,
394
- }),
395
- );
396
- } catch {
397
- warnings.push(`Skipped unreadable task contract: ${file}`);
398
- }
399
- }
400
- return summaries;
401
- }
402
-
403
408
  async function collectObservabilitySummaries(
404
409
  homeDir: string,
405
410
  warnings: string[],
@@ -541,10 +546,9 @@ async function collectTeamStatus(
541
546
  thinkingLevel: agent.thinkingLevel,
542
547
  status: agent.status,
543
548
  paneId: agent.tmux.paneId,
544
- canReceiveMessages:
545
- agent.status === "running" ||
546
- agent.status === "recovering" ||
547
- agent.status === "recreated",
549
+ canReceiveMessages: isActiveTeamAgentStatus(agent.status),
550
+ isIdle: isIdleTeamAgentStatus(agent.status),
551
+ isMidTurn: isMidTurnTeamAgentStatus(agent.status),
548
552
  nativeSessionRecorded: agent.nativeSession.sessionId !== null,
549
553
  updatedAt: agent.updatedAt,
550
554
  })),
@@ -552,7 +556,8 @@ async function collectTeamStatus(
552
556
  notifications: status.notifications.map((notification) => ({
553
557
  ok: notification.ok,
554
558
  error: notification.error ?? null,
555
- deliveredTo: notification.deliveredTo ?? null,
559
+ delivery: notification.delivery ?? null,
560
+ queuedFor: notification.queuedFor ?? null,
556
561
  })),
557
562
  }) as DaemonTeamStatusSummary;
558
563
  } catch (error) {
@@ -577,6 +582,61 @@ async function sendDashboardTeamMessage(
577
582
  });
578
583
  }
579
584
 
585
+ async function updateDashboardTeamAgentState(homeDir: string, body: unknown): Promise<unknown> {
586
+ const input = expectRequestBody(body);
587
+ const result = await updateTeamAgentHookState({
588
+ homeDir,
589
+ runId: expectBodyString(input, "runId"),
590
+ roleId: expectBodyString(input, "roleId"),
591
+ hookEvent: expectBodyString(input, "hookEvent"),
592
+ });
593
+ return {
594
+ ok: true,
595
+ roleId: result.agent.roleId,
596
+ status: result.agent.status,
597
+ statusPath: result.statusPath,
598
+ };
599
+ }
600
+
601
+ async function claimDashboardTeamMessages(homeDir: string, body: unknown): Promise<unknown> {
602
+ const input = expectRequestBody(body);
603
+ const runId = expectBodyString(input, "runId");
604
+ const roleId = expectBodyString(input, "roleId");
605
+ const messages = await readPendingTeamMessagesForRole({
606
+ homeDir,
607
+ runId,
608
+ roleId,
609
+ limit: optionalBodyNumber(input.limit),
610
+ });
611
+ return {
612
+ runId,
613
+ roleId,
614
+ messages,
615
+ };
616
+ }
617
+
618
+ async function markDashboardTeamMessagesDelivered(
619
+ homeDir: string,
620
+ body: unknown,
621
+ ): Promise<unknown> {
622
+ const input = expectRequestBody(body);
623
+ const messageIdsValue = input.messageIds;
624
+ if (!Array.isArray(messageIdsValue)) throw new Error("Expected body.messageIds array.");
625
+ const messageIds = messageIdsValue.map((value) => {
626
+ if (typeof value !== "string" || value.length === 0) {
627
+ throw new Error("Expected body.messageIds to contain non-empty strings.");
628
+ }
629
+ return value;
630
+ });
631
+ await markTeamMessagesDelivered({
632
+ homeDir,
633
+ runId: expectBodyString(input, "runId"),
634
+ roleId: expectBodyString(input, "roleId"),
635
+ messageIds,
636
+ });
637
+ return { ok: true, delivered: messageIds };
638
+ }
639
+
580
640
  async function spawnDashboardTeamRole(
581
641
  homeDir: string,
582
642
  body: unknown,
@@ -752,17 +812,6 @@ function describeError(error: unknown): string {
752
812
  return error instanceof Error ? error.message : String(error);
753
813
  }
754
814
 
755
- async function collectNamedFiles(root: string, name: string): Promise<string[]> {
756
- const entries = await readdir(root, { withFileTypes: true });
757
- const files: string[] = [];
758
- for (const entry of entries) {
759
- const path = join(root, entry.name);
760
- if (entry.isDirectory()) files.push(...(await collectNamedFiles(path, name)));
761
- else if (entry.isFile() && entry.name === name) files.push(path);
762
- }
763
- return files;
764
- }
765
-
766
815
  function ok(data: unknown, warnings: string[]): { status: number; body: DaemonResponseBody } {
767
816
  return { status: 200, body: { ok: true, data: sanitizeMetadata(data), warnings } };
768
817
  }