@agimon-ai/po-team 0.0.0-alpha.2 → 0.0.0-alpha.21

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 (120) hide show
  1. package/LICENSE +166 -5
  2. package/README.md +11 -12
  3. package/dist/{agentDiscovery-Whwzc4dP.cjs → agentDiscovery-DTTlM_LT.cjs} +246 -350
  4. package/dist/agentDiscovery-DTTlM_LT.cjs.map +1 -0
  5. package/dist/{agentDiscovery-ZQlFkKvB.mjs → agentDiscovery-ZNG8iZXV.mjs} +210 -279
  6. package/dist/agentDiscovery-ZNG8iZXV.mjs.map +1 -0
  7. package/dist/apiContracts.cjs +10 -3
  8. package/dist/apiContracts.cjs.map +1 -1
  9. package/dist/apiContracts.d.mts.map +1 -1
  10. package/dist/apiContracts.mjs +10 -3
  11. package/dist/apiContracts.mjs.map +1 -1
  12. package/dist/{externalProcessIpc-ojE0pvw0.cjs → atomicJson-DRT03Ql5.cjs} +155 -21
  13. package/dist/atomicJson-DRT03Ql5.cjs.map +1 -0
  14. package/dist/{externalProcessIpc-C-15lJrB.mjs → atomicJson-VVM1iAPu.mjs} +140 -12
  15. package/dist/atomicJson-VVM1iAPu.mjs.map +1 -0
  16. package/dist/{capabilityCeiling-CF7kAS18.mjs → capabilityCeiling-BaHT9v9S.mjs} +3 -3
  17. package/dist/capabilityCeiling-BaHT9v9S.mjs.map +1 -0
  18. package/dist/{capabilityCeiling-C-Y7H8oC.cjs → capabilityCeiling-CO3ZjYmY.cjs} +3 -3
  19. package/dist/capabilityCeiling-CO3ZjYmY.cjs.map +1 -0
  20. package/dist/capabilityCeiling.cjs +2 -2
  21. package/dist/capabilityCeiling.d.mts +2 -2
  22. package/dist/capabilityCeiling.mjs +2 -2
  23. package/dist/delegation.cjs +1 -1
  24. package/dist/delegation.cjs.map +1 -1
  25. package/dist/delegation.d.mts +1 -1
  26. package/dist/delegation.mjs +1 -1
  27. package/dist/delegation.mjs.map +1 -1
  28. package/dist/delegationApi.cjs +30 -30
  29. package/dist/delegationApi.d.mts +2 -2
  30. package/dist/delegationApi.mjs +1 -2
  31. package/dist/env.cjs +1 -1
  32. package/dist/env.d.mts +1 -1
  33. package/dist/env.mjs +1 -2
  34. package/dist/{environment-DrqhrxG_.mjs → environment-BtvJI2S-.mjs} +3 -3
  35. package/dist/environment-BtvJI2S-.mjs.map +1 -0
  36. package/dist/{environment-ma5oQ7uM.d.mts → environment-Bz1OlWIT.d.mts} +2 -2
  37. package/dist/{environment-ma5oQ7uM.d.mts.map → environment-Bz1OlWIT.d.mts.map} +1 -1
  38. package/dist/{environment-CaCW3SgT.cjs → environment-Cqs7vHJL.cjs} +3 -3
  39. package/dist/environment-Cqs7vHJL.cjs.map +1 -0
  40. package/dist/extensions/native.mjs +34 -12
  41. package/dist/extensions/native.mjs.map +1 -1
  42. package/dist/extensions/server.cjs +990 -786
  43. package/dist/extensions/server.cjs.map +1 -1
  44. package/dist/extensions/server.d.mts +2 -2
  45. package/dist/extensions/server.d.mts.map +1 -1
  46. package/dist/extensions/server.mjs +975 -772
  47. package/dist/extensions/server.mjs.map +1 -1
  48. package/dist/extensions/web.mjs +994 -807
  49. package/dist/extensions/web.mjs.map +1 -1
  50. package/dist/fablePlan.cjs +3 -3
  51. package/dist/fablePlan.d.mts +2 -2
  52. package/dist/fablePlan.mjs +1 -2
  53. package/dist/{fablePlanApi-CZ3uUcMi.d.mts → fablePlanApi-BTNKjLni.d.mts} +6 -6
  54. package/dist/{fablePlanApi-CZ3uUcMi.d.mts.map → fablePlanApi-BTNKjLni.d.mts.map} +1 -1
  55. package/dist/{fablePlanApi-wBu9bRXU.cjs → fablePlanApi-BhI4jnDP.cjs} +12 -12
  56. package/dist/fablePlanApi-BhI4jnDP.cjs.map +1 -0
  57. package/dist/{fablePlanApi-BsHhgVFV.mjs → fablePlanApi-DuMg43Hu.mjs} +5 -5
  58. package/dist/fablePlanApi-DuMg43Hu.mjs.map +1 -0
  59. package/dist/fablePlanApi.cjs +3 -3
  60. package/dist/fablePlanApi.d.mts +2 -2
  61. package/dist/fablePlanApi.mjs +1 -2
  62. package/dist/index.cjs +17 -8
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.mts +5 -1
  65. package/dist/index.d.mts.map +1 -1
  66. package/dist/index.mjs +15 -5
  67. package/dist/index.mjs.map +1 -1
  68. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  69. package/dist/runs/background/cliRunnerEntry.cjs +26 -25
  70. package/dist/runs/background/cliRunnerEntry.cjs.map +1 -1
  71. package/dist/runs/background/cliRunnerEntry.mjs +14 -14
  72. package/dist/runs/background/cliRunnerEntry.mjs.map +1 -1
  73. package/dist/runtimeSubagentPolicy.cjs +3 -3
  74. package/dist/runtimeSubagentPolicy.d.mts +2 -2
  75. package/dist/runtimeSubagentPolicy.mjs +1 -2
  76. package/dist/runtimeSubagentTool.cjs +7 -2
  77. package/dist/runtimeSubagentTool.d.mts +1 -1
  78. package/dist/runtimeSubagentTool.mjs +1 -2
  79. package/dist/{subagentPolicy-ALJam4GN.d.mts → subagentPolicy-BE-OZoa-.d.mts} +6 -6
  80. package/dist/subagentPolicy-BE-OZoa-.d.mts.map +1 -0
  81. package/dist/{subagentPolicy-DQRyKDH-.cjs → subagentPolicy-DIpaKHO8.cjs} +8 -8
  82. package/dist/subagentPolicy-DIpaKHO8.cjs.map +1 -0
  83. package/dist/{subagentPolicy-DxjsKeyj.mjs → subagentPolicy-LfcXuqO5.mjs} +5 -5
  84. package/dist/subagentPolicy-LfcXuqO5.mjs.map +1 -0
  85. package/dist/subagentPolicy.cjs +3 -3
  86. package/dist/subagentPolicy.d.mts +2 -2
  87. package/dist/subagentPolicy.mjs +1 -2
  88. package/dist/{subagentTool-Ca2zQXKw.cjs → subagentTool-BueNPGHo.cjs} +5 -16
  89. package/dist/subagentTool-BueNPGHo.cjs.map +1 -0
  90. package/dist/{subagentTool-BTssGLw8.d.mts → subagentTool-UdB3s6Mn.d.mts} +2 -11
  91. package/dist/subagentTool-UdB3s6Mn.d.mts.map +1 -0
  92. package/dist/{subagentTool-WOYrjxgT.mjs → subagentTool-a2oh2v5B.mjs} +5 -16
  93. package/dist/subagentTool-a2oh2v5B.mjs.map +1 -0
  94. package/dist/subagentTool.cjs +7 -2
  95. package/dist/subagentTool.d.mts +1 -1
  96. package/dist/subagentTool.mjs +1 -2
  97. package/dist/teamSnapshot.d.mts +1 -0
  98. package/dist/teamSnapshot.d.mts.map +1 -1
  99. package/dist/teamSnapshot.mjs +1 -2
  100. package/package.json +25 -24
  101. package/dist/agentDiscovery-Whwzc4dP.cjs.map +0 -1
  102. package/dist/agentDiscovery-ZQlFkKvB.mjs.map +0 -1
  103. package/dist/atomicJson-D0346bt5.cjs +0 -178
  104. package/dist/atomicJson-D0346bt5.cjs.map +0 -1
  105. package/dist/atomicJson-D2t0CDh9.mjs +0 -137
  106. package/dist/atomicJson-D2t0CDh9.mjs.map +0 -1
  107. package/dist/capabilityCeiling-C-Y7H8oC.cjs.map +0 -1
  108. package/dist/capabilityCeiling-CF7kAS18.mjs.map +0 -1
  109. package/dist/environment-CaCW3SgT.cjs.map +0 -1
  110. package/dist/environment-DrqhrxG_.mjs.map +0 -1
  111. package/dist/externalProcessIpc-C-15lJrB.mjs.map +0 -1
  112. package/dist/externalProcessIpc-ojE0pvw0.cjs.map +0 -1
  113. package/dist/fablePlanApi-BsHhgVFV.mjs.map +0 -1
  114. package/dist/fablePlanApi-wBu9bRXU.cjs.map +0 -1
  115. package/dist/subagentPolicy-ALJam4GN.d.mts.map +0 -1
  116. package/dist/subagentPolicy-DQRyKDH-.cjs.map +0 -1
  117. package/dist/subagentPolicy-DxjsKeyj.mjs.map +0 -1
  118. package/dist/subagentTool-BTssGLw8.d.mts.map +0 -1
  119. package/dist/subagentTool-Ca2zQXKw.cjs.map +0 -1
  120. package/dist/subagentTool-WOYrjxgT.mjs.map +0 -1
