@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.
- package/LICENSE +166 -5
- package/README.md +11 -12
- package/dist/{agentDiscovery-Whwzc4dP.cjs → agentDiscovery-DTTlM_LT.cjs} +246 -350
- package/dist/agentDiscovery-DTTlM_LT.cjs.map +1 -0
- package/dist/{agentDiscovery-ZQlFkKvB.mjs → agentDiscovery-ZNG8iZXV.mjs} +210 -279
- package/dist/agentDiscovery-ZNG8iZXV.mjs.map +1 -0
- package/dist/apiContracts.cjs +10 -3
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.d.mts.map +1 -1
- package/dist/apiContracts.mjs +10 -3
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/{externalProcessIpc-ojE0pvw0.cjs → atomicJson-DRT03Ql5.cjs} +155 -21
- package/dist/atomicJson-DRT03Ql5.cjs.map +1 -0
- package/dist/{externalProcessIpc-C-15lJrB.mjs → atomicJson-VVM1iAPu.mjs} +140 -12
- package/dist/atomicJson-VVM1iAPu.mjs.map +1 -0
- package/dist/{capabilityCeiling-CF7kAS18.mjs → capabilityCeiling-BaHT9v9S.mjs} +3 -3
- package/dist/capabilityCeiling-BaHT9v9S.mjs.map +1 -0
- package/dist/{capabilityCeiling-C-Y7H8oC.cjs → capabilityCeiling-CO3ZjYmY.cjs} +3 -3
- package/dist/capabilityCeiling-CO3ZjYmY.cjs.map +1 -0
- package/dist/capabilityCeiling.cjs +2 -2
- package/dist/capabilityCeiling.d.mts +2 -2
- package/dist/capabilityCeiling.mjs +2 -2
- package/dist/delegation.cjs +1 -1
- package/dist/delegation.cjs.map +1 -1
- package/dist/delegation.d.mts +1 -1
- package/dist/delegation.mjs +1 -1
- package/dist/delegation.mjs.map +1 -1
- package/dist/delegationApi.cjs +30 -30
- package/dist/delegationApi.d.mts +2 -2
- package/dist/delegationApi.mjs +1 -2
- package/dist/env.cjs +1 -1
- package/dist/env.d.mts +1 -1
- package/dist/env.mjs +1 -2
- package/dist/{environment-DrqhrxG_.mjs → environment-BtvJI2S-.mjs} +3 -3
- package/dist/environment-BtvJI2S-.mjs.map +1 -0
- package/dist/{environment-ma5oQ7uM.d.mts → environment-Bz1OlWIT.d.mts} +2 -2
- package/dist/{environment-ma5oQ7uM.d.mts.map → environment-Bz1OlWIT.d.mts.map} +1 -1
- package/dist/{environment-CaCW3SgT.cjs → environment-Cqs7vHJL.cjs} +3 -3
- package/dist/environment-Cqs7vHJL.cjs.map +1 -0
- package/dist/extensions/native.mjs +34 -12
- package/dist/extensions/native.mjs.map +1 -1
- package/dist/extensions/server.cjs +990 -786
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +2 -2
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +975 -772
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +994 -807
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/fablePlan.cjs +3 -3
- package/dist/fablePlan.d.mts +2 -2
- package/dist/fablePlan.mjs +1 -2
- package/dist/{fablePlanApi-CZ3uUcMi.d.mts → fablePlanApi-BTNKjLni.d.mts} +6 -6
- package/dist/{fablePlanApi-CZ3uUcMi.d.mts.map → fablePlanApi-BTNKjLni.d.mts.map} +1 -1
- package/dist/{fablePlanApi-wBu9bRXU.cjs → fablePlanApi-BhI4jnDP.cjs} +12 -12
- package/dist/fablePlanApi-BhI4jnDP.cjs.map +1 -0
- package/dist/{fablePlanApi-BsHhgVFV.mjs → fablePlanApi-DuMg43Hu.mjs} +5 -5
- package/dist/fablePlanApi-DuMg43Hu.mjs.map +1 -0
- package/dist/fablePlanApi.cjs +3 -3
- package/dist/fablePlanApi.d.mts +2 -2
- package/dist/fablePlanApi.mjs +1 -2
- package/dist/index.cjs +17 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +5 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +15 -5
- package/dist/index.mjs.map +1 -1
- package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
- package/dist/runs/background/cliRunnerEntry.cjs +26 -25
- package/dist/runs/background/cliRunnerEntry.cjs.map +1 -1
- package/dist/runs/background/cliRunnerEntry.mjs +14 -14
- package/dist/runs/background/cliRunnerEntry.mjs.map +1 -1
- package/dist/runtimeSubagentPolicy.cjs +3 -3
- package/dist/runtimeSubagentPolicy.d.mts +2 -2
- package/dist/runtimeSubagentPolicy.mjs +1 -2
- package/dist/runtimeSubagentTool.cjs +7 -2
- package/dist/runtimeSubagentTool.d.mts +1 -1
- package/dist/runtimeSubagentTool.mjs +1 -2
- package/dist/{subagentPolicy-ALJam4GN.d.mts → subagentPolicy-BE-OZoa-.d.mts} +6 -6
- package/dist/subagentPolicy-BE-OZoa-.d.mts.map +1 -0
- package/dist/{subagentPolicy-DQRyKDH-.cjs → subagentPolicy-DIpaKHO8.cjs} +8 -8
- package/dist/subagentPolicy-DIpaKHO8.cjs.map +1 -0
- package/dist/{subagentPolicy-DxjsKeyj.mjs → subagentPolicy-LfcXuqO5.mjs} +5 -5
- package/dist/subagentPolicy-LfcXuqO5.mjs.map +1 -0
- package/dist/subagentPolicy.cjs +3 -3
- package/dist/subagentPolicy.d.mts +2 -2
- package/dist/subagentPolicy.mjs +1 -2
- package/dist/{subagentTool-Ca2zQXKw.cjs → subagentTool-BueNPGHo.cjs} +5 -16
- package/dist/subagentTool-BueNPGHo.cjs.map +1 -0
- package/dist/{subagentTool-BTssGLw8.d.mts → subagentTool-UdB3s6Mn.d.mts} +2 -11
- package/dist/subagentTool-UdB3s6Mn.d.mts.map +1 -0
- package/dist/{subagentTool-WOYrjxgT.mjs → subagentTool-a2oh2v5B.mjs} +5 -16
- package/dist/subagentTool-a2oh2v5B.mjs.map +1 -0
- package/dist/subagentTool.cjs +7 -2
- package/dist/subagentTool.d.mts +1 -1
- package/dist/subagentTool.mjs +1 -2
- package/dist/teamSnapshot.d.mts +1 -0
- package/dist/teamSnapshot.d.mts.map +1 -1
- package/dist/teamSnapshot.mjs +1 -2
- package/package.json +25 -24
- package/dist/agentDiscovery-Whwzc4dP.cjs.map +0 -1
- package/dist/agentDiscovery-ZQlFkKvB.mjs.map +0 -1
- package/dist/atomicJson-D0346bt5.cjs +0 -178
- package/dist/atomicJson-D0346bt5.cjs.map +0 -1
- package/dist/atomicJson-D2t0CDh9.mjs +0 -137
- package/dist/atomicJson-D2t0CDh9.mjs.map +0 -1
- package/dist/capabilityCeiling-C-Y7H8oC.cjs.map +0 -1
- package/dist/capabilityCeiling-CF7kAS18.mjs.map +0 -1
- package/dist/environment-CaCW3SgT.cjs.map +0 -1
- package/dist/environment-DrqhrxG_.mjs.map +0 -1
- package/dist/externalProcessIpc-C-15lJrB.mjs.map +0 -1
- package/dist/externalProcessIpc-ojE0pvw0.cjs.map +0 -1
- package/dist/fablePlanApi-BsHhgVFV.mjs.map +0 -1
- package/dist/fablePlanApi-wBu9bRXU.cjs.map +0 -1
- package/dist/subagentPolicy-ALJam4GN.d.mts.map +0 -1
- package/dist/subagentPolicy-DQRyKDH-.cjs.map +0 -1
- package/dist/subagentPolicy-DxjsKeyj.mjs.map +0 -1
- package/dist/subagentTool-BTssGLw8.d.mts.map +0 -1
- package/dist/subagentTool-Ca2zQXKw.cjs.map +0 -1
- package/dist/subagentTool-WOYrjxgT.mjs.map +0 -1
|
@@ -1,85 +1,8 @@
|
|
|
1
|
-
import "./atomicJson-D2t0CDh9.mjs";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
1
|
import * as path from "node:path";
|
|
4
|
-
import {
|
|
2
|
+
import { getOrThrow, readTextIfExists } from "@agimon-ai/po-core/platform";
|
|
3
|
+
import * as fs from "node:fs";
|
|
5
4
|
import * as os from "node:os";
|
|
6
5
|
import { piAgentDirectory } from "@agimon-ai/po-core/piSettings";
|
|
7
|
-
//#region src/schemas/team/packageConfig.ts
|
|
8
|
-
const PO_TEAM_PACKAGE = "@agimon-ai/po-team";
|
|
9
|
-
const TEAM_CONFIG_KEYS = ["models", "excludeTools"];
|
|
10
|
-
const TEAM_MODEL_KEYS = ["model", "thinking"];
|
|
11
|
-
const THINKING_VALUES = /* @__PURE__ */ new Set([
|
|
12
|
-
"off",
|
|
13
|
-
"minimal",
|
|
14
|
-
"low",
|
|
15
|
-
"medium",
|
|
16
|
-
"high",
|
|
17
|
-
"xhigh",
|
|
18
|
-
"max"
|
|
19
|
-
]);
|
|
20
|
-
function isRecord(value) {
|
|
21
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
-
}
|
|
23
|
-
function assertKnownKeys(value, keys, location) {
|
|
24
|
-
const unsupported = Object.keys(value).filter((key) => !keys.includes(key));
|
|
25
|
-
if (unsupported.length > 0) throw new Error(`${location} has unsupported field(s): ${unsupported.join(", ")}`);
|
|
26
|
-
}
|
|
27
|
-
/** Validate and normalize one Team-owned package configuration mapping. */
|
|
28
|
-
function parseTeamPackageConfig(value, location) {
|
|
29
|
-
if (!isRecord(value)) throw new Error(`${location} must be a mapping`);
|
|
30
|
-
assertKnownKeys(value, TEAM_CONFIG_KEYS, location);
|
|
31
|
-
const hasModels = Object.hasOwn(value, "models");
|
|
32
|
-
const hasExcludeTools = Object.hasOwn(value, "excludeTools");
|
|
33
|
-
if (!hasModels && !hasExcludeTools) throw new Error(`${location} must define models or excludeTools`);
|
|
34
|
-
let models;
|
|
35
|
-
if (hasModels) {
|
|
36
|
-
if (!Array.isArray(value.models) || value.models.length === 0) throw new Error(`${location}.models must be a non-empty array`);
|
|
37
|
-
models = value.models.map((entry, index) => {
|
|
38
|
-
const entryLocation = `${location}.models[${index}]`;
|
|
39
|
-
if (!isRecord(entry)) throw new Error(`${entryLocation} must be a mapping`);
|
|
40
|
-
assertKnownKeys(entry, TEAM_MODEL_KEYS, entryLocation);
|
|
41
|
-
if (typeof entry.model !== "string" || !entry.model.trim()) throw new Error(`${entryLocation}.model must be a non-empty string`);
|
|
42
|
-
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(", ")}`);
|
|
43
|
-
return {
|
|
44
|
-
model: entry.model.trim(),
|
|
45
|
-
...entry.thinking ? { thinking: entry.thinking } : {}
|
|
46
|
-
};
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
let excludeTools;
|
|
50
|
-
if (hasExcludeTools) {
|
|
51
|
-
if (!Array.isArray(value.excludeTools) || value.excludeTools.length === 0) throw new Error(`${location}.excludeTools must be a non-empty array`);
|
|
52
|
-
excludeTools = [...new Set(value.excludeTools.map((entry, index) => {
|
|
53
|
-
if (typeof entry !== "string" || !entry.trim()) throw new Error(`${location}.excludeTools[${index}] must be a non-empty string`);
|
|
54
|
-
return entry.trim();
|
|
55
|
-
}))];
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
...models ? { models } : {},
|
|
59
|
-
...excludeTools ? { excludeTools } : {}
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
/** Merge Team configurations contributed by selected layers in selection order. */
|
|
63
|
-
function mergeTeamPackageConfigurations(entries) {
|
|
64
|
-
let models;
|
|
65
|
-
const excludeTools = /* @__PURE__ */ new Set();
|
|
66
|
-
let hasExcludeTools = false;
|
|
67
|
-
for (const entry of entries) {
|
|
68
|
-
const location = `Package "${entry.specifier}" config in layer "${entry.layer}" of .po/modes.yaml`;
|
|
69
|
-
const next = parseTeamPackageConfig(entry.config, location);
|
|
70
|
-
if (next.models) models = next.models.map((model) => ({ ...model }));
|
|
71
|
-
if (next.excludeTools) {
|
|
72
|
-
hasExcludeTools = true;
|
|
73
|
-
for (const tool of next.excludeTools) excludeTools.add(tool);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!models && !hasExcludeTools) return void 0;
|
|
77
|
-
return {
|
|
78
|
-
...models ? { models } : {},
|
|
79
|
-
...hasExcludeTools ? { excludeTools: [...excludeTools] } : {}
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
//#endregion
|
|
83
6
|
//#region src/services/agentFrontmatter/index.ts
|
|
84
7
|
/**
|
|
85
8
|
* Parsing the YAML frontmatter block at the head of an agent or chain file.
|
|
@@ -309,29 +232,105 @@ function getConfigDirName(environment = process.env) {
|
|
|
309
232
|
function getProjectConfigDir(projectRoot, environment = process.env) {
|
|
310
233
|
return path.join(projectRoot, getConfigDirName(environment));
|
|
311
234
|
}
|
|
312
|
-
/**
|
|
313
|
-
function
|
|
314
|
-
|
|
315
|
-
|
|
235
|
+
/** The user-level agent directory Po runs with, from Po's own resolver. */
|
|
236
|
+
function getAgentDir(environment = process.env) {
|
|
237
|
+
return piAgentDirectory(environment, environment.HOME ?? os.homedir());
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/services/agentProjectRoot/sync.ts
|
|
241
|
+
/** @deprecated Node-only shim. */
|
|
242
|
+
/**
|
|
243
|
+
* Locating the project root that agent configuration hangs off.
|
|
244
|
+
*
|
|
245
|
+
* A project root is any ancestor of the working directory that carries a config
|
|
246
|
+
* directory or a legacy `.agents` directory. There can be several: a package
|
|
247
|
+
* inside a monorepo and the monorepo itself both qualify. Which one wins is a
|
|
248
|
+
* setting, because both answers are legitimate. Teams that keep one shared set
|
|
249
|
+
* of agents at the repository root want `git-root`, teams that give each package
|
|
250
|
+
* its own want `nearest`, and neither can be inferred from the tree alone.
|
|
251
|
+
*
|
|
252
|
+
* DESIGN PATTERNS:
|
|
253
|
+
* - Candidates are collected once, nearest first, then a policy picks from them.
|
|
254
|
+
* Separating collection from policy is what lets `git-root` be answered
|
|
255
|
+
* without a second walk
|
|
256
|
+
* - Path derivation goes through `shared/configDir.ts`, so a rebranded build
|
|
257
|
+
* that renames its config directory needs no change here
|
|
258
|
+
*
|
|
259
|
+
* AVOID:
|
|
260
|
+
* - Hardcoding the config directory name; it is discovered, not assumed
|
|
261
|
+
* - Treating the nearest candidate as the project root without reading the
|
|
262
|
+
* setting first
|
|
263
|
+
*/
|
|
264
|
+
/** Stable cache identity for equivalent absolute, relative, and symlinked cwd spellings. */
|
|
265
|
+
function canonicalizeDiscoveryCwd$1(cwd) {
|
|
266
|
+
const resolved = path.resolve(cwd);
|
|
316
267
|
try {
|
|
317
|
-
|
|
318
|
-
} catch
|
|
319
|
-
|
|
320
|
-
throw new Error(`Failed to read settings file '${filePath}': ${message}`, { cause: error });
|
|
268
|
+
return fs.realpathSync.native(resolved);
|
|
269
|
+
} catch {
|
|
270
|
+
return resolved;
|
|
321
271
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/services/agentMemory/index.ts
|
|
275
|
+
/** Tools that let an agent append to its own memory file. */
|
|
276
|
+
const WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
277
|
+
"edit",
|
|
278
|
+
"write",
|
|
279
|
+
"bash"
|
|
280
|
+
]);
|
|
281
|
+
function unquoteFrontmatterValue(value) {
|
|
282
|
+
const trimmed = value.trim();
|
|
283
|
+
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
|
|
284
|
+
return trimmed;
|
|
285
|
+
}
|
|
286
|
+
function parseMemoryScope(value) {
|
|
287
|
+
return value === "project" || value === "user" ? value : void 0;
|
|
288
|
+
}
|
|
289
|
+
function parseMemoryBoolean(value) {
|
|
290
|
+
if (value === "true") return true;
|
|
291
|
+
if (value === "false") return false;
|
|
292
|
+
}
|
|
293
|
+
function parsePositiveInteger(value) {
|
|
294
|
+
if (value === void 0) return void 0;
|
|
295
|
+
const parsed = Number(value);
|
|
296
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
297
|
+
}
|
|
298
|
+
/** Split a `memory` frontmatter block into key/value pairs, either spelling. */
|
|
299
|
+
function readMemoryEntries(raw) {
|
|
300
|
+
const entries = /* @__PURE__ */ new Map();
|
|
301
|
+
const inlineObject = raw.trim().match(/^\{(.*)\}$/s);
|
|
302
|
+
const parts = inlineObject?.[1] !== void 0 ? inlineObject[1].split(",") : raw.split("\n");
|
|
303
|
+
for (const part of parts) {
|
|
304
|
+
const match = part.trim().match(/^([\w-]+)\s*:\s*(.*)$/);
|
|
305
|
+
if (!match?.[1] || match[2] === void 0) continue;
|
|
306
|
+
entries.set(match[1], unquoteFrontmatterValue(match[2]));
|
|
328
307
|
}
|
|
329
|
-
|
|
330
|
-
return parsed;
|
|
308
|
+
return entries;
|
|
331
309
|
}
|
|
332
|
-
/**
|
|
333
|
-
|
|
334
|
-
|
|
310
|
+
/**
|
|
311
|
+
* Parse a `memory` frontmatter block, or undefined when it declares nothing.
|
|
312
|
+
*
|
|
313
|
+
* Accepts both the inline `{ scope: project }` form and the indented block form,
|
|
314
|
+
* because agent files in the wild use each.
|
|
315
|
+
*/
|
|
316
|
+
function parseMemoryFrontmatter(raw) {
|
|
317
|
+
if (!raw) return void 0;
|
|
318
|
+
const entries = readMemoryEntries(raw);
|
|
319
|
+
const enabled = parseMemoryBoolean(entries.get("enabled"));
|
|
320
|
+
const scope = parseMemoryScope(entries.get("scope"));
|
|
321
|
+
const maxEntries = parsePositiveInteger(entries.get("maxEntries"));
|
|
322
|
+
if (enabled === void 0 && scope === void 0 && maxEntries === void 0) return void 0;
|
|
323
|
+
const config = {};
|
|
324
|
+
if (enabled !== void 0) config.enabled = enabled;
|
|
325
|
+
if (scope !== void 0) config.scope = scope;
|
|
326
|
+
if (maxEntries !== void 0) config.maxEntries = maxEntries;
|
|
327
|
+
return config;
|
|
328
|
+
}
|
|
329
|
+
/** Whether an agent can write files this run. An unset `tools` inherits the builtins. */
|
|
330
|
+
function agentHasWriteTools(agent) {
|
|
331
|
+
const tools = agent.tools;
|
|
332
|
+
if (!tools) return true;
|
|
333
|
+
return tools.some((tool) => WRITE_TOOLS.has(tool));
|
|
335
334
|
}
|
|
336
335
|
//#endregion
|
|
337
336
|
//#region src/services/agentProjectRoot/index.ts
|
|
@@ -349,7 +348,7 @@ function getAgentDir(environment = process.env) {
|
|
|
349
348
|
* - Candidates are collected once, nearest first, then a policy picks from them.
|
|
350
349
|
* Separating collection from policy is what lets `git-root` be answered
|
|
351
350
|
* without a second walk
|
|
352
|
-
* - Path derivation
|
|
351
|
+
* - Path derivation uses injected directory settings, so a rebranded build
|
|
353
352
|
* that renames its config directory needs no change here
|
|
354
353
|
*
|
|
355
354
|
* AVOID:
|
|
@@ -357,6 +356,25 @@ function getAgentDir(environment = process.env) {
|
|
|
357
356
|
* - Treating the nearest candidate as the project root without reading the
|
|
358
357
|
* setting first
|
|
359
358
|
*/
|
|
359
|
+
async function readSettingsFile(io, filePath) {
|
|
360
|
+
let raw;
|
|
361
|
+
try {
|
|
362
|
+
raw = await readTextIfExists(io.fs, filePath, io.context);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
365
|
+
throw new Error(`Failed to read settings file '${filePath}': ${message}`, { cause: error });
|
|
366
|
+
}
|
|
367
|
+
if (raw === void 0) return {};
|
|
368
|
+
let parsed;
|
|
369
|
+
try {
|
|
370
|
+
parsed = JSON.parse(raw);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
373
|
+
throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
|
|
374
|
+
}
|
|
375
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
|
|
376
|
+
return parsed;
|
|
377
|
+
}
|
|
360
378
|
const SETTINGS_FILE_NAME = "settings.json";
|
|
361
379
|
const GIT_DIR_NAME$1 = ".git";
|
|
362
380
|
/**
|
|
@@ -370,32 +388,33 @@ const AGENTS_DIR_NAME = "agents";
|
|
|
370
388
|
const SUBAGENTS_SETTINGS_KEY$1 = "subagents";
|
|
371
389
|
const PROJECT_ROOT_RESOLUTION_KEY = "projectRootResolution";
|
|
372
390
|
/** Stable cache identity for equivalent absolute, relative, and symlinked cwd spellings. */
|
|
373
|
-
function canonicalizeDiscoveryCwd(cwd) {
|
|
391
|
+
async function canonicalizeDiscoveryCwd(io, cwd) {
|
|
374
392
|
const resolved = path.resolve(cwd);
|
|
375
393
|
try {
|
|
376
|
-
return fs.
|
|
394
|
+
return getOrThrow(await io.fs.canonicalPath(resolved, io.context));
|
|
377
395
|
} catch {
|
|
378
396
|
return resolved;
|
|
379
397
|
}
|
|
380
398
|
}
|
|
381
399
|
/** Whether a path exists and is a directory. Any failure reads as "no". */
|
|
382
|
-
function isDirectory
|
|
400
|
+
async function isDirectory(io, dirPath) {
|
|
383
401
|
try {
|
|
384
|
-
|
|
402
|
+
const canonical = getOrThrow(await io.fs.canonicalPath(dirPath, io.context));
|
|
403
|
+
return getOrThrow(await io.fs.fileInfo(canonical, io.context)).kind === "directory";
|
|
385
404
|
} catch {
|
|
386
405
|
return false;
|
|
387
406
|
}
|
|
388
407
|
}
|
|
389
408
|
/** True when `dir` carries either marker that makes it a project root. */
|
|
390
|
-
function isProjectRootCandidate(
|
|
391
|
-
return isDirectory
|
|
409
|
+
async function isProjectRootCandidate(io, dir) {
|
|
410
|
+
return await isDirectory(io, path.join(dir, io.configDirName)) || await isDirectory(io, path.join(dir, DOT_AGENTS_DIR_NAME));
|
|
392
411
|
}
|
|
393
412
|
/** Every project root above `cwd`, nearest first. */
|
|
394
|
-
function findProjectRootCandidates(
|
|
413
|
+
async function findProjectRootCandidates(io, cwd) {
|
|
395
414
|
const roots = [];
|
|
396
415
|
let currentDir = cwd;
|
|
397
416
|
while (true) {
|
|
398
|
-
if (isProjectRootCandidate(
|
|
417
|
+
if (await isProjectRootCandidate(io, currentDir)) roots.push(currentDir);
|
|
399
418
|
const parentDir = path.dirname(currentDir);
|
|
400
419
|
if (parentDir === currentDir) return roots;
|
|
401
420
|
currentDir = parentDir;
|
|
@@ -407,10 +426,11 @@ function findProjectRootCandidates(cwd, environment = process.env) {
|
|
|
407
426
|
* Existence is enough: a worktree or submodule has a `.git` file rather than a
|
|
408
427
|
* directory, and both are real repository roots.
|
|
409
428
|
*/
|
|
410
|
-
function findNearestGitRoot(cwd) {
|
|
429
|
+
async function findNearestGitRoot(io, cwd) {
|
|
411
430
|
let currentDir = cwd;
|
|
412
431
|
while (true) {
|
|
413
|
-
|
|
432
|
+
const gitMarker = await io.fs.exists(path.join(currentDir, GIT_DIR_NAME$1), io.context);
|
|
433
|
+
if (gitMarker.ok && gitMarker.value) return currentDir;
|
|
414
434
|
const parentDir = path.dirname(currentDir);
|
|
415
435
|
if (parentDir === currentDir) return null;
|
|
416
436
|
currentDir = parentDir;
|
|
@@ -422,10 +442,9 @@ function findNearestGitRoot(cwd) {
|
|
|
422
442
|
* Deliberately throws on an unrecognised value rather than falling back: a typo
|
|
423
443
|
* here silently changes which agents the whole session sees.
|
|
424
444
|
*/
|
|
425
|
-
function readProjectRootResolution(
|
|
426
|
-
const settingsPath = path.join(
|
|
427
|
-
|
|
428
|
-
const subagents = readSettingsFileStrict(settingsPath)[SUBAGENTS_SETTINGS_KEY$1];
|
|
445
|
+
async function readProjectRootResolution(io, projectRoot) {
|
|
446
|
+
const settingsPath = path.join(projectRoot, io.configDirName, SETTINGS_FILE_NAME);
|
|
447
|
+
const subagents = (await readSettingsFile(io, settingsPath))[SUBAGENTS_SETTINGS_KEY$1];
|
|
429
448
|
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return void 0;
|
|
430
449
|
const value = subagents[PROJECT_ROOT_RESOLUTION_KEY];
|
|
431
450
|
if (value === void 0) return void 0;
|
|
@@ -439,15 +458,15 @@ function readProjectRootResolution(projectRoot, environment = process.env) {
|
|
|
439
458
|
* of its monorepo's shared agents, and from the git root second so a repository
|
|
440
459
|
* can claim its packages without editing each one.
|
|
441
460
|
*/
|
|
442
|
-
function findConfiguredProjectRoot(
|
|
443
|
-
const candidates = findProjectRootCandidates(
|
|
461
|
+
async function findConfiguredProjectRoot(io, cwd) {
|
|
462
|
+
const candidates = await findProjectRootCandidates(io, cwd);
|
|
444
463
|
const nearestRoot = candidates[0];
|
|
445
464
|
if (!nearestRoot) return null;
|
|
446
|
-
const nearestMode = readProjectRootResolution(
|
|
465
|
+
const nearestMode = await readProjectRootResolution(io, nearestRoot);
|
|
447
466
|
if (nearestMode === "nearest") return nearestRoot;
|
|
448
|
-
const gitRoot = findNearestGitRoot(cwd);
|
|
467
|
+
const gitRoot = await findNearestGitRoot(io, cwd);
|
|
449
468
|
const gitProjectRoot = gitRoot ? candidates.find((candidate) => path.resolve(candidate) === path.resolve(gitRoot)) : void 0;
|
|
450
|
-
if (gitProjectRoot && (nearestMode === "git-root" || readProjectRootResolution(
|
|
469
|
+
if (gitProjectRoot && (nearestMode === "git-root" || await readProjectRootResolution(io, gitProjectRoot) === "git-root")) return gitProjectRoot;
|
|
451
470
|
return nearestRoot;
|
|
452
471
|
}
|
|
453
472
|
/**
|
|
@@ -458,17 +477,17 @@ function findConfiguredProjectRoot(cwd, environment = process.env) {
|
|
|
458
477
|
* working after the move. Returned unfiltered because the caller reads each
|
|
459
478
|
* directory anyway and a missing one costs nothing.
|
|
460
479
|
*/
|
|
461
|
-
function userAgentDirs(
|
|
462
|
-
return [path.join(
|
|
480
|
+
function userAgentDirs(io) {
|
|
481
|
+
return [path.join(piAgentDirectory(io.environment, io.homeDirectory), AGENTS_DIR_NAME), path.join(io.homeDirectory, DOT_AGENTS_DIR_NAME)];
|
|
463
482
|
}
|
|
464
483
|
/** The user-level settings file. Always resolvable, unlike the project one. */
|
|
465
|
-
function getUserAgentSettingsPath(
|
|
466
|
-
return path.join(
|
|
484
|
+
function getUserAgentSettingsPath(io) {
|
|
485
|
+
return path.join(piAgentDirectory(io.environment, io.homeDirectory), SETTINGS_FILE_NAME);
|
|
467
486
|
}
|
|
468
487
|
/** The project settings file, or null when `cwd` sits under no project root. */
|
|
469
|
-
function getProjectAgentSettingsPath(
|
|
470
|
-
const projectRoot = findConfiguredProjectRoot(
|
|
471
|
-
return projectRoot ? path.join(
|
|
488
|
+
async function getProjectAgentSettingsPath(io, cwd) {
|
|
489
|
+
const projectRoot = await findConfiguredProjectRoot(io, cwd);
|
|
490
|
+
return projectRoot ? path.join(projectRoot, io.configDirName, SETTINGS_FILE_NAME) : null;
|
|
472
491
|
}
|
|
473
492
|
/**
|
|
474
493
|
* Project agent directories for `cwd`.
|
|
@@ -478,85 +497,23 @@ function getProjectAgentSettingsPath(cwd, environment = process.env) {
|
|
|
478
497
|
* `preferredDir` is where a newly created agent should be written, and is
|
|
479
498
|
* returned whether or not it exists yet.
|
|
480
499
|
*/
|
|
481
|
-
function resolveNearestProjectAgentDirs(
|
|
482
|
-
const projectRoot = findConfiguredProjectRoot(
|
|
500
|
+
async function resolveNearestProjectAgentDirs(io, cwd) {
|
|
501
|
+
const projectRoot = await findConfiguredProjectRoot(io, cwd);
|
|
483
502
|
if (!projectRoot) return {
|
|
484
503
|
readDirs: [],
|
|
485
504
|
preferredDir: null
|
|
486
505
|
};
|
|
487
506
|
const legacyDir = path.join(projectRoot, DOT_AGENTS_DIR_NAME);
|
|
488
|
-
const preferredDir = path.join(
|
|
507
|
+
const preferredDir = path.join(projectRoot, io.configDirName, AGENTS_DIR_NAME);
|
|
489
508
|
const readDirs = [];
|
|
490
|
-
if (isDirectory
|
|
491
|
-
if (isDirectory
|
|
509
|
+
if (await isDirectory(io, legacyDir)) readDirs.push(legacyDir);
|
|
510
|
+
if (await isDirectory(io, preferredDir)) readDirs.push(preferredDir);
|
|
492
511
|
return {
|
|
493
512
|
readDirs,
|
|
494
513
|
preferredDir
|
|
495
514
|
};
|
|
496
515
|
}
|
|
497
516
|
//#endregion
|
|
498
|
-
//#region src/services/agentMemory/index.ts
|
|
499
|
-
/** Tools that let an agent append to its own memory file. */
|
|
500
|
-
const WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
501
|
-
"edit",
|
|
502
|
-
"write",
|
|
503
|
-
"bash"
|
|
504
|
-
]);
|
|
505
|
-
function unquoteFrontmatterValue(value) {
|
|
506
|
-
const trimmed = value.trim();
|
|
507
|
-
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
|
|
508
|
-
return trimmed;
|
|
509
|
-
}
|
|
510
|
-
function parseMemoryScope(value) {
|
|
511
|
-
return value === "project" || value === "user" ? value : void 0;
|
|
512
|
-
}
|
|
513
|
-
function parseMemoryBoolean(value) {
|
|
514
|
-
if (value === "true") return true;
|
|
515
|
-
if (value === "false") return false;
|
|
516
|
-
}
|
|
517
|
-
function parsePositiveInteger(value) {
|
|
518
|
-
if (value === void 0) return void 0;
|
|
519
|
-
const parsed = Number(value);
|
|
520
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
521
|
-
}
|
|
522
|
-
/** Split a `memory` frontmatter block into key/value pairs, either spelling. */
|
|
523
|
-
function readMemoryEntries(raw) {
|
|
524
|
-
const entries = /* @__PURE__ */ new Map();
|
|
525
|
-
const inlineObject = raw.trim().match(/^\{(.*)\}$/s);
|
|
526
|
-
const parts = inlineObject?.[1] !== void 0 ? inlineObject[1].split(",") : raw.split("\n");
|
|
527
|
-
for (const part of parts) {
|
|
528
|
-
const match = part.trim().match(/^([\w-]+)\s*:\s*(.*)$/);
|
|
529
|
-
if (!match?.[1] || match[2] === void 0) continue;
|
|
530
|
-
entries.set(match[1], unquoteFrontmatterValue(match[2]));
|
|
531
|
-
}
|
|
532
|
-
return entries;
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Parse a `memory` frontmatter block, or undefined when it declares nothing.
|
|
536
|
-
*
|
|
537
|
-
* Accepts both the inline `{ scope: project }` form and the indented block form,
|
|
538
|
-
* because agent files in the wild use each.
|
|
539
|
-
*/
|
|
540
|
-
function parseMemoryFrontmatter(raw) {
|
|
541
|
-
if (!raw) return void 0;
|
|
542
|
-
const entries = readMemoryEntries(raw);
|
|
543
|
-
const enabled = parseMemoryBoolean(entries.get("enabled"));
|
|
544
|
-
const scope = parseMemoryScope(entries.get("scope"));
|
|
545
|
-
const maxEntries = parsePositiveInteger(entries.get("maxEntries"));
|
|
546
|
-
if (enabled === void 0 && scope === void 0 && maxEntries === void 0) return void 0;
|
|
547
|
-
const config = {};
|
|
548
|
-
if (enabled !== void 0) config.enabled = enabled;
|
|
549
|
-
if (scope !== void 0) config.scope = scope;
|
|
550
|
-
if (maxEntries !== void 0) config.maxEntries = maxEntries;
|
|
551
|
-
return config;
|
|
552
|
-
}
|
|
553
|
-
/** Whether an agent can write files this run. An unset `tools` inherits the builtins. */
|
|
554
|
-
function agentHasWriteTools(agent) {
|
|
555
|
-
const tools = agent.tools;
|
|
556
|
-
if (!tools) return true;
|
|
557
|
-
return tools.some((tool) => WRITE_TOOLS.has(tool));
|
|
558
|
-
}
|
|
559
|
-
//#endregion
|
|
560
517
|
//#region src/services/agentSettings/index.ts
|
|
561
518
|
const SUBAGENTS_SETTINGS_KEY = "subagents";
|
|
562
519
|
const AGENT_OVERRIDES_KEY = "agentOverrides";
|
|
@@ -748,9 +705,9 @@ function parseToolBudgetOverride(value, name, filePath) {
|
|
|
748
705
|
return budget;
|
|
749
706
|
}
|
|
750
707
|
/** Read and validate one settings file's `subagents` block. */
|
|
751
|
-
function readSubagentSettings(filePath) {
|
|
708
|
+
async function readSubagentSettings(io, filePath) {
|
|
752
709
|
if (!filePath) return EMPTY_SUBAGENT_SETTINGS;
|
|
753
|
-
const subagentsObject = readObjectField(
|
|
710
|
+
const subagentsObject = readObjectField(await readSettingsFile(io, filePath), SUBAGENTS_SETTINGS_KEY);
|
|
754
711
|
if (!subagentsObject) return EMPTY_SUBAGENT_SETTINGS;
|
|
755
712
|
let disableBuiltins;
|
|
756
713
|
if ("disableBuiltins" in subagentsObject) {
|
|
@@ -1116,16 +1073,9 @@ const LEGACY_AGENTS_DIR_NAME = ".agents";
|
|
|
1116
1073
|
const LEGACY_SKILLS_DIR_NAME = "skills";
|
|
1117
1074
|
const AGENT_FILE_EXTENSION = ".md";
|
|
1118
1075
|
const CHAIN_FILE_EXTENSION = ".chain.md";
|
|
1119
|
-
function isDirectory(target) {
|
|
1120
|
-
try {
|
|
1121
|
-
return fs.statSync(target).isDirectory();
|
|
1122
|
-
} catch {
|
|
1123
|
-
return false;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
1076
|
/** A directory that owns its own agent configuration is a separate project. */
|
|
1127
|
-
function isDiscoveryNestedProjectRoot(
|
|
1128
|
-
return isDirectory(
|
|
1077
|
+
async function isDiscoveryNestedProjectRoot(io, dir) {
|
|
1078
|
+
return await isDirectory(io, path.join(dir, io.configDirName)) || await isDirectory(io, path.join(dir, LEGACY_AGENTS_DIR_NAME));
|
|
1129
1079
|
}
|
|
1130
1080
|
/**
|
|
1131
1081
|
* Decide whether the walk should skip a subdirectory.
|
|
@@ -1134,10 +1084,11 @@ function isDiscoveryNestedProjectRoot(dir, environment) {
|
|
|
1134
1084
|
* belong to that project, not to the one being scanned. The root itself is
|
|
1135
1085
|
* exempt: scanning a project's own agent directory must not prune it.
|
|
1136
1086
|
*/
|
|
1137
|
-
function shouldPruneDiscoveryDir(rootDir, dir, dirName
|
|
1087
|
+
async function shouldPruneDiscoveryDir(io, rootDir, dir, dirName) {
|
|
1138
1088
|
if (DISCOVERY_PRUNED_DIR_NAMES.has(dirName)) return true;
|
|
1139
|
-
|
|
1140
|
-
|
|
1089
|
+
const gitMarker = await io.fs.exists(path.join(dir, GIT_DIR_NAME), io.context);
|
|
1090
|
+
if (gitMarker.ok && gitMarker.value) return true;
|
|
1091
|
+
return path.resolve(dir) !== path.resolve(rootDir) && await isDiscoveryNestedProjectRoot(io, dir);
|
|
1141
1092
|
}
|
|
1142
1093
|
/**
|
|
1143
1094
|
* Collect matching files under `dir`, sorted so discovery order is stable.
|
|
@@ -1145,24 +1096,19 @@ function shouldPruneDiscoveryDir(rootDir, dir, dirName, environment) {
|
|
|
1145
1096
|
* Symlinked entries are followed as files, because staging an agent by symlink
|
|
1146
1097
|
* is a supported way to share one definition across projects.
|
|
1147
1098
|
*/
|
|
1148
|
-
function listFilesRecursive(dir, predicate, rootDir = dir
|
|
1099
|
+
async function listFilesRecursive(io, dir, predicate, rootDir = dir) {
|
|
1149
1100
|
const files = [];
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1154
|
-
} catch {
|
|
1155
|
-
return files;
|
|
1156
|
-
}
|
|
1101
|
+
const result = await io.fs.listDir(dir, io.context);
|
|
1102
|
+
if (!result.ok) return files;
|
|
1103
|
+
const entries = result.value.sort((a, b) => a.name.localeCompare(b.name));
|
|
1157
1104
|
for (const entry of entries) {
|
|
1158
1105
|
const filePath = path.join(dir, entry.name);
|
|
1159
|
-
if (entry.
|
|
1160
|
-
if (!shouldPruneDiscoveryDir(rootDir, filePath, entry.name
|
|
1106
|
+
if (entry.kind === "directory") {
|
|
1107
|
+
if (!await shouldPruneDiscoveryDir(io, rootDir, filePath, entry.name)) files.push(...await listFilesRecursive(io, filePath, predicate, rootDir));
|
|
1161
1108
|
continue;
|
|
1162
1109
|
}
|
|
1163
|
-
if (
|
|
1164
|
-
if (
|
|
1165
|
-
files.push(filePath);
|
|
1110
|
+
if (entry.kind !== "file" && entry.kind !== "symlink") continue;
|
|
1111
|
+
if (predicate(entry.name)) files.push(filePath);
|
|
1166
1112
|
}
|
|
1167
1113
|
return files;
|
|
1168
1114
|
}
|
|
@@ -1206,13 +1152,15 @@ function isAgentFileName(fileName) {
|
|
|
1206
1152
|
* The returned configs carry only what the file itself declared. Settings
|
|
1207
1153
|
* defaults, builtin overrides, and cross-source precedence are the caller's job.
|
|
1208
1154
|
*/
|
|
1209
|
-
function loadAgentsFromDir(dir, source
|
|
1155
|
+
async function loadAgentsFromDir(io, dir, source) {
|
|
1210
1156
|
const agents = [];
|
|
1211
|
-
for (const filePath of listFilesRecursive(dir, isAgentFileName, dir
|
|
1157
|
+
for (const filePath of await listFilesRecursive(io, dir, isAgentFileName, dir)) {
|
|
1212
1158
|
if (isLegacyAgentSkillPath(dir, filePath)) continue;
|
|
1213
1159
|
let content;
|
|
1214
1160
|
try {
|
|
1215
|
-
|
|
1161
|
+
const text = await readTextIfExists(io.fs, filePath, io.context);
|
|
1162
|
+
if (text === void 0) continue;
|
|
1163
|
+
content = text;
|
|
1216
1164
|
} catch {
|
|
1217
1165
|
continue;
|
|
1218
1166
|
}
|
|
@@ -1273,7 +1221,7 @@ function loadAgentsFromDir(dir, source, environment = process.env) {
|
|
|
1273
1221
|
* Read from the environment on every call rather than captured once, because a
|
|
1274
1222
|
* host can stage a plugin directory after this module is first imported.
|
|
1275
1223
|
*/
|
|
1276
|
-
function pluginAgentDirs(environment
|
|
1224
|
+
function pluginAgentDirs(environment) {
|
|
1277
1225
|
return (environment["PI_SUBAGENT_EXTRA_AGENT_DIRS"] ?? "").split(path.delimiter).map((directory) => directory.trim()).filter((directory) => directory.length > 0);
|
|
1278
1226
|
}
|
|
1279
1227
|
//#endregion
|
|
@@ -1381,8 +1329,6 @@ const MAX_CACHED_DISCOVERIES = 64;
|
|
|
1381
1329
|
const FINGERPRINT_FIELD_SEPARATOR = ":";
|
|
1382
1330
|
const FINGERPRINT_ENTRY_SEPARATOR = "|";
|
|
1383
1331
|
const MISSING_SOURCE_MARKER = "-";
|
|
1384
|
-
const MAJOR_MODES_CONFIG_RELATIVE_PATH = path.join(".po", "modes.yaml");
|
|
1385
|
-
const THINKING_SUFFIX = /:(?:off|minimal|low|medium|high|xhigh|max)$/;
|
|
1386
1332
|
/**
|
|
1387
1333
|
* Merge the sources into one list, later sources shadowing earlier ones.
|
|
1388
1334
|
*
|
|
@@ -1398,37 +1344,21 @@ function mergeAgentsForScope(scope, userAgents, projectAgents, pluginAgents = []
|
|
|
1398
1344
|
return [...byName.values()];
|
|
1399
1345
|
}
|
|
1400
1346
|
/** Size and mtime of one path, or a marker when it does not exist. */
|
|
1401
|
-
function stampOf(target) {
|
|
1347
|
+
async function stampOf(io, target) {
|
|
1402
1348
|
try {
|
|
1403
|
-
const
|
|
1349
|
+
const canonical = getOrThrow(await io.fs.canonicalPath(target, io.context));
|
|
1350
|
+
const stat = getOrThrow(await io.fs.fileInfo(canonical, io.context));
|
|
1404
1351
|
return `${target}${FINGERPRINT_FIELD_SEPARATOR}${stat.mtimeMs}${FINGERPRINT_FIELD_SEPARATOR}${stat.size}`;
|
|
1405
1352
|
} catch {
|
|
1406
1353
|
return `${target}${FINGERPRINT_FIELD_SEPARATOR}${MISSING_SOURCE_MARKER}`;
|
|
1407
1354
|
}
|
|
1408
1355
|
}
|
|
1409
|
-
/** Resolve Team-owned package configuration from the current parent harness state. */
|
|
1410
|
-
function resolveActiveTeamPackageConfig(environment = process.env) {
|
|
1411
|
-
const harness = loadHarnessState(environment).state;
|
|
1412
|
-
if (!harness.root) return void 0;
|
|
1413
|
-
const layers = loadMajorModesConfig(harness.root, environment.HOME);
|
|
1414
|
-
const config = mergeTeamPackageConfigurations(resolvePackageConfigurations(layers, harness.layers, PO_TEAM_PACKAGE));
|
|
1415
|
-
return config ? {
|
|
1416
|
-
config,
|
|
1417
|
-
path: path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH)
|
|
1418
|
-
} : void 0;
|
|
1419
|
-
}
|
|
1420
|
-
function teamModelSpecs(config) {
|
|
1421
|
-
if (!config?.models) return void 0;
|
|
1422
|
-
return config.models.map(({ model, thinking }) => thinking ? `${model.replace(THINKING_SUFFIX, "")}:${thinking}` : model);
|
|
1423
|
-
}
|
|
1424
|
-
/** Resolve Team's ordered package model policy without merging it into an agent definition. */
|
|
1425
|
-
function resolveActiveTeamModelSpecs(environment = process.env) {
|
|
1426
|
-
return teamModelSpecs(resolveActiveTeamPackageConfig(environment)?.config);
|
|
1427
|
-
}
|
|
1428
1356
|
var AgentDiscoveryService = class {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1357
|
+
io;
|
|
1358
|
+
agentConfig;
|
|
1359
|
+
constructor(io, agentConfig) {
|
|
1360
|
+
this.io = io;
|
|
1361
|
+
this.agentConfig = agentConfig;
|
|
1432
1362
|
}
|
|
1433
1363
|
cache = new LruCache(MAX_CACHED_DISCOVERIES);
|
|
1434
1364
|
/**
|
|
@@ -1441,23 +1371,25 @@ var AgentDiscoveryService = class {
|
|
|
1441
1371
|
now() {
|
|
1442
1372
|
return Date.now();
|
|
1443
1373
|
}
|
|
1444
|
-
discover(cwd, scope) {
|
|
1374
|
+
async discover(cwd, scope) {
|
|
1445
1375
|
const resolvedCwd = path.resolve(cwd);
|
|
1446
|
-
const canonicalCwd = canonicalizeDiscoveryCwd(resolvedCwd);
|
|
1376
|
+
const canonicalCwd = await canonicalizeDiscoveryCwd(this.io, resolvedCwd);
|
|
1447
1377
|
const key = `${scope}${FINGERPRINT_ENTRY_SEPARATOR}${canonicalCwd}`;
|
|
1448
1378
|
const cached = this.cache.get(key);
|
|
1449
1379
|
const timestamp = this.now();
|
|
1450
|
-
|
|
1451
|
-
|
|
1380
|
+
const team = await this.agentConfig?.currentOrUndefined();
|
|
1381
|
+
if (cached && cached.team === team && cached.expiresAt > timestamp && cached.fingerprint === await this.fingerprint(canonicalCwd)) return cloneResult(cached.result);
|
|
1382
|
+
const result = await this.load(resolvedCwd, scope, team);
|
|
1452
1383
|
this.cache.set(key, {
|
|
1453
1384
|
result,
|
|
1454
1385
|
expiresAt: timestamp + this.ttlMs,
|
|
1455
|
-
fingerprint: this.fingerprint(canonicalCwd)
|
|
1386
|
+
fingerprint: await this.fingerprint(canonicalCwd),
|
|
1387
|
+
team
|
|
1456
1388
|
});
|
|
1457
1389
|
return cloneResult(result);
|
|
1458
1390
|
}
|
|
1459
|
-
find(cwd, scope, name) {
|
|
1460
|
-
return this.discover(cwd, scope).agents.find((agent) => agent.name === name);
|
|
1391
|
+
async find(cwd, scope, name) {
|
|
1392
|
+
return (await this.discover(cwd, scope)).agents.find((agent) => agent.name === name);
|
|
1461
1393
|
}
|
|
1462
1394
|
invalidate() {
|
|
1463
1395
|
this.cache.clear();
|
|
@@ -1469,43 +1401,42 @@ var AgentDiscoveryService = class {
|
|
|
1469
1401
|
* from the same directory read overlapping sources, and computing one stamp
|
|
1470
1402
|
* keeps the stat count fixed regardless of how many scopes are queried.
|
|
1471
1403
|
*/
|
|
1472
|
-
fingerprint(cwd) {
|
|
1473
|
-
const harness = loadHarnessState(this.environment).state;
|
|
1404
|
+
async fingerprint(cwd) {
|
|
1474
1405
|
const sources = [
|
|
1475
|
-
getUserAgentSettingsPath(this.
|
|
1476
|
-
getProjectAgentSettingsPath(
|
|
1477
|
-
...userAgentDirs(this.
|
|
1478
|
-
...resolveNearestProjectAgentDirs(
|
|
1479
|
-
...pluginAgentDirs(this.environment)
|
|
1406
|
+
getUserAgentSettingsPath(this.io),
|
|
1407
|
+
await getProjectAgentSettingsPath(this.io, cwd) ?? MISSING_SOURCE_MARKER,
|
|
1408
|
+
...userAgentDirs(this.io),
|
|
1409
|
+
...(await resolveNearestProjectAgentDirs(this.io, cwd)).readDirs,
|
|
1410
|
+
...pluginAgentDirs(this.io.environment)
|
|
1480
1411
|
];
|
|
1481
|
-
|
|
1482
|
-
return [...sources.map(stampOf), `active-layers${FINGERPRINT_FIELD_SEPARATOR}${harness.layers.join(",")}`].join(FINGERPRINT_ENTRY_SEPARATOR);
|
|
1412
|
+
return (await Promise.all(sources.map((target) => stampOf(this.io, target)))).join(FINGERPRINT_ENTRY_SEPARATOR);
|
|
1483
1413
|
}
|
|
1484
1414
|
/**
|
|
1485
1415
|
* The uncached read. Protected so a test can count how often it actually runs,
|
|
1486
1416
|
* which is the only direct way to assert that the cache is doing its job.
|
|
1487
1417
|
*/
|
|
1488
|
-
load(cwd, scope) {
|
|
1489
|
-
const userSettingsPath = getUserAgentSettingsPath(this.
|
|
1490
|
-
const projectSettingsPath = getProjectAgentSettingsPath(
|
|
1491
|
-
const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
|
|
1492
|
-
const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
|
|
1418
|
+
async load(cwd, scope, team) {
|
|
1419
|
+
const userSettingsPath = getUserAgentSettingsPath(this.io);
|
|
1420
|
+
const projectSettingsPath = await getProjectAgentSettingsPath(this.io, cwd);
|
|
1421
|
+
const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : await readSubagentSettings(this.io, userSettingsPath);
|
|
1422
|
+
const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : await readSubagentSettings(this.io, projectSettingsPath);
|
|
1493
1423
|
const legacyDefaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
1494
|
-
const
|
|
1495
|
-
const
|
|
1496
|
-
const defaultModel = packagePrimaryModel && teamPackage ? {
|
|
1424
|
+
const packagePrimaryModel = team?.models?.[0];
|
|
1425
|
+
const defaultModel = packagePrimaryModel && team ? {
|
|
1497
1426
|
type: "packages.team.defaultModel",
|
|
1498
1427
|
scope: "package",
|
|
1499
|
-
path:
|
|
1428
|
+
path: team.path ?? "",
|
|
1500
1429
|
model: packagePrimaryModel
|
|
1501
1430
|
} : legacyDefaultModel;
|
|
1502
1431
|
const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
|
|
1503
1432
|
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
|
|
1504
1433
|
const resolve = (agents) => applyCustomAgentOverrides(applySubagentDefaults(agents, defaultModel, defaultThinking, defaultExtensions), userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
1505
|
-
const
|
|
1434
|
+
const projectDirs = await resolveNearestProjectAgentDirs(this.io, cwd);
|
|
1435
|
+
const loadDirs = async (dirs, source) => resolve((await Promise.all(dirs.map((dir) => loadAgentsFromDir(this.io, dir, source)))).flat());
|
|
1436
|
+
const plugin = await loadDirs(pluginAgentDirs(this.io.environment), "plugin");
|
|
1506
1437
|
return {
|
|
1507
|
-
agents: mergeAgentsForScope(scope, scope === "project" ? [] :
|
|
1508
|
-
projectAgentsDir:
|
|
1438
|
+
agents: mergeAgentsForScope(scope, scope === "project" ? [] : await loadDirs(userAgentDirs(this.io), "user"), scope === "user" ? [] : await loadDirs(projectDirs.readDirs, "project"), plugin).filter((agent) => agent.disabled !== true),
|
|
1439
|
+
projectAgentsDir: projectDirs.preferredDir,
|
|
1509
1440
|
modelScope: projectSettings.modelScope ?? userSettings.modelScope
|
|
1510
1441
|
};
|
|
1511
1442
|
}
|
|
@@ -1524,6 +1455,6 @@ function cloneResult(result) {
|
|
|
1524
1455
|
};
|
|
1525
1456
|
}
|
|
1526
1457
|
//#endregion
|
|
1527
|
-
export {
|
|
1458
|
+
export { getAgentDir as a, canonicalizeDiscoveryCwd$1 as i, LruCache as n, getConfigDirName as o, agentHasWriteTools as r, getProjectConfigDir as s, AgentDiscoveryService as t };
|
|
1528
1459
|
|
|
1529
|
-
//# sourceMappingURL=agentDiscovery-
|
|
1460
|
+
//# sourceMappingURL=agentDiscovery-ZNG8iZXV.mjs.map
|