@@ -1,88 +1,12 @@
1
- const require_atomicJson = require("./atomicJson-D0346bt5.cjs");
2
- let node_fs = require("node:fs");
3
- node_fs = require_atomicJson.__toESM(node_fs, 1);
1
+ const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
4
2
  let node_path = require("node:path");
5
- node_path = require_atomicJson.__toESM(node_path, 1);
6
- let _agimon_ai_po_config = require("@agimon-ai/po-config");
3
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
4
+ let _agimon_ai_po_core_platform = require("@agimon-ai/po-core/platform");
5
+ let node_fs = require("node:fs");
6
+ node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
7
7
  let node_os = require("node:os");
8
- node_os = require_atomicJson.__toESM(node_os, 1);
8
+ node_os = require_rolldown_runtime.__toESM(node_os, 1);
9
9
  let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
10
- //#region src/schemas/team/packageConfig.ts
11
- const PO_TEAM_PACKAGE = "@agimon-ai/po-team";
12
- const TEAM_CONFIG_KEYS = ["models", "excludeTools"];
13
- const TEAM_MODEL_KEYS = ["model", "thinking"];
14
- const THINKING_VALUES = /* @__PURE__ */ new Set([
15
- "off",
16
- "minimal",
17
- "low",
18
- "medium",
19
- "high",
20
- "xhigh",
21
- "max"
22
- ]);
23
- function isRecord(value) {
24
- return typeof value === "object" && value !== null && !Array.isArray(value);
25
- }
26
- function assertKnownKeys(value, keys, location) {
27
- const unsupported = Object.keys(value).filter((key) => !keys.includes(key));
28
- if (unsupported.length > 0) throw new Error(`${location} has unsupported field(s): ${unsupported.join(", ")}`);
29
- }
30
- /** Validate and normalize one Team-owned package configuration mapping. */
31
- function parseTeamPackageConfig(value, location) {
32
- if (!isRecord(value)) throw new Error(`${location} must be a mapping`);
33
- assertKnownKeys(value, TEAM_CONFIG_KEYS, location);
34
- const hasModels = Object.hasOwn(value, "models");
35
- const hasExcludeTools = Object.hasOwn(value, "excludeTools");
36
- if (!hasModels && !hasExcludeTools) throw new Error(`${location} must define models or excludeTools`);
37
- let models;
38
- if (hasModels) {
39
- if (!Array.isArray(value.models) || value.models.length === 0) throw new Error(`${location}.models must be a non-empty array`);
40
- models = value.models.map((entry, index) => {
41
- const entryLocation = `${location}.models[${index}]`;
42
- if (!isRecord(entry)) throw new Error(`${entryLocation} must be a mapping`);
43
- assertKnownKeys(entry, TEAM_MODEL_KEYS, entryLocation);
44
- if (typeof entry.model !== "string" || !entry.model.trim()) throw new Error(`${entryLocation}.model must be a non-empty string`);
45
- if (entry.thinking !== void 0 && (typeof entry.thinking !== "string" || !THINKING_VALUES.has(entry.thinking))) throw new Error(`${entryLocation}.thinking must be one of: ${[...THINKING_VALUES].join(", ")}`);
46
- return {
47
- model: entry.model.trim(),
48
- ...entry.thinking ? { thinking: entry.thinking } : {}
49
- };
50
- });
51
- }
52
- let excludeTools;
53
- if (hasExcludeTools) {
54
- if (!Array.isArray(value.excludeTools) || value.excludeTools.length === 0) throw new Error(`${location}.excludeTools must be a non-empty array`);
55
- excludeTools = [...new Set(value.excludeTools.map((entry, index) => {
56
- if (typeof entry !== "string" || !entry.trim()) throw new Error(`${location}.excludeTools[${index}] must be a non-empty string`);
57
- return entry.trim();
58
- }))];
59
- }
60
- return {
61
- ...models ? { models } : {},
62
- ...excludeTools ? { excludeTools } : {}
63
- };
64
- }
65
- /** Merge Team configurations contributed by selected layers in selection order. */
66
- function mergeTeamPackageConfigurations(entries) {
67
- let models;
68
- const excludeTools = /* @__PURE__ */ new Set();
69
- let hasExcludeTools = false;
70
- for (const entry of entries) {
71
- const location = `Package "${entry.specifier}" config in layer "${entry.layer}" of .po/modes.yaml`;
72
- const next = parseTeamPackageConfig(entry.config, location);
73
- if (next.models) models = next.models.map((model) => ({ ...model }));
74
- if (next.excludeTools) {
75
- hasExcludeTools = true;
76
- for (const tool of next.excludeTools) excludeTools.add(tool);
77
- }
78
- }
79
- if (!models && !hasExcludeTools) return void 0;
80
- return {
81
- ...models ? { models } : {},
82
- ...hasExcludeTools ? { excludeTools: [...excludeTools] } : {}
83
- };
84
- }
85
- //#endregion
86
10
  //#region src/services/agentFrontmatter/index.ts
87
11
  /**
88
12
  * Parsing the YAML frontmatter block at the head of an agent or chain file.
@@ -312,29 +236,133 @@ function getConfigDirName(environment = process.env) {
312
236
  function getProjectConfigDir(projectRoot, environment = process.env) {
313
237
  return node_path.join(projectRoot, getConfigDirName(environment));
314
238
  }
315
- /** Read a JSON settings file as an object, retaining strict path-specific errors. */
316
- function readSettingsFileStrict(filePath) {
317
- if (!node_fs.existsSync(filePath)) return {};
318
- let raw;
239
+ /** The user-level agent directory Po runs with, from Po's own resolver. */
240
+ function getAgentDir(environment = process.env) {
241
+ return (0, _agimon_ai_po_core_piSettings.piAgentDirectory)(environment, environment.HOME ?? node_os.homedir());
242
+ }
243
+ //#endregion
244
+ //#region src/services/agentProjectRoot/sync.ts
245
+ /** @deprecated Node-only shim. */
246
+ /**
247
+ * Locating the project root that agent configuration hangs off.
248
+ *
249
+ * A project root is any ancestor of the working directory that carries a config
250
+ * directory or a legacy `.agents` directory. There can be several: a package
251
+ * inside a monorepo and the monorepo itself both qualify. Which one wins is a
252
+ * setting, because both answers are legitimate. Teams that keep one shared set
253
+ * of agents at the repository root want `git-root`, teams that give each package
254
+ * its own want `nearest`, and neither can be inferred from the tree alone.
255
+ *
256
+ * DESIGN PATTERNS:
257
+ * - Candidates are collected once, nearest first, then a policy picks from them.
258
+ * Separating collection from policy is what lets `git-root` be answered
259
+ * without a second walk
260
+ * - Path derivation goes through `shared/configDir.ts`, so a rebranded build
261
+ * that renames its config directory needs no change here
262
+ *
263
+ * AVOID:
264
+ * - Hardcoding the config directory name; it is discovered, not assumed
265
+ * - Treating the nearest candidate as the project root without reading the
266
+ * setting first
267
+ */
268
+ /** Stable cache identity for equivalent absolute, relative, and symlinked cwd spellings. */
269
+ function canonicalizeDiscoveryCwd$1(cwd) {
270
+ const resolved = node_path.resolve(cwd);
319
271
  try {
320
- raw = node_fs.readFileSync(filePath, "utf-8");
321
- } catch (error) {
322
- const message = error instanceof Error ? error.message : String(error);
323
- throw new Error(`Failed to read settings file '${filePath}': ${message}`, { cause: error });
272
+ return node_fs.realpathSync.native(resolved);
273
+ } catch {
274
+ return resolved;
324
275
  }
325
- let parsed;
326
- try {
327
- parsed = JSON.parse(raw);
328
- } catch (error) {
329
- const message = error instanceof Error ? error.message : String(error);
330
- throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
276
+ }
277
+ //#endregion
278
+ //#region src/services/agentMemory/index.ts
279
+ /**
280
+ * Per-agent persistent memory, injected into a child system prompt.
281
+ *
282
+ * An agent may opt into a durable, role-specific memory scope via its `memory`
283
+ * frontmatter. The opening lines of a `MEMORY.md` in the resolved directory are
284
+ * injected so a recurring custom agent can recall accumulated role notes:
285
+ * threat models, gotchas, verified commands, decisions. An agent with no write
286
+ * tools gets a read-only variant of the block instead.
287
+ *
288
+ * DESIGN PATTERNS:
289
+ * - The directory is derived from the agent's name, never from a user-supplied
290
+ * path, so no agent can name its way into another agent's notes
291
+ * - The file is opened once with O_NOFOLLOW and every subsequent check runs on
292
+ * that descriptor, so a symlink swapped in after the check cannot be followed
293
+ * - Reads are capped in both lines and bytes, and stop early. A memory file that
294
+ * grew unbounded must not be able to consume the child's whole context window
295
+ * - Every containment failure degrades to no injection. A prompt with no memory
296
+ * is correct; a prompt pointed at an unverified path is not
297
+ *
298
+ * WHY THE DIRECTORY IS NAMESPACED:
299
+ * Memory lives under a dedicated `agent-memory/` subdirectory so it can never
300
+ * collide with the coding agent's own `memory/{project}/` tree.
301
+ *
302
+ * AVOID:
303
+ * - Injecting memory contents without the boundary instruction; the file is data
304
+ * a previous run wrote, and a prompt-injection payload could have landed in it
305
+ * - Re-stat'ing by path between the open and the read
306
+ */
307
+ /** Tools that let an agent append to its own memory file. */
308
+ const WRITE_TOOLS = /* @__PURE__ */ new Set([
309
+ "edit",
310
+ "write",
311
+ "bash"
312
+ ]);
313
+ function unquoteFrontmatterValue(value) {
314
+ const trimmed = value.trim();
315
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
316
+ return trimmed;
317
+ }
318
+ function parseMemoryScope(value) {
319
+ return value === "project" || value === "user" ? value : void 0;
320
+ }
321
+ function parseMemoryBoolean(value) {
322
+ if (value === "true") return true;
323
+ if (value === "false") return false;
324
+ }
325
+ function parsePositiveInteger(value) {
326
+ if (value === void 0) return void 0;
327
+ const parsed = Number(value);
328
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
329
+ }
330
+ /** Split a `memory` frontmatter block into key/value pairs, either spelling. */
331
+ function readMemoryEntries(raw) {
332
+ const entries = /* @__PURE__ */ new Map();
333
+ const inlineObject = raw.trim().match(/^\{(.*)\}$/s);
334
+ const parts = inlineObject?.[1] !== void 0 ? inlineObject[1].split(",") : raw.split("\n");
335
+ for (const part of parts) {
336
+ const match = part.trim().match(/^([\w-]+)\s*:\s*(.*)$/);
337
+ if (!match?.[1] || match[2] === void 0) continue;
338
+ entries.set(match[1], unquoteFrontmatterValue(match[2]));
331
339
  }
332
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
333
- return parsed;
340
+ return entries;
334
341
  }
335
- /** The user-level agent directory Po runs with, from Po's own resolver. */
336
- function getAgentDir(environment = process.env) {
337
- return (0, _agimon_ai_po_core_piSettings.piAgentDirectory)(environment, environment.HOME ?? node_os.homedir());
342
+ /**
343
+ * Parse a `memory` frontmatter block, or undefined when it declares nothing.
344
+ *
345
+ * Accepts both the inline `{ scope: project }` form and the indented block form,
346
+ * because agent files in the wild use each.
347
+ */
348
+ function parseMemoryFrontmatter(raw) {
349
+ if (!raw) return void 0;
350
+ const entries = readMemoryEntries(raw);
351
+ const enabled = parseMemoryBoolean(entries.get("enabled"));
352
+ const scope = parseMemoryScope(entries.get("scope"));
353
+ const maxEntries = parsePositiveInteger(entries.get("maxEntries"));
354
+ if (enabled === void 0 && scope === void 0 && maxEntries === void 0) return void 0;
355
+ const config = {};
356
+ if (enabled !== void 0) config.enabled = enabled;
357
+ if (scope !== void 0) config.scope = scope;
358
+ if (maxEntries !== void 0) config.maxEntries = maxEntries;
359
+ return config;
360
+ }
361
+ /** Whether an agent can write files this run. An unset `tools` inherits the builtins. */
362
+ function agentHasWriteTools(agent) {
363
+ const tools = agent.tools;
364
+ if (!tools) return true;
365
+ return tools.some((tool) => WRITE_TOOLS.has(tool));
338
366
  }
339
367
  //#endregion
340
368
  //#region src/services/agentProjectRoot/index.ts
@@ -352,7 +380,7 @@ function getAgentDir(environment = process.env) {
352
380
  * - Candidates are collected once, nearest first, then a policy picks from them.
353
381
  * Separating collection from policy is what lets `git-root` be answered
354
382
  * without a second walk
355
- * - Path derivation goes through `shared/configDir.ts`, so a rebranded build
383
+ * - Path derivation uses injected directory settings, so a rebranded build
356
384
  * that renames its config directory needs no change here
357
385
  *
358
386
  * AVOID:
@@ -360,6 +388,25 @@ function getAgentDir(environment = process.env) {
360
388
  * - Treating the nearest candidate as the project root without reading the
361
389
  * setting first
362
390
  */
391
+ async function readSettingsFile(io, filePath) {
392
+ let raw;
393
+ try {
394
+ raw = await (0, _agimon_ai_po_core_platform.readTextIfExists)(io.fs, filePath, io.context);
395
+ } catch (error) {
396
+ const message = error instanceof Error ? error.message : String(error);
397
+ throw new Error(`Failed to read settings file '${filePath}': ${message}`, { cause: error });
398
+ }
399
+ if (raw === void 0) return {};
400
+ let parsed;
401
+ try {
402
+ parsed = JSON.parse(raw);
403
+ } catch (error) {
404
+ const message = error instanceof Error ? error.message : String(error);
405
+ throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
406
+ }
407
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
408
+ return parsed;
409
+ }
363
410
  const SETTINGS_FILE_NAME = "settings.json";
364
411
  const GIT_DIR_NAME$1 = ".git";
365
412
  /**
@@ -373,32 +420,33 @@ const AGENTS_DIR_NAME = "agents";
373
420
  const SUBAGENTS_SETTINGS_KEY$1 = "subagents";
374
421
  const PROJECT_ROOT_RESOLUTION_KEY = "projectRootResolution";
375
422
  /** Stable cache identity for equivalent absolute, relative, and symlinked cwd spellings. */
376
- function canonicalizeDiscoveryCwd(cwd) {
423
+ async function canonicalizeDiscoveryCwd(io, cwd) {
377
424
  const resolved = node_path.resolve(cwd);
378
425
  try {
379
- return node_fs.realpathSync.native(resolved);
426
+ return (0, _agimon_ai_po_core_platform.getOrThrow)(await io.fs.canonicalPath(resolved, io.context));
380
427
  } catch {
381
428
  return resolved;
382
429
  }
383
430
  }
384
431
  /** Whether a path exists and is a directory. Any failure reads as "no". */
385
- function isDirectory$1(dirPath) {
432
+ async function isDirectory(io, dirPath) {
386
433
  try {
387
- return node_fs.statSync(dirPath).isDirectory();
434
+ const canonical = (0, _agimon_ai_po_core_platform.getOrThrow)(await io.fs.canonicalPath(dirPath, io.context));
435
+ return (0, _agimon_ai_po_core_platform.getOrThrow)(await io.fs.fileInfo(canonical, io.context)).kind === "directory";
388
436
  } catch {
389
437
  return false;
390
438
  }
391
439
  }
392
440
  /** True when `dir` carries either marker that makes it a project root. */
393
- function isProjectRootCandidate(dir, environment = process.env) {
394
- return isDirectory$1(getProjectConfigDir(dir, environment)) || isDirectory$1(node_path.join(dir, DOT_AGENTS_DIR_NAME));
441
+ async function isProjectRootCandidate(io, dir) {
442
+ return await isDirectory(io, node_path.join(dir, io.configDirName)) || await isDirectory(io, node_path.join(dir, DOT_AGENTS_DIR_NAME));
395
443
  }
396
444
  /** Every project root above `cwd`, nearest first. */
397
- function findProjectRootCandidates(cwd, environment = process.env) {
445
+ async function findProjectRootCandidates(io, cwd) {
398
446
  const roots = [];
399
447
  let currentDir = cwd;
400
448
  while (true) {
401
- if (isProjectRootCandidate(currentDir, environment)) roots.push(currentDir);
449
+ if (await isProjectRootCandidate(io, currentDir)) roots.push(currentDir);
402
450
  const parentDir = node_path.dirname(currentDir);
403
451
  if (parentDir === currentDir) return roots;
404
452
  currentDir = parentDir;
@@ -410,10 +458,11 @@ function findProjectRootCandidates(cwd, environment = process.env) {
410
458
  * Existence is enough: a worktree or submodule has a `.git` file rather than a
411
459
  * directory, and both are real repository roots.
412
460
  */
413
- function findNearestGitRoot(cwd) {
461
+ async function findNearestGitRoot(io, cwd) {
414
462
  let currentDir = cwd;
415
463
  while (true) {
416
- if (node_fs.existsSync(node_path.join(currentDir, GIT_DIR_NAME$1))) return currentDir;
464
+ const gitMarker = await io.fs.exists(node_path.join(currentDir, GIT_DIR_NAME$1), io.context);
465
+ if (gitMarker.ok && gitMarker.value) return currentDir;
417
466
  const parentDir = node_path.dirname(currentDir);
418
467
  if (parentDir === currentDir) return null;
419
468
  currentDir = parentDir;
@@ -425,10 +474,9 @@ function findNearestGitRoot(cwd) {
425
474
  * Deliberately throws on an unrecognised value rather than falling back: a typo
426
475
  * here silently changes which agents the whole session sees.
427
476
  */
428
- function readProjectRootResolution(projectRoot, environment = process.env) {
429
- const settingsPath = node_path.join(getProjectConfigDir(projectRoot, environment), SETTINGS_FILE_NAME);
430
- if (!node_fs.existsSync(settingsPath)) return void 0;
431
- const subagents = readSettingsFileStrict(settingsPath)[SUBAGENTS_SETTINGS_KEY$1];
477
+ async function readProjectRootResolution(io, projectRoot) {
478
+ const settingsPath = node_path.join(projectRoot, io.configDirName, SETTINGS_FILE_NAME);
479
+ const subagents = (await readSettingsFile(io, settingsPath))[SUBAGENTS_SETTINGS_KEY$1];
432
480
  if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return void 0;
433
481
  const value = subagents[PROJECT_ROOT_RESOLUTION_KEY];
434
482
  if (value === void 0) return void 0;
@@ -442,15 +490,15 @@ function readProjectRootResolution(projectRoot, environment = process.env) {
442
490
  * of its monorepo's shared agents, and from the git root second so a repository
443
491
  * can claim its packages without editing each one.
444
492
  */
445
- function findConfiguredProjectRoot(cwd, environment = process.env) {
446
- const candidates = findProjectRootCandidates(cwd, environment);
493
+ async function findConfiguredProjectRoot(io, cwd) {
494
+ const candidates = await findProjectRootCandidates(io, cwd);
447
495
  const nearestRoot = candidates[0];
448
496
  if (!nearestRoot) return null;
449
- const nearestMode = readProjectRootResolution(nearestRoot, environment);
497
+ const nearestMode = await readProjectRootResolution(io, nearestRoot);
450
498
  if (nearestMode === "nearest") return nearestRoot;
451
- const gitRoot = findNearestGitRoot(cwd);
499
+ const gitRoot = await findNearestGitRoot(io, cwd);
452
500
  const gitProjectRoot = gitRoot ? candidates.find((candidate) => node_path.resolve(candidate) === node_path.resolve(gitRoot)) : void 0;
453
- if (gitProjectRoot && (nearestMode === "git-root" || readProjectRootResolution(gitProjectRoot, environment) === "git-root")) return gitProjectRoot;
501
+ if (gitProjectRoot && (nearestMode === "git-root" || await readProjectRootResolution(io, gitProjectRoot) === "git-root")) return gitProjectRoot;
454
502
  return nearestRoot;
455
503
  }
456
504
  /**
@@ -461,17 +509,17 @@ function findConfiguredProjectRoot(cwd, environment = process.env) {
461
509
  * working after the move. Returned unfiltered because the caller reads each
462
510
  * directory anyway and a missing one costs nothing.
463
511
  */
464
- function userAgentDirs(environment = process.env) {
465
- return [node_path.join(getAgentDir(environment), AGENTS_DIR_NAME), node_path.join(environment.HOME ?? node_os.homedir(), DOT_AGENTS_DIR_NAME)];
512
+ function userAgentDirs(io) {
513
+ return [node_path.join((0, _agimon_ai_po_core_piSettings.piAgentDirectory)(io.environment, io.homeDirectory), AGENTS_DIR_NAME), node_path.join(io.homeDirectory, DOT_AGENTS_DIR_NAME)];
466
514
  }
467
515
  /** The user-level settings file. Always resolvable, unlike the project one. */
468
- function getUserAgentSettingsPath(environment = process.env) {
469
- return node_path.join(getAgentDir(environment), SETTINGS_FILE_NAME);
516
+ function getUserAgentSettingsPath(io) {
517
+ return node_path.join((0, _agimon_ai_po_core_piSettings.piAgentDirectory)(io.environment, io.homeDirectory), SETTINGS_FILE_NAME);
470
518
  }
471
519
  /** The project settings file, or null when `cwd` sits under no project root. */
472
- function getProjectAgentSettingsPath(cwd, environment = process.env) {
473
- const projectRoot = findConfiguredProjectRoot(cwd, environment);
474
- return projectRoot ? node_path.join(getProjectConfigDir(projectRoot, environment), SETTINGS_FILE_NAME) : null;
520
+ async function getProjectAgentSettingsPath(io, cwd) {
521
+ const projectRoot = await findConfiguredProjectRoot(io, cwd);
522
+ return projectRoot ? node_path.join(projectRoot, io.configDirName, SETTINGS_FILE_NAME) : null;
475
523
  }
476
524
  /**
477
525
  * Project agent directories for `cwd`.
@@ -481,144 +529,24 @@ function getProjectAgentSettingsPath(cwd, environment = process.env) {
481
529
  * `preferredDir` is where a newly created agent should be written, and is
482
530
  * returned whether or not it exists yet.
483
531
  */
484
- function resolveNearestProjectAgentDirs(cwd, environment = process.env) {
485
- const projectRoot = findConfiguredProjectRoot(cwd, environment);
532
+ async function resolveNearestProjectAgentDirs(io, cwd) {
533
+ const projectRoot = await findConfiguredProjectRoot(io, cwd);
486
534
  if (!projectRoot) return {
487
535
  readDirs: [],
488
536
  preferredDir: null
489
537
  };
490
538
  const legacyDir = node_path.join(projectRoot, DOT_AGENTS_DIR_NAME);
491
- const preferredDir = node_path.join(getProjectConfigDir(projectRoot, environment), AGENTS_DIR_NAME);
539
+ const preferredDir = node_path.join(projectRoot, io.configDirName, AGENTS_DIR_NAME);
492
540
  const readDirs = [];
493
- if (isDirectory$1(legacyDir)) readDirs.push(legacyDir);
494
- if (isDirectory$1(preferredDir)) readDirs.push(preferredDir);
541
+ if (await isDirectory(io, legacyDir)) readDirs.push(legacyDir);
542
+ if (await isDirectory(io, preferredDir)) readDirs.push(preferredDir);
495
543
  return {
496
544
  readDirs,
497
545
  preferredDir
498
546
  };
499
547
  }
500
548
  //#endregion
501
- //#region src/services/agentMemory/index.ts
502
- /**
503
- * Per-agent persistent memory, injected into a child system prompt.
504
- *
505
- * An agent may opt into a durable, role-specific memory scope via its `memory`
506
- * frontmatter. The opening lines of a `MEMORY.md` in the resolved directory are
507
- * injected so a recurring custom agent can recall accumulated role notes:
508
- * threat models, gotchas, verified commands, decisions. An agent with no write
509
- * tools gets a read-only variant of the block instead.
510
- *
511
- * DESIGN PATTERNS:
512
- * - The directory is derived from the agent's name, never from a user-supplied
513
- * path, so no agent can name its way into another agent's notes
514
- * - The file is opened once with O_NOFOLLOW and every subsequent check runs on
515
- * that descriptor, so a symlink swapped in after the check cannot be followed
516
- * - Reads are capped in both lines and bytes, and stop early. A memory file that
517
- * grew unbounded must not be able to consume the child's whole context window
518
- * - Every containment failure degrades to no injection. A prompt with no memory
519
- * is correct; a prompt pointed at an unverified path is not
520
- *
521
- * WHY THE DIRECTORY IS NAMESPACED:
522
- * Memory lives under a dedicated `agent-memory/` subdirectory so it can never
523
- * collide with the coding agent's own `memory/{project}/` tree.
524
- *
525
- * AVOID:
526
- * - Injecting memory contents without the boundary instruction; the file is data
527
- * a previous run wrote, and a prompt-injection payload could have landed in it
528
- * - Re-stat'ing by path between the open and the read
529
- */
530
- /** Tools that let an agent append to its own memory file. */
531
- const WRITE_TOOLS = /* @__PURE__ */ new Set([
532
- "edit",
533
- "write",
534
- "bash"
535
- ]);
536
- function unquoteFrontmatterValue(value) {
537
- const trimmed = value.trim();
538
- if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
539
- return trimmed;
540
- }
541
- function parseMemoryScope(value) {
542
- return value === "project" || value === "user" ? value : void 0;
543
- }
544
- function parseMemoryBoolean(value) {
545
- if (value === "true") return true;
546
- if (value === "false") return false;
547
- }
548
- function parsePositiveInteger(value) {
549
- if (value === void 0) return void 0;
550
- const parsed = Number(value);
551
- return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
552
- }
553
- /** Split a `memory` frontmatter block into key/value pairs, either spelling. */
554
- function readMemoryEntries(raw) {
555
- const entries = /* @__PURE__ */ new Map();
556
- const inlineObject = raw.trim().match(/^\{(.*)\}$/s);
557
- const parts = inlineObject?.[1] !== void 0 ? inlineObject[1].split(",") : raw.split("\n");
558
- for (const part of parts) {
559
- const match = part.trim().match(/^([\w-]+)\s*:\s*(.*)$/);
560
- if (!match?.[1] || match[2] === void 0) continue;
561
- entries.set(match[1], unquoteFrontmatterValue(match[2]));
562
- }
563
- return entries;
564
- }
565
- /**
566
- * Parse a `memory` frontmatter block, or undefined when it declares nothing.
567
- *
568
- * Accepts both the inline `{ scope: project }` form and the indented block form,
569
- * because agent files in the wild use each.
570
- */
571
- function parseMemoryFrontmatter(raw) {
572
- if (!raw) return void 0;
573
- const entries = readMemoryEntries(raw);
574
- const enabled = parseMemoryBoolean(entries.get("enabled"));
575
- const scope = parseMemoryScope(entries.get("scope"));
576
- const maxEntries = parsePositiveInteger(entries.get("maxEntries"));
577
- if (enabled === void 0 && scope === void 0 && maxEntries === void 0) return void 0;
578
- const config = {};
579
- if (enabled !== void 0) config.enabled = enabled;
580
- if (scope !== void 0) config.scope = scope;
581
- if (maxEntries !== void 0) config.maxEntries = maxEntries;
582
- return config;
583
- }
584
- /** Whether an agent can write files this run. An unset `tools` inherits the builtins. */
585
- function agentHasWriteTools(agent) {
586
- const tools = agent.tools;
587
- if (!tools) return true;
588
- return tools.some((tool) => WRITE_TOOLS.has(tool));
589
- }
590
- //#endregion
591
549
  //#region src/services/agentSettings/index.ts
592
- /**
593
- * Agent settings: reading them, and applying what they say to discovered agents.
594
- *
595
- * Two things live here because they are the same contract seen from both ends.
596
- * Reading turns untyped JSON on disk into settings; applying folds those
597
- * settings over the agents discovery found. The precedence is fixed: project
598
- * settings beat user settings, an explicit per-agent override beats a bulk
599
- * switch, and for a custom agent the agent's own frontmatter beats both.
600
- *
601
- * That last rule is why builtins and custom agents take different paths.
602
- * Overriding a builtin replaces values outright, because a builtin has no file
603
- * the user could have edited instead. Overriding a custom agent only fills
604
- * fields its frontmatter left unset, so a settings default never silently
605
- * contradicts what the author wrote in the file.
606
- *
607
- * DESIGN PATTERNS:
608
- * - Reading is strict and throws with the offending path and field. A settings
609
- * file that is quietly half-ignored is worse than one that refuses to load,
610
- * because the user sees agents behaving in ways their config does not explain
611
- * - Applying is pure and copy-on-write. Every `apply*` returns new configs and
612
- * never mutates its input, so a cached discovery result cannot be corrupted
613
- * by a later pass over it
614
- * - Overriding a builtin snapshots the values it replaced into `override.base`,
615
- * which is what makes removing the override restore the builtin exactly
616
- *
617
- * AVOID:
618
- * - Trusting any shape read off disk without a real guard
619
- * - Mutating an `AgentConfig` in place
620
- * - Writing a settings file non-atomically; it holds the user's overrides
621
- */
622
550
  const SUBAGENTS_SETTINGS_KEY = "subagents";
623
551
  const AGENT_OVERRIDES_KEY = "agentOverrides";
624
552
  /** Prefix marking a tool the child calls on an MCP server directly. */
@@ -809,9 +737,9 @@ function parseToolBudgetOverride(value, name, filePath) {
809
737
  return budget;
810
738
  }
811
739
  /** Read and validate one settings file's `subagents` block. */
812
- function readSubagentSettings(filePath) {
740
+ async function readSubagentSettings(io, filePath) {
813
741
  if (!filePath) return EMPTY_SUBAGENT_SETTINGS;
814
- const subagentsObject = readObjectField(readSettingsFileStrict(filePath), SUBAGENTS_SETTINGS_KEY);
742
+ const subagentsObject = readObjectField(await readSettingsFile(io, filePath), SUBAGENTS_SETTINGS_KEY);
815
743
  if (!subagentsObject) return EMPTY_SUBAGENT_SETTINGS;
816
744
  let disableBuiltins;
817
745
  if ("disableBuiltins" in subagentsObject) {
@@ -1177,16 +1105,9 @@ const LEGACY_AGENTS_DIR_NAME = ".agents";
1177
1105
  const LEGACY_SKILLS_DIR_NAME = "skills";
1178
1106
  const AGENT_FILE_EXTENSION = ".md";
1179
1107
  const CHAIN_FILE_EXTENSION = ".chain.md";
1180
- function isDirectory(target) {
1181
- try {
1182
- return node_fs.statSync(target).isDirectory();
1183
- } catch {
1184
- return false;
1185
- }
1186
- }
1187
1108
  /** A directory that owns its own agent configuration is a separate project. */
1188
- function isDiscoveryNestedProjectRoot(dir, environment) {
1189
- return isDirectory(getProjectConfigDir(dir, environment)) || isDirectory(node_path.join(dir, LEGACY_AGENTS_DIR_NAME));
1109
+ async function isDiscoveryNestedProjectRoot(io, dir) {
1110
+ return await isDirectory(io, node_path.join(dir, io.configDirName)) || await isDirectory(io, node_path.join(dir, LEGACY_AGENTS_DIR_NAME));
1190
1111
  }
1191
1112
  /**
1192
1113
  * Decide whether the walk should skip a subdirectory.
@@ -1195,10 +1116,11 @@ function isDiscoveryNestedProjectRoot(dir, environment) {
1195
1116
  * belong to that project, not to the one being scanned. The root itself is
1196
1117
  * exempt: scanning a project's own agent directory must not prune it.
1197
1118
  */
1198
- function shouldPruneDiscoveryDir(rootDir, dir, dirName, environment) {
1119
+ async function shouldPruneDiscoveryDir(io, rootDir, dir, dirName) {
1199
1120
  if (DISCOVERY_PRUNED_DIR_NAMES.has(dirName)) return true;
1200
- if (node_fs.existsSync(node_path.join(dir, GIT_DIR_NAME))) return true;
1201
- return node_path.resolve(dir) !== node_path.resolve(rootDir) && isDiscoveryNestedProjectRoot(dir, environment);
1121
+ const gitMarker = await io.fs.exists(node_path.join(dir, GIT_DIR_NAME), io.context);
1122
+ if (gitMarker.ok && gitMarker.value) return true;
1123
+ return node_path.resolve(dir) !== node_path.resolve(rootDir) && await isDiscoveryNestedProjectRoot(io, dir);
1202
1124
  }
1203
1125
  /**
1204
1126
  * Collect matching files under `dir`, sorted so discovery order is stable.
@@ -1206,24 +1128,19 @@ function shouldPruneDiscoveryDir(rootDir, dir, dirName, environment) {
1206
1128
  * Symlinked entries are followed as files, because staging an agent by symlink
1207
1129
  * is a supported way to share one definition across projects.
1208
1130
  */
1209
- function listFilesRecursive(dir, predicate, rootDir = dir, environment = process.env) {
1131
+ async function listFilesRecursive(io, dir, predicate, rootDir = dir) {
1210
1132
  const files = [];
1211
- if (!node_fs.existsSync(dir)) return files;
1212
- let entries;
1213
- try {
1214
- entries = node_fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1215
- } catch {
1216
- return files;
1217
- }
1133
+ const result = await io.fs.listDir(dir, io.context);
1134
+ if (!result.ok) return files;
1135
+ const entries = result.value.sort((a, b) => a.name.localeCompare(b.name));
1218
1136
  for (const entry of entries) {
1219
1137
  const filePath = node_path.join(dir, entry.name);
1220
- if (entry.isDirectory()) {
1221
- if (!shouldPruneDiscoveryDir(rootDir, filePath, entry.name, environment)) files.push(...listFilesRecursive(filePath, predicate, rootDir, environment));
1138
+ if (entry.kind === "directory") {
1139
+ if (!await shouldPruneDiscoveryDir(io, rootDir, filePath, entry.name)) files.push(...await listFilesRecursive(io, filePath, predicate, rootDir));
1222
1140
  continue;
1223
1141
  }
1224
- if (!entry.isFile() && !entry.isSymbolicLink()) continue;
1225
- if (!predicate(entry.name)) continue;
1226
- files.push(filePath);
1142
+ if (entry.kind !== "file" && entry.kind !== "symlink") continue;
1143
+ if (predicate(entry.name)) files.push(filePath);
1227
1144
  }
1228
1145
  return files;
1229
1146
  }
@@ -1267,13 +1184,15 @@ function isAgentFileName(fileName) {
1267
1184
  * The returned configs carry only what the file itself declared. Settings
1268
1185
  * defaults, builtin overrides, and cross-source precedence are the caller's job.
1269
1186
  */
1270
- function loadAgentsFromDir(dir, source, environment = process.env) {
1187
+ async function loadAgentsFromDir(io, dir, source) {
1271
1188
  const agents = [];
1272
- for (const filePath of listFilesRecursive(dir, isAgentFileName, dir, environment)) {
1189
+ for (const filePath of await listFilesRecursive(io, dir, isAgentFileName, dir)) {
1273
1190
  if (isLegacyAgentSkillPath(dir, filePath)) continue;
1274
1191
  let content;
1275
1192
  try {
1276
- content = node_fs.readFileSync(filePath, "utf-8");
1193
+ const text = await (0, _agimon_ai_po_core_platform.readTextIfExists)(io.fs, filePath, io.context);
1194
+ if (text === void 0) continue;
1195
+ content = text;
1277
1196
  } catch {
1278
1197
  continue;
1279
1198
  }
@@ -1334,7 +1253,7 @@ function loadAgentsFromDir(dir, source, environment = process.env) {
1334
1253
  * Read from the environment on every call rather than captured once, because a
1335
1254
  * host can stage a plugin directory after this module is first imported.
1336
1255
  */
1337
- function pluginAgentDirs(environment = process.env) {
1256
+ function pluginAgentDirs(environment) {
1338
1257
  return (environment["PI_SUBAGENT_EXTRA_AGENT_DIRS"] ?? "").split(node_path.delimiter).map((directory) => directory.trim()).filter((directory) => directory.length > 0);
1339
1258
  }
1340
1259
  //#endregion
@@ -1442,8 +1361,6 @@ const MAX_CACHED_DISCOVERIES = 64;
1442
1361
  const FINGERPRINT_FIELD_SEPARATOR = ":";
1443
1362
  const FINGERPRINT_ENTRY_SEPARATOR = "|";
1444
1363
  const MISSING_SOURCE_MARKER = "-";
1445
- const MAJOR_MODES_CONFIG_RELATIVE_PATH = node_path.join(".po", "modes.yaml");
1446
- const THINKING_SUFFIX = /:(?:off|minimal|low|medium|high|xhigh|max)$/;
1447
1364
  /**
1448
1365
  * Merge the sources into one list, later sources shadowing earlier ones.
1449
1366
  *
@@ -1459,37 +1376,21 @@ function mergeAgentsForScope(scope, userAgents, projectAgents, pluginAgents = []
1459
1376
  return [...byName.values()];
1460
1377
  }
1461
1378
  /** Size and mtime of one path, or a marker when it does not exist. */
1462
- function stampOf(target) {
1379
+ async function stampOf(io, target) {
1463
1380
  try {
1464
- const stat = node_fs.statSync(target);
1381
+ const canonical = (0, _agimon_ai_po_core_platform.getOrThrow)(await io.fs.canonicalPath(target, io.context));
1382
+ const stat = (0, _agimon_ai_po_core_platform.getOrThrow)(await io.fs.fileInfo(canonical, io.context));
1465
1383
  return `${target}${FINGERPRINT_FIELD_SEPARATOR}${stat.mtimeMs}${FINGERPRINT_FIELD_SEPARATOR}${stat.size}`;
1466
1384
  } catch {
1467
1385
  return `${target}${FINGERPRINT_FIELD_SEPARATOR}${MISSING_SOURCE_MARKER}`;
1468
1386
  }
1469
1387
  }
1470
- /** Resolve Team-owned package configuration from the current parent harness state. */
1471
- function resolveActiveTeamPackageConfig(environment = process.env) {
1472
- const harness = (0, _agimon_ai_po_config.loadHarnessState)(environment).state;
1473
- if (!harness.root) return void 0;
1474
- const layers = (0, _agimon_ai_po_config.loadMajorModesConfig)(harness.root, environment.HOME);
1475
- const config = mergeTeamPackageConfigurations((0, _agimon_ai_po_config.resolvePackageConfigurations)(layers, harness.layers, PO_TEAM_PACKAGE));
1476
- return config ? {
1477
- config,
1478
- path: node_path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH)
1479
- } : void 0;
1480
- }
1481
- function teamModelSpecs(config) {
1482
- if (!config?.models) return void 0;
1483
- return config.models.map(({ model, thinking }) => thinking ? `${model.replace(THINKING_SUFFIX, "")}:${thinking}` : model);
1484
- }
1485
- /** Resolve Team's ordered package model policy without merging it into an agent definition. */
1486
- function resolveActiveTeamModelSpecs(environment = process.env) {
1487
- return teamModelSpecs(resolveActiveTeamPackageConfig(environment)?.config);
1488
- }
1489
1388
  var AgentDiscoveryService = class {
1490
- environment;
1491
- constructor(environment = process.env) {
1492
- this.environment = environment;
1389
+ io;
1390
+ agentConfig;
1391
+ constructor(io, agentConfig) {
1392
+ this.io = io;
1393
+ this.agentConfig = agentConfig;
1493
1394
  }
1494
1395
  cache = new LruCache(MAX_CACHED_DISCOVERIES);
1495
1396
  /**
@@ -1502,23 +1403,25 @@ var AgentDiscoveryService = class {
1502
1403
  now() {
1503
1404
  return Date.now();
1504
1405
  }
1505
- discover(cwd, scope) {
1406
+ async discover(cwd, scope) {
1506
1407
  const resolvedCwd = node_path.resolve(cwd);
1507
- const canonicalCwd = canonicalizeDiscoveryCwd(resolvedCwd);
1408
+ const canonicalCwd = await canonicalizeDiscoveryCwd(this.io, resolvedCwd);
1508
1409
  const key = `${scope}${FINGERPRINT_ENTRY_SEPARATOR}${canonicalCwd}`;
1509
1410
  const cached = this.cache.get(key);
1510
1411
  const timestamp = this.now();
1511
- if (cached && cached.expiresAt > timestamp && cached.fingerprint === this.fingerprint(canonicalCwd)) return cloneResult(cached.result);
1512
- const result = this.load(resolvedCwd, scope);
1412
+ const team = await this.agentConfig?.currentOrUndefined();
1413
+ if (cached && cached.team === team && cached.expiresAt > timestamp && cached.fingerprint === await this.fingerprint(canonicalCwd)) return cloneResult(cached.result);
1414
+ const result = await this.load(resolvedCwd, scope, team);
1513
1415
  this.cache.set(key, {
1514
1416
  result,
1515
1417
  expiresAt: timestamp + this.ttlMs,
1516
- fingerprint: this.fingerprint(canonicalCwd)
1418
+ fingerprint: await this.fingerprint(canonicalCwd),
1419
+ team
1517
1420
  });
1518
1421
  return cloneResult(result);
1519
1422
  }
1520
- find(cwd, scope, name) {
1521
- return this.discover(cwd, scope).agents.find((agent) => agent.name === name);
1423
+ async find(cwd, scope, name) {
1424
+ return (await this.discover(cwd, scope)).agents.find((agent) => agent.name === name);
1522
1425
  }
1523
1426
  invalidate() {
1524
1427
  this.cache.clear();
@@ -1530,43 +1433,42 @@ var AgentDiscoveryService = class {
1530
1433
  * from the same directory read overlapping sources, and computing one stamp
1531
1434
  * keeps the stat count fixed regardless of how many scopes are queried.
1532
1435
  */
1533
- fingerprint(cwd) {
1534
- const harness = (0, _agimon_ai_po_config.loadHarnessState)(this.environment).state;
1436
+ async fingerprint(cwd) {
1535
1437
  const sources = [
1536
- getUserAgentSettingsPath(this.environment),
1537
- getProjectAgentSettingsPath(cwd, this.environment) ?? MISSING_SOURCE_MARKER,
1538
- ...userAgentDirs(this.environment),
1539
- ...resolveNearestProjectAgentDirs(cwd, this.environment).readDirs,
1540
- ...pluginAgentDirs(this.environment)
1438
+ getUserAgentSettingsPath(this.io),
1439
+ await getProjectAgentSettingsPath(this.io, cwd) ?? MISSING_SOURCE_MARKER,
1440
+ ...userAgentDirs(this.io),
1441
+ ...(await resolveNearestProjectAgentDirs(this.io, cwd)).readDirs,
1442
+ ...pluginAgentDirs(this.io.environment)
1541
1443
  ];
1542
- if (harness.root) sources.push(node_path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH));
1543
- return [...sources.map(stampOf), `active-layers${FINGERPRINT_FIELD_SEPARATOR}${harness.layers.join(",")}`].join(FINGERPRINT_ENTRY_SEPARATOR);
1444
+ return (await Promise.all(sources.map((target) => stampOf(this.io, target)))).join(FINGERPRINT_ENTRY_SEPARATOR);
1544
1445
  }
1545
1446
  /**
1546
1447
  * The uncached read. Protected so a test can count how often it actually runs,
1547
1448
  * which is the only direct way to assert that the cache is doing its job.
1548
1449
  */
1549
- load(cwd, scope) {
1550
- const userSettingsPath = getUserAgentSettingsPath(this.environment);
1551
- const projectSettingsPath = getProjectAgentSettingsPath(cwd, this.environment);
1552
- const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
1553
- const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
1450
+ async load(cwd, scope, team) {
1451
+ const userSettingsPath = getUserAgentSettingsPath(this.io);
1452
+ const projectSettingsPath = await getProjectAgentSettingsPath(this.io, cwd);
1453
+ const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : await readSubagentSettings(this.io, userSettingsPath);
1454
+ const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : await readSubagentSettings(this.io, projectSettingsPath);
1554
1455
  const legacyDefaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
1555
- const teamPackage = resolveActiveTeamPackageConfig(this.environment);
1556
- const packagePrimaryModel = teamModelSpecs(teamPackage?.config)?.[0];
1557
- const defaultModel = packagePrimaryModel && teamPackage ? {
1456
+ const packagePrimaryModel = team?.models?.[0];
1457
+ const defaultModel = packagePrimaryModel && team ? {
1558
1458
  type: "packages.team.defaultModel",
1559
1459
  scope: "package",
1560
- path: teamPackage.path,
1460
+ path: team.path ?? "",
1561
1461
  model: packagePrimaryModel
1562
1462
  } : legacyDefaultModel;
1563
1463
  const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
1564
1464
  const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
1565
1465
  const resolve = (agents) => applyCustomAgentOverrides(applySubagentDefaults(agents, defaultModel, defaultThinking, defaultExtensions), userSettings, projectSettings, userSettingsPath, projectSettingsPath);
1566
- const plugin = resolve(pluginAgentDirs(this.environment).flatMap((directory) => loadAgentsFromDir(directory, "plugin", this.environment)));
1466
+ const projectDirs = await resolveNearestProjectAgentDirs(this.io, cwd);
1467
+ const loadDirs = async (dirs, source) => resolve((await Promise.all(dirs.map((dir) => loadAgentsFromDir(this.io, dir, source)))).flat());
1468
+ const plugin = await loadDirs(pluginAgentDirs(this.io.environment), "plugin");
1567
1469
  return {
1568
- agents: mergeAgentsForScope(scope, scope === "project" ? [] : resolve(userAgentDirs(this.environment).flatMap((directory) => loadAgentsFromDir(directory, "user", this.environment))), scope === "user" ? [] : resolve(resolveNearestProjectAgentDirs(cwd, this.environment).readDirs.flatMap((directory) => loadAgentsFromDir(directory, "project", this.environment))), plugin).filter((agent) => agent.disabled !== true),
1569
- projectAgentsDir: resolveNearestProjectAgentDirs(cwd, this.environment).preferredDir,
1470
+ agents: mergeAgentsForScope(scope, scope === "project" ? [] : await loadDirs(userAgentDirs(this.io), "user"), scope === "user" ? [] : await loadDirs(projectDirs.readDirs, "project"), plugin).filter((agent) => agent.disabled !== true),
1471
+ projectAgentsDir: projectDirs.preferredDir,
1570
1472
  modelScope: projectSettings.modelScope ?? userSettings.modelScope
1571
1473
  };
1572
1474
  }
@@ -1606,7 +1508,7 @@ Object.defineProperty(exports, "agentHasWriteTools", {
1606
1508
  Object.defineProperty(exports, "canonicalizeDiscoveryCwd", {
1607
1509
  enumerable: true,
1608
1510
  get: function() {
1609
- return canonicalizeDiscoveryCwd;
1511
+ return canonicalizeDiscoveryCwd$1;
1610
1512
  }
1611
1513
  });
1612
1514
  Object.defineProperty(exports, "getAgentDir", {
@@ -1615,23 +1517,17 @@ Object.defineProperty(exports, "getAgentDir", {
1615
1517
  return getAgentDir;
1616
1518
  }
1617
1519
  });
1618
- Object.defineProperty(exports, "getProjectConfigDir", {
1619
- enumerable: true,
1620
- get: function() {
1621
- return getProjectConfigDir;
1622
- }
1623
- });
1624
- Object.defineProperty(exports, "resolveActiveTeamModelSpecs", {
1520
+ Object.defineProperty(exports, "getConfigDirName", {
1625
1521
  enumerable: true,
1626
1522
  get: function() {
1627
- return resolveActiveTeamModelSpecs;
1523
+ return getConfigDirName;
1628
1524
  }
1629
1525
  });
1630
- Object.defineProperty(exports, "resolveActiveTeamPackageConfig", {
1526
+ Object.defineProperty(exports, "getProjectConfigDir", {
1631
1527
  enumerable: true,
1632
1528
  get: function() {
1633
- return resolveActiveTeamPackageConfig;
1529
+ return getProjectConfigDir;
1634
1530
  }
1635
1531
  });
1636
1532
 
1637
- //# sourceMappingURL=agentDiscovery-Whwzc4dP.cjs.map
1533
+ //# sourceMappingURL=agentDiscovery-DTTlM_LT.cjs.map