@ai-outfitter/outfitter 1.3.1 → 1.5.0

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.
@@ -4,6 +4,23 @@ export interface AgentProcessLauncher {
4
4
  }
5
5
  export declare const launchAgentProcess: (launcher: AgentProcessLauncher, launchPlan: AgentLaunchPlan, agentId: string) => Promise<number>;
6
6
  export declare const resolveAgentLaunchExecutable: (launchPlan: AgentLaunchPlan) => AgentLaunchPlan;
7
+ /**
8
+ * Forwards termination signals from this process to a spawned harness, resolving once the harness
9
+ * actually exits.
10
+ *
11
+ * Without this, a resident agent cannot shut down. Node installs no default forwarding, so the
12
+ * harness never learns the session is ending: under Kubernetes it is SIGKILLed when the grace period
13
+ * expires, skipping credential persistence and projection cleanup. The same gap shows up outside
14
+ * containers — Ctrl-C in a terminal, or a cancelled CI job — which is why this belongs here rather
15
+ * than in a container init.
16
+ *
17
+ * Installing a handler suppresses Node's default termination, so every path must resolve, and the
18
+ * listeners must come off afterwards or repeated launches in one process leak them.
19
+ */
20
+ export declare const attachSignalForwarding: (child: {
21
+ kill(signal?: NodeJS.Signals): boolean;
22
+ killed: boolean;
23
+ }, emitter?: NodeJS.EventEmitter, graceMs?: number) => (() => void);
7
24
  export declare const spawnLauncher: AgentProcessLauncher;
8
25
  /**
9
26
  * Launches a resolved plan through the given spawn boundary. The install-hint agentId is derived
@@ -1,6 +1,7 @@
1
1
  // Turns a logical agent launch plan into an actual launched process: resolves the bundled pi
2
2
  // binary, runs the launcher, and translates a missing agent CLI into actionable install guidance.
3
3
  import { existsSync, readFileSync } from 'node:fs';
4
+ import os from 'node:os';
4
5
  import { dirname, join } from 'node:path';
5
6
  import { fileURLToPath } from 'node:url';
6
7
  export const launchAgentProcess = async (launcher, launchPlan, agentId) => {
@@ -39,14 +40,64 @@ export const resolveAgentLaunchExecutable = (launchPlan) => {
39
40
  env: { PI_SKIP_VERSION_CHECK: '1', ...launchPlan.env },
40
41
  };
41
42
  };
43
+ // Signals we forward to the harness. SIGKILL is deliberately absent: it cannot be caught, and the
44
+ // kernel delivers it to us directly.
45
+ const FORWARDED_SIGNALS = ['SIGTERM', 'SIGINT', 'SIGHUP'];
46
+ // How long the harness gets to exit after a forwarded signal before we stop being polite. Kubernetes
47
+ // defaults to a 30s grace period and SIGKILLs the pod afterwards, so this has to be comfortably
48
+ // shorter or the escalation never runs.
49
+ const TERMINATION_GRACE_MS = 10_000;
50
+ /**
51
+ * Forwards termination signals from this process to a spawned harness, resolving once the harness
52
+ * actually exits.
53
+ *
54
+ * Without this, a resident agent cannot shut down. Node installs no default forwarding, so the
55
+ * harness never learns the session is ending: under Kubernetes it is SIGKILLed when the grace period
56
+ * expires, skipping credential persistence and projection cleanup. The same gap shows up outside
57
+ * containers — Ctrl-C in a terminal, or a cancelled CI job — which is why this belongs here rather
58
+ * than in a container init.
59
+ *
60
+ * Installing a handler suppresses Node's default termination, so every path must resolve, and the
61
+ * listeners must come off afterwards or repeated launches in one process leak them.
62
+ */
63
+ export const attachSignalForwarding = (child, emitter = process, graceMs = TERMINATION_GRACE_MS) => {
64
+ let escalation;
65
+ const forward = (signal) => () => {
66
+ if (child.killed)
67
+ return;
68
+ child.kill(signal);
69
+ // A harness that ignores or hangs on the signal would otherwise keep us alive until the
70
+ // orchestrator's own SIGKILL, losing the chance to exit cleanly first.
71
+ escalation ??= setTimeout(() => child.kill('SIGKILL'), graceMs);
72
+ escalation.unref?.();
73
+ };
74
+ const handlers = FORWARDED_SIGNALS.map((signal) => [signal, forward(signal)]);
75
+ for (const [signal, handler] of handlers)
76
+ emitter.on(signal, handler);
77
+ return () => {
78
+ for (const [signal, handler] of handlers)
79
+ emitter.removeListener(signal, handler);
80
+ if (escalation)
81
+ clearTimeout(escalation);
82
+ };
83
+ };
42
84
  /* v8 ignore start -- real process spawn is covered by end-to-end smoke usage, not unit tests. */
43
85
  export const spawnLauncher = {
44
86
  async launch(plan) {
45
87
  const { default: spawn } = await import('cross-spawn');
46
88
  return await new Promise((resolve, reject) => {
47
89
  const child = spawn(plan.command, [...plan.args], { stdio: 'inherit', env: { ...process.env, ...plan.env } });
48
- child.on('error', reject); // ENOENT surfaces as an actionable install message
49
- child.on('close', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
90
+ const detach = attachSignalForwarding(child);
91
+ child.on('error', (error) => {
92
+ detach();
93
+ reject(error); // ENOENT surfaces as an actionable install message
94
+ });
95
+ child.on('close', (code, signal) => {
96
+ detach();
97
+ // 128+n is the shell convention for "died on signal n", and it is what a caller inspecting
98
+ // our exit status expects to see when the harness was terminated rather than returning.
99
+ resolve(code ?? (signal ? 128 + (os.constants.signals[signal] ?? 0) : 0));
100
+ });
50
101
  });
51
102
  },
52
103
  };
@@ -1 +1 @@
1
- {"version":3,"file":"AgentLaunch.js","sourceRoot":"","sources":["../../src/agents/AgentLaunch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentLaunch.js","sourceRoot":"","sources":["../../src/agents/AgentLaunch.ts"],"names":[],"mappings":"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"}
@@ -18,13 +18,23 @@ export interface EnsurePiExtensionsResult {
18
18
  interface PiExtensionSource {
19
19
  readonly source: string;
20
20
  readonly installSegments: readonly string[];
21
- /** Exact semver to verify against a cached install; undefined for ranges/tags/git refs. */
21
+ /** Exact semver to verify against a cached npm install; undefined for ranges and tags. */
22
22
  readonly pinnedVersion?: string;
23
+ /** The `@ref` of a git specifier, to verify against a cached checkout; undefined when unpinned. */
24
+ readonly pinnedGitRef?: string;
23
25
  }
24
26
  /** Translates an Outfitter extension specifier to a pi `install` source + its cache install path. */
25
27
  export declare const mapSpecifierToPiSource: (specifier: string) => PiExtensionSource | {
26
28
  readonly unsupported: string;
27
29
  };
30
+ /**
31
+ * Defense in depth behind segment validation: refuses an install dir that resolves outside the
32
+ * cache root before any filesystem access (the stale-git path runs `rm -rf` on the install dir).
33
+ * Strict containment also keeps the `<installDir>.outfitter-ref.json` marker sibling inside the
34
+ * cache, since a contained install dir is at least one level below the root. Exported for tests;
35
+ * segment validation in mapSpecifierToPiSource should make this unreachable.
36
+ */
37
+ export declare const assertInstallDirInsideCache: (installDir: string, cacheAgentDir: string, specifier: string) => void;
28
38
  /** Ensures each pi extension is cached (installing when online) and returns its load directory. */
29
39
  export declare const ensurePiExtensions: (specifiers: readonly string[], input: EnsurePiExtensionsInput) => Promise<EnsurePiExtensionsResult>;
30
40
  export {};
@@ -5,10 +5,43 @@
5
5
  // npm: <cacheAgentDir>/npm/node_modules/<pkg>
6
6
  // Outfitter's `git:`/`npm:` specifiers are already pi's `install` source grammar, so the source is
7
7
  // passed through unchanged; only the install directory is reconstructed to check the cache.
8
- import { existsSync, mkdirSync, readFileSync } from 'node:fs';
9
- import { join } from 'node:path';
8
+ //
9
+ // A cached git checkout is only trusted when it still matches the specifier's `@ref` pin:
10
+ // - full-SHA ref: `git -C <installDir> rev-parse HEAD` is compared against the pin (offline-safe,
11
+ // no network); a mismatch or an unreadable HEAD reinstalls when online.
12
+ // - branch/tag ref: the ref string is recorded in a `<installDir>.outfitter-ref.json` marker at
13
+ // install time and only a *changed* ref string triggers a reinstall — a moved remote branch tip
14
+ // is deliberately not chased, so pinned launches never hit the network.
15
+ // - branch/tag ref with no marker (a pre-marker cache): served as-is when offline (no evidence it
16
+ // is wrong), refreshed once when online, which writes the marker.
17
+ // Offline, a checkout that provably mismatches its pin is dropped with a warning — the same
18
+ // severity the offline path already applies to a missing extension (fatal only under `--strict`).
19
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
20
+ import { join, resolve, sep } from 'node:path';
10
21
  import { launchThroughSpawn, spawnLauncher } from '../agents/AgentLaunch.js';
22
+ import { runGit } from '../sources/GitRepository.js';
11
23
  const exactSemverPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
24
+ const fullShaPattern = /^[0-9a-f]{40}$/iu;
25
+ // Specifier text becomes filesystem path segments (and the stale-reinstall path runs `rm -rf` on
26
+ // the joined result), so a segment that traverses (`..`, `.`) or smuggles a separator (`\`, which
27
+ // `join` does not split but Windows resolves) must be rejected before any path is built.
28
+ const unsafePathSegment = (segment) => segment === '.' || segment === '..' || segment.includes('\\');
29
+ const mapGitSpecifier = (specifier) => {
30
+ const rest = specifier.slice('git:'.length);
31
+ const pathPart = rest.includes('@') ? rest.slice(0, rest.lastIndexOf('@')) : rest;
32
+ const ref = rest.includes('@') ? rest.slice(rest.lastIndexOf('@') + 1) : '';
33
+ const segments = pathPart.split('/').filter((part) => part !== '');
34
+ if (segments.length < 2)
35
+ return { unsupported: `extension '${specifier}' is not a valid git source` };
36
+ if (segments.some(unsafePathSegment)) {
37
+ return { unsupported: `extension '${specifier}' contains an unsafe path segment` };
38
+ }
39
+ return {
40
+ source: specifier,
41
+ installSegments: ['git', ...segments],
42
+ pinnedGitRef: ref === '' ? undefined : ref,
43
+ };
44
+ };
12
45
  /** Translates an Outfitter extension specifier to a pi `install` source + its cache install path. */
13
46
  export const mapSpecifierToPiSource = (specifier) => {
14
47
  if (specifier.startsWith('npm:')) {
@@ -16,6 +49,9 @@ export const mapSpecifierToPiSource = (specifier) => {
16
49
  const name = rest.replace(/@[^@/]+$/u, ''); // strip a trailing @version, keep a scope's leading @
17
50
  if (name === '')
18
51
  return { unsupported: `extension '${specifier}' has no package name` };
52
+ if (name.split('/').some(unsafePathSegment)) {
53
+ return { unsupported: `extension '${specifier}' contains an unsafe path segment` };
54
+ }
19
55
  const version = rest.length > name.length ? rest.slice(name.length + 1) : undefined;
20
56
  return {
21
57
  source: specifier,
@@ -23,14 +59,8 @@ export const mapSpecifierToPiSource = (specifier) => {
23
59
  pinnedVersion: version !== undefined && exactSemverPattern.test(version) ? version : undefined,
24
60
  };
25
61
  }
26
- if (specifier.startsWith('git:')) {
27
- const rest = specifier.slice('git:'.length);
28
- const pathPart = rest.includes('@') ? rest.slice(0, rest.lastIndexOf('@')) : rest;
29
- const segments = pathPart.split('/').filter((part) => part !== '');
30
- if (segments.length < 2)
31
- return { unsupported: `extension '${specifier}' is not a valid git source` };
32
- return { source: specifier, installSegments: ['git', ...segments] };
33
- }
62
+ if (specifier.startsWith('git:'))
63
+ return mapGitSpecifier(specifier);
34
64
  return { unsupported: `extension '${specifier}' uses an unsupported source (only git: and npm: project to pi)` };
35
65
  };
36
66
  const installedVersion = (installDir) => {
@@ -42,6 +72,88 @@ const installedVersion = (installDir) => {
42
72
  return undefined;
43
73
  }
44
74
  };
75
+ /**
76
+ * Defense in depth behind segment validation: refuses an install dir that resolves outside the
77
+ * cache root before any filesystem access (the stale-git path runs `rm -rf` on the install dir).
78
+ * Strict containment also keeps the `<installDir>.outfitter-ref.json` marker sibling inside the
79
+ * cache, since a contained install dir is at least one level below the root. Exported for tests;
80
+ * segment validation in mapSpecifierToPiSource should make this unreachable.
81
+ */
82
+ export const assertInstallDirInsideCache = (installDir, cacheAgentDir, specifier) => {
83
+ const root = resolve(cacheAgentDir);
84
+ if (!resolve(installDir).startsWith(root + sep)) {
85
+ throw new Error(`extension '${specifier}' resolves outside the extension cache; refusing to touch it.`);
86
+ }
87
+ };
88
+ /** Marker recording which git ref an install satisfied, kept beside (never inside) the checkout. */
89
+ const refMarkerPath = (installDir) => `${installDir}.outfitter-ref.json`;
90
+ const cachedGitHead = (installDir) => {
91
+ try {
92
+ return runGit(['-C', installDir, 'rev-parse', 'HEAD']);
93
+ }
94
+ catch {
95
+ return undefined;
96
+ }
97
+ };
98
+ const recordedInstallRef = (installDir) => {
99
+ try {
100
+ const marker = JSON.parse(readFileSync(refMarkerPath(installDir), 'utf8'));
101
+ return marker.ref;
102
+ }
103
+ catch {
104
+ return undefined;
105
+ }
106
+ };
107
+ /** After installing a branch/tag-pinned git extension, records the ref (and resolved SHA). */
108
+ const recordInstalledGitRef = (installDir, mapped) => {
109
+ if (mapped.pinnedGitRef === undefined || fullShaPattern.test(mapped.pinnedGitRef))
110
+ return;
111
+ const marker = { ref: mapped.pinnedGitRef, headSha: cachedGitHead(installDir) };
112
+ writeFileSync(refMarkerPath(installDir), JSON.stringify(marker));
113
+ };
114
+ // Remove-then-install for a stale git pin: `pi install` owns the directory layout, so
115
+ // GitRepository's atomic fetch-and-swap cannot apply here. A failure between the remove and the
116
+ // install leaves neither a directory nor a marker, which the next run detects as plain missing.
117
+ // The npm path keeps its original behavior: `pi install` is spawned over the existing dir.
118
+ const removeStaleGitInstall = (installDir, mapped) => {
119
+ if (mapped.pinnedGitRef === undefined)
120
+ return;
121
+ rmSync(installDir, { recursive: true, force: true });
122
+ rmSync(refMarkerPath(installDir), { force: true });
123
+ };
124
+ /** Compares a cached checkout against its `@ref` pin per the policy in the file header. */
125
+ const gitCacheStatus = (installDir, ref) => {
126
+ if (fullShaPattern.test(ref)) {
127
+ const head = cachedGitHead(installDir);
128
+ return {
129
+ state: head?.toLowerCase() === ref.toLowerCase() ? 'fresh' : 'stale',
130
+ found: head ?? 'no readable git HEAD',
131
+ };
132
+ }
133
+ const recorded = recordedInstallRef(installDir);
134
+ if (recorded === undefined)
135
+ return { state: 'unverified', found: 'no recorded install ref' };
136
+ return { state: recorded === ref ? 'fresh' : 'stale', found: recorded };
137
+ };
138
+ /** Decides whether an existing install satisfies the specifier's pin (git ref or exact semver). */
139
+ const evaluateCachedInstall = (installDir, mapped, offline) => {
140
+ if (!existsSync(installDir))
141
+ return { serve: false };
142
+ if (mapped.pinnedGitRef === undefined) {
143
+ return { serve: mapped.pinnedVersion === undefined || installedVersion(installDir) === mapped.pinnedVersion };
144
+ }
145
+ const status = gitCacheStatus(installDir, mapped.pinnedGitRef);
146
+ if (status.state === 'fresh' || (status.state === 'unverified' && offline))
147
+ return { serve: true };
148
+ if (offline) {
149
+ return {
150
+ serve: false,
151
+ staleWarning: `extension '${mapped.source}' is cached at the wrong revision ` +
152
+ `(pinned ${mapped.pinnedGitRef}, found ${status.found}) and cannot be reinstalled offline.`,
153
+ };
154
+ }
155
+ return { serve: false };
156
+ };
45
157
  /* v8 ignore start -- real `pi install` subprocess; ensurePiExtensions is unit-tested with a fake spawner. */
46
158
  const defaultSpawner = ({ source, cacheAgentDir }) => launchThroughSpawn(spawnLauncher, {
47
159
  command: 'pi',
@@ -54,12 +166,15 @@ const ensureOneExtension = async (specifier, input, spawn) => {
54
166
  if ('unsupported' in mapped)
55
167
  return { warning: mapped.unsupported };
56
168
  const installDir = join(input.cacheAgentDir, ...mapped.installSegments);
57
- const cached = existsSync(installDir) &&
58
- (mapped.pinnedVersion === undefined || installedVersion(installDir) === mapped.pinnedVersion);
59
- if (cached)
169
+ assertInstallDirInsideCache(installDir, input.cacheAgentDir, specifier);
170
+ const decision = evaluateCachedInstall(installDir, mapped, input.offline);
171
+ if (decision.serve)
60
172
  return { loadDir: installDir };
173
+ if (decision.staleWarning !== undefined)
174
+ return { warning: decision.staleWarning };
61
175
  if (input.offline)
62
176
  return { warning: `extension '${specifier}' is not cached and cannot be installed offline.` };
177
+ removeStaleGitInstall(installDir, mapped);
63
178
  mkdirSync(input.cacheAgentDir, { recursive: true });
64
179
  try {
65
180
  const exitCode = await spawn({ source: mapped.source, cacheAgentDir: input.cacheAgentDir });
@@ -70,6 +185,7 @@ const ensureOneExtension = async (specifier, input, spawn) => {
70
185
  catch (error) {
71
186
  return { warning: `extension '${specifier}' failed to install (${String(error)}).` };
72
187
  }
188
+ recordInstalledGitRef(installDir, mapped);
73
189
  return { loadDir: installDir };
74
190
  };
75
191
  /** Ensures each pi extension is cached (installing when online) and returns its load directory. */
@@ -1 +1 @@
1
- {"version":3,"file":"PiExtensionCache.js","sourceRoot":"","sources":["../../src/extensions/PiExtensionCache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PiExtensionCache.js","sourceRoot":"","sources":["../../src/extensions/PiExtensionCache.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import { dirname, join } from 'node:path';
4
4
  import { escapesRoots } from '../dump/Containment.js';
5
5
  import { removeTargetTypeConflict } from '../fs/TypeConflict.js';
6
6
  import { isAgentDefinitionIssue, readAgentDefinition } from '../resolver/AgentDefinition.js';
7
+ import { effectiveToolAllowlist } from './Tools.js';
7
8
  /** Recursively copies a directory, skipping symlinked entries so no path escapes the tree. */
8
9
  const copyDirectory = (sourceDir, targetDir) => {
9
10
  removeTargetTypeConflict(targetDir, 'directory');
@@ -73,8 +74,7 @@ const serializeSubagent = (subagent) => {
73
74
  const definition = readValidSubagentDefinition(subagent);
74
75
  if (definition === undefined)
75
76
  return undefined;
76
- const denied = new Set(definition.loadout.tools?.deny ?? []);
77
- const tools = definition.loadout.tools?.allow?.filter((tool) => !denied.has(tool));
77
+ const tools = effectiveToolAllowlist(definition.loadout.tools);
78
78
  const frontmatter = [
79
79
  `name: ${JSON.stringify(subagent.slug)}`,
80
80
  `description: ${JSON.stringify(definition.description ?? definition.label ?? `Delegated ${subagent.slug} agent.`)}`,
@@ -95,8 +95,7 @@ const composedSubagentBody = (subagent) => [
95
95
  .filter((fragment) => fragment !== undefined && fragment.length > 0)
96
96
  .join('\n\n');
97
97
  const serializeComposedSubagent = (subagent) => {
98
- const denied = new Set(subagent.tools?.deny ?? []);
99
- const tools = subagent.tools?.allow?.filter((tool) => !denied.has(tool));
98
+ const tools = effectiveToolAllowlist(subagent.tools);
100
99
  const description = subagent.identity.description ?? subagent.identity.label;
101
100
  const frontmatter = [
102
101
  `name: ${JSON.stringify(subagent.resource.slug)}`,
@@ -1 +1 @@
1
- {"version":3,"file":"Materialize.js","sourceRoot":"","sources":["../../src/projection/Materialize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Materialize.js","sourceRoot":"","sources":["../../src/projection/Materialize.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAgBpD,8FAA8F;AAC9F,MAAM,aAAa,GAAG,CAAC,SAAiB,EAAE,SAAiB,EAAQ,EAAE;IACnE,wBAAwB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACjD,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC;YAC3C,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/C,wBAAwB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC7C,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAE,OAAe,EAAQ,EAAE;IACjE,wBAAwB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;AAEpF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,iBAAoC,EAAE,aAAqB,EAAQ,EAAE;IACpH,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9C,KAAK,MAAM,eAAe,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/D,IAAI,SAAS,CAAC,eAAe,CAAC,CAAC,cAAc,EAAE;YAAE,SAAS;QAC1D,aAAa,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;IAChD,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,KAAuB,EAAE,aAAqB,EAAsB,EAAE;IAC9F,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAE7C,oFAAoF;IACpF,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACpC,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,KAAyB,EAAqB,EAAE,CACpF,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAEnE,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,MAAyB,EAAqB,EAAE,CAClF,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;AAE/E,MAAM,oBAAoB,GAAG,CAAC,QAA0B,EAAW,EAAE;IACnE,MAAM,KAAK,GAAG;QACZ,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI;QAC1B,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;QAC/D,GAAG,CAAC,QAAQ,CAAC,gBAAgB,IAAI,EAAE,CAAC;KACrC,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;IACtE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AACzD,CAAC,CAAC;AAEF,MAAM,2BAA2B,GAAG,CAAC,QAA0B,EAA+B,EAAE;IAC9F,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,UAAU,GAAG,mBAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAEnF,OAAO,sBAAsB,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;AAC1G,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,QAA0B,EAAsB,EAAE;IAC3E,MAAM,UAAU,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IAEzD,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE/C,MAAM,KAAK,GAAG,sBAAsB,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG;QAClB,SAAS,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACxC,gBAAgB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,IAAI,UAAU,CAAC,KAAK,IAAI,aAAa,QAAQ,CAAC,IAAI,SAAS,CAAC,EAAE;QACnH,GAAG,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC;QACpD,GAAG,cAAc,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC1D,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC;QACrC,GAAG,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC;QACpD,GAAG,YAAY,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC;KAC7D,CAAC;IAEF,OAAO,QAAQ,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,UAAU,CAAC,IAAI,EAAE,CAAC;AACrE,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAAC,QAA0B,EAAU,EAAE,CAClE;IACE,QAAQ,CAAC,QAAQ,CAAC,YAAY;IAC9B,QAAQ,CAAC,QAAQ,CAAC,aAAa;IAC/B,GAAG,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;IAC5E,GAAG,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;CACrE;KACE,MAAM,CAAC,CAAC,QAAQ,EAAsB,EAAE,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;KACvF,IAAI,CAAC,MAAM,CAAC,CAAC;AAElB,MAAM,yBAAyB,GAAG,CAAC,QAA0B,EAAU,EAAE;IACvE,MAAM,KAAK,GAAG,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC7E,MAAM,WAAW,GAAG;QAClB,SAAS,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACjD,gBAAgB,IAAI,CAAC,SAAS,CAAC,WAAW,IAAI,aAAa,QAAQ,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,EAAE;QAC7F,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC;QAC1C,GAAG,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC;QAChD,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC;QACrC,GAAG,YAAY,CACb,QAAQ,EACR,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC3C;QACD,GAAG,YAAY,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC;KACnD,CAAC;IAEF,OAAO,QAAQ,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;AACpF,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAC3B,SAAsC,EACtC,iBAA0D,EAC1D,aAAqB,EACF,EAAE;IACrB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACtD,MAAM,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,SAAS,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,cAAc,GAAG,IAAI,GAAG,CAC5B,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAChF,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC;QAC3G,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,kBAAkB,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAC1B,WAA4B,EAC5B,aAAqB,EACiE,EAAE;IACxF,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;IACjE,kBAAkB,CAAC,gBAAgB,EAAE,WAAW,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAC9E,MAAM,iBAAiB,GAAa,EAAE,CAAC;IAEvC,IAAI,WAAW,CAAC,QAAQ,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACrD,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;QACrD,kBAAkB,CAAC,WAAW,EAAE,WAAW,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACpE,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;IACD,CAAC,WAAW,CAAC,QAAQ,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC3E,MAAM,IAAI,GAAG,UAAU,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;QAC3F,kBAAkB,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChE,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;IACtD,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QAC7C,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC/E,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;YACpF,kBAAkB,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;YAChE,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACtD,kBAAkB,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAC;AACjD,CAAC,CAAC;AAEF,mGAAmG;AACnG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,WAA4B,EAC5B,aAAqB,EACI,EAAE;IAC3B,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,GAAG,mBAAmB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAChG,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC3F,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAC5D,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,MAAM,gBAAgB,GAAG,oBAAoB,CAC3C,WAAW,CAAC,OAAO,CAAC,SAAS,EAC7B,WAAW,CAAC,OAAO,CAAC,iBAAiB,EACrC,aAAa,CACd,CAAC;IAEF,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,kBAAkB,CAChB,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,EAC/B,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAC/E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,aAAa;QACb,gBAAgB;QAChB,iBAAiB;QACjB,gBAAgB;QAChB,aAAa;QACb,gBAAgB;KACjB,CAAC;AACJ,CAAC,CAAC"}
@@ -3,14 +3,16 @@ import { readFileSync, writeFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { PI_SESSION_DIRECTORY_ENV } from '../agents/PiSessionDirectory.js';
5
5
  import { materializeComposition, materializeConfigurationOverlays } from './Materialize.js';
6
+ import { toolArgs } from './Tools.js';
6
7
  // Loadout elements a projection actually maps to native config. Anything else is reported
7
8
  // unsupported so `--strict` catches silently-dropped selections. Baseline for both harnesses is
8
- // identity + skills + model + thinking. Pi also projects selected subagents and MCP servers into
9
- // its runtime config directory, and projects `extensions` once the run path has resolved their
10
- // install dirs (`extensionLoadDirs`). plugins/tools remain unsupported pending incremental parity
11
- // (#183).
9
+ // identity + skills + model + thinking + tools. Both harnesses express an allowlist and a denylist
10
+ // natively, so `tools` is unconditionally supported; a name projection cannot carry is a hard error
11
+ // from `toolArgs`, not an unsupported element. Pi also projects selected subagents and MCP servers
12
+ // into its runtime config directory, and projects `extensions` once the run path has resolved their
13
+ // install dirs (`extensionLoadDirs`). plugins remain unsupported pending incremental parity (#183).
12
14
  const supportedElements = (input) => {
13
- const baseline = ['skills', 'model', 'thinking'];
15
+ const baseline = ['skills', 'model', 'thinking', 'tools'];
14
16
  if (input.harness !== 'pi')
15
17
  return baseline;
16
18
  const pi = [...baseline, 'subagents', 'mcp', 'prompt_template'];
@@ -96,6 +98,10 @@ const buildLaunchPlan = (composition, input, systemPromptPath, appendPromptPaths
96
98
  return {
97
99
  command: isPi ? 'pi' : 'claude',
98
100
  args: [
101
+ // Tool args lead the list on purpose: Claude's `--allowedTools`/`--disallowedTools` are
102
+ // variadic and stop only at the next `--flag`, and the prompt args that follow always start
103
+ // with one. Placed later they could swallow a pass-through positional. See Tools.ts.
104
+ ...(composition.loadout.tools === undefined ? [] : toolArgs(input.harness, composition.loadout.tools)),
99
105
  ...promptArgs(composition, input, systemPromptPath, appendPromptPaths),
100
106
  ...skillArgs,
101
107
  ...extensionArgs,
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectHarness.js","sourceRoot":"","sources":["../../src/projection/ProjectHarness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProjectHarness.js","sourceRoot":"","sources":["../../src/projection/ProjectHarness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import type { Loadout } from '../resolver/Resource.js';
2
+ import type { Harness } from '../settings/Settings.js';
3
+ /** The `{allow?, deny?}` tool selection carried by a loadout. */
4
+ export type ToolSelection = NonNullable<Loadout['tools']>;
5
+ /**
6
+ * A projectable tool name. Kept in lockstep with `$defs/toolName` in `agent.schema.json`, which
7
+ * rejects the same shapes at the YAML read boundary; a unit test asserts the two stay equal.
8
+ *
9
+ * Three characters are excluded, because projection puts these names into harness argv:
10
+ *
11
+ * - A leading `-` makes the name an independent flag on Claude, where each entry becomes its own
12
+ * argv element. `allow: [Read, --dangerously-skip-permissions]` would not name a tool. It would
13
+ * turn off the permission system this element exists to configure. Profiles compose from remote
14
+ * catalogs and inheritance chains, so a tool name is not always written by the person who runs
15
+ * the agent, which makes this a supply-chain path to arbitrary launch flags.
16
+ * - A comma splits one name into several entries inside pi's `--tools a,b,c` value.
17
+ * - Whitespace is not a separator on either path today, because no shell is involved and pi gets a
18
+ * single argv element. It is rejected as conservatism, and it keeps names portable between the
19
+ * two harnesses. Note that this also excludes Claude's scoped permission rules, such as
20
+ * `Bash(npm run test:*)`. That is deliberate for a harness-neutral field: the same string is
21
+ * meaningless to pi. Relaxing the pattern later is backward compatible.
22
+ */
23
+ export declare const TOOL_NAME_PATTERN: RegExp;
24
+ /** The rule the pattern encodes, phrased for an error that a profile author reads. */
25
+ export declare const TOOL_NAME_RULE = "a tool name must not start with '-' or contain a comma or whitespace, because projection would make it a separate harness flag or split it into several names.";
26
+ /**
27
+ * The first tool name in the selection that projection cannot carry, or `undefined` when every name
28
+ * is projectable. Shared with the agent read boundary, so `config.json` overlays, which bypass the
29
+ * JSON Schema, are held to the same invariant.
30
+ */
31
+ export declare const invalidToolName: (tools: Loadout["tools"]) => string | undefined;
32
+ /**
33
+ * The effective tool allowlist: `allow` minus `deny`, per OFTR-003.10.4 ("denied tools MUST win
34
+ * when projected"). `undefined` and `[]` mean different things and callers must keep them apart:
35
+ * `undefined` is "no allowlist declared", `[]` is "an allowlist that `deny` emptied".
36
+ */
37
+ export declare const effectiveToolAllowlist: (tools: Loadout["tools"]) => readonly string[] | undefined;
38
+ /**
39
+ * pi and Claude Code BOTH have an availability control; Claude additionally has a permission layer
40
+ * on top of its own. The pi rows were measured against pi 0.83.0 with a probe extension that
41
+ * registers a uniquely-named tool and prints `pi.getActiveTools()` at `session_start`; the exact
42
+ * runs are recorded in the PR for #255. The Claude rows come from `claude --help` and
43
+ * https://code.claude.com/docs/en/cli-reference — documented behaviour, not locally measured.
44
+ *
45
+ * | | pi | claude |
46
+ * | availability ceiling | `--no-tools --tools a,b,c` (comma-joined) | `--tools a b c` (variadic; built-in set only) |
47
+ * | pre-approval within it | n/a — pi has no permission layer | `--allowedTools a b c` (variadic) |
48
+ * | denylist | `--exclude-tools a,b,c` (comma-joined) | `--disallowedTools a b c` (variadic; a bare name removes the tool from context, per docs) |
49
+ * | no tools at all | `--no-tools` | `--tools ""` (empties the built-in set only) |
50
+ *
51
+ * An allowlist therefore projects to TWO flags on Claude: `--tools` is the ceiling (the tool is not
52
+ * in the session), and `--allowedTools` pre-approves the same names within that ceiling so a
53
+ * headless session is not stopped by a permission prompt for the very tools the profile granted.
54
+ * Either flag alone would be wrong: `--allowedTools` alone leaves every unlisted builtin available
55
+ * (merely unapproved), and `--tools` alone leaves the granted tools prompting for approval.
56
+ *
57
+ * Scope caveat, from the CLI reference: `--tools` governs the BUILT-IN set only. MCP tools
58
+ * (`mcp__server__*`) are untouched by it — they are governed by which servers load (the `mcp`
59
+ * loadout element) and by `--disallowedTools` patterns such as `mcp__*`. So `--tools ""` is not
60
+ * pi's zero-tool session when MCP servers are selected: the builtins are gone, the MCP tools
61
+ * remain. (`mcp__*` does pass TOOL_NAME_PATTERN, but only Claude reads `*` as a wildcard; on pi it
62
+ * would be a literal name, so a harness-neutral profile cannot lean on it.)
63
+ *
64
+ * Four rules follow from the flags themselves:
65
+ *
66
+ * - On pi, `--tools` is a hard allowlist across all three tool categories (built-in, extension, and
67
+ * custom), not an addition to the built-in set. Measured: `--tools read` alone yields exactly
68
+ * `["read"]`, with the probe extension's own tool gone. No base flag is needed to make an
69
+ * allowlist restrict. `--no-tools` is emitted with it anyway, because it costs nothing, states
70
+ * the intent in the argv, and keeps the allowlist a ceiling if a future pi made `--tools`
71
+ * additive.
72
+ * - `--no-tools` is pi's empty-session flag, NOT `--no-builtin-tools`. `--no-builtin-tools` keeps
73
+ * extension and custom tools by design: measured alone it yields `["probe_marker_tool"]`, the
74
+ * extension tool, while `--no-tools` yields `[]`. An allowlist that `deny` empties must therefore
75
+ * project to `--no-tools` on pi and `--tools ""` on Claude (help: 'Use "" to disable all tools'),
76
+ * or the "zero tools" request would fail open and leave the whole builtin set available. The
77
+ * empty value is one empty-string argv element — no shell is involved, so it survives as such.
78
+ * - `deny` is ALWAYS emitted on both harnesses, including when `allow` is also set and has already
79
+ * removed those names. On Claude a bare name in `--disallowedTools` removes the tool from context
80
+ * per the docs, which makes the explicit denial an availability statement in its own right rather
81
+ * than a bet on the approval path. On pi it is belt and braces, since a tool left out of
82
+ * `--tools` does not exist. Either way it satisfies OFTR-003.10.4 ("denied tools MUST win when
83
+ * projected") without depending on the filter alone.
84
+ * - Claude's three flags are all variadic. Each consumes every following token until the next
85
+ * `--flag`, so they are emitted in a fixed order — `--tools`, `--allowedTools`,
86
+ * `--disallowedTools` — where each present flag is terminated by the next one, and the launch
87
+ * plan places the whole group first, ahead of the prompt flags, which terminate the last. They
88
+ * must never sit immediately before pass-through positionals, which would be eaten as tool names.
89
+ * pi's flags take one comma-joined value each, so they are not exposed to this.
90
+ */
91
+ export declare const toolArgs: (harness: Harness, tools: ToolSelection) => readonly string[];
@@ -0,0 +1,129 @@
1
+ /**
2
+ * A projectable tool name. Kept in lockstep with `$defs/toolName` in `agent.schema.json`, which
3
+ * rejects the same shapes at the YAML read boundary; a unit test asserts the two stay equal.
4
+ *
5
+ * Three characters are excluded, because projection puts these names into harness argv:
6
+ *
7
+ * - A leading `-` makes the name an independent flag on Claude, where each entry becomes its own
8
+ * argv element. `allow: [Read, --dangerously-skip-permissions]` would not name a tool. It would
9
+ * turn off the permission system this element exists to configure. Profiles compose from remote
10
+ * catalogs and inheritance chains, so a tool name is not always written by the person who runs
11
+ * the agent, which makes this a supply-chain path to arbitrary launch flags.
12
+ * - A comma splits one name into several entries inside pi's `--tools a,b,c` value.
13
+ * - Whitespace is not a separator on either path today, because no shell is involved and pi gets a
14
+ * single argv element. It is rejected as conservatism, and it keeps names portable between the
15
+ * two harnesses. Note that this also excludes Claude's scoped permission rules, such as
16
+ * `Bash(npm run test:*)`. That is deliberate for a harness-neutral field: the same string is
17
+ * meaningless to pi. Relaxing the pattern later is backward compatible.
18
+ */
19
+ export const TOOL_NAME_PATTERN = /^[^\s,-][^\s,]*$/;
20
+ /**
21
+ * Rejects a tool name that projection cannot carry as a name. This throws instead of filtering,
22
+ * because dropping an entry from a security control without saying so is the failure mode this
23
+ * element must not have. A selection that cannot be projected exactly as written must not launch.
24
+ */
25
+ const assertProjectableToolNames = (tools) => {
26
+ const offending = invalidToolName(tools);
27
+ if (offending !== undefined) {
28
+ throw new Error(`tool name ${JSON.stringify(offending)} cannot be projected: ${TOOL_NAME_RULE}`);
29
+ }
30
+ };
31
+ /** The rule the pattern encodes, phrased for an error that a profile author reads. */
32
+ export const TOOL_NAME_RULE = "a tool name must not start with '-' or contain a comma or whitespace, because projection would make it a separate harness flag or split it into several names.";
33
+ /**
34
+ * The first tool name in the selection that projection cannot carry, or `undefined` when every name
35
+ * is projectable. Shared with the agent read boundary, so `config.json` overlays, which bypass the
36
+ * JSON Schema, are held to the same invariant.
37
+ */
38
+ export const invalidToolName = (tools) => [...(tools?.allow ?? []), ...(tools?.deny ?? [])].find((name) => !TOOL_NAME_PATTERN.test(name));
39
+ /**
40
+ * The effective tool allowlist: `allow` minus `deny`, per OFTR-003.10.4 ("denied tools MUST win
41
+ * when projected"). `undefined` and `[]` mean different things and callers must keep them apart:
42
+ * `undefined` is "no allowlist declared", `[]` is "an allowlist that `deny` emptied".
43
+ */
44
+ export const effectiveToolAllowlist = (tools) => {
45
+ const denied = new Set(tools?.deny ?? []);
46
+ return tools?.allow?.filter((tool) => !denied.has(tool));
47
+ };
48
+ /**
49
+ * pi and Claude Code BOTH have an availability control; Claude additionally has a permission layer
50
+ * on top of its own. The pi rows were measured against pi 0.83.0 with a probe extension that
51
+ * registers a uniquely-named tool and prints `pi.getActiveTools()` at `session_start`; the exact
52
+ * runs are recorded in the PR for #255. The Claude rows come from `claude --help` and
53
+ * https://code.claude.com/docs/en/cli-reference — documented behaviour, not locally measured.
54
+ *
55
+ * | | pi | claude |
56
+ * | availability ceiling | `--no-tools --tools a,b,c` (comma-joined) | `--tools a b c` (variadic; built-in set only) |
57
+ * | pre-approval within it | n/a — pi has no permission layer | `--allowedTools a b c` (variadic) |
58
+ * | denylist | `--exclude-tools a,b,c` (comma-joined) | `--disallowedTools a b c` (variadic; a bare name removes the tool from context, per docs) |
59
+ * | no tools at all | `--no-tools` | `--tools ""` (empties the built-in set only) |
60
+ *
61
+ * An allowlist therefore projects to TWO flags on Claude: `--tools` is the ceiling (the tool is not
62
+ * in the session), and `--allowedTools` pre-approves the same names within that ceiling so a
63
+ * headless session is not stopped by a permission prompt for the very tools the profile granted.
64
+ * Either flag alone would be wrong: `--allowedTools` alone leaves every unlisted builtin available
65
+ * (merely unapproved), and `--tools` alone leaves the granted tools prompting for approval.
66
+ *
67
+ * Scope caveat, from the CLI reference: `--tools` governs the BUILT-IN set only. MCP tools
68
+ * (`mcp__server__*`) are untouched by it — they are governed by which servers load (the `mcp`
69
+ * loadout element) and by `--disallowedTools` patterns such as `mcp__*`. So `--tools ""` is not
70
+ * pi's zero-tool session when MCP servers are selected: the builtins are gone, the MCP tools
71
+ * remain. (`mcp__*` does pass TOOL_NAME_PATTERN, but only Claude reads `*` as a wildcard; on pi it
72
+ * would be a literal name, so a harness-neutral profile cannot lean on it.)
73
+ *
74
+ * Four rules follow from the flags themselves:
75
+ *
76
+ * - On pi, `--tools` is a hard allowlist across all three tool categories (built-in, extension, and
77
+ * custom), not an addition to the built-in set. Measured: `--tools read` alone yields exactly
78
+ * `["read"]`, with the probe extension's own tool gone. No base flag is needed to make an
79
+ * allowlist restrict. `--no-tools` is emitted with it anyway, because it costs nothing, states
80
+ * the intent in the argv, and keeps the allowlist a ceiling if a future pi made `--tools`
81
+ * additive.
82
+ * - `--no-tools` is pi's empty-session flag, NOT `--no-builtin-tools`. `--no-builtin-tools` keeps
83
+ * extension and custom tools by design: measured alone it yields `["probe_marker_tool"]`, the
84
+ * extension tool, while `--no-tools` yields `[]`. An allowlist that `deny` empties must therefore
85
+ * project to `--no-tools` on pi and `--tools ""` on Claude (help: 'Use "" to disable all tools'),
86
+ * or the "zero tools" request would fail open and leave the whole builtin set available. The
87
+ * empty value is one empty-string argv element — no shell is involved, so it survives as such.
88
+ * - `deny` is ALWAYS emitted on both harnesses, including when `allow` is also set and has already
89
+ * removed those names. On Claude a bare name in `--disallowedTools` removes the tool from context
90
+ * per the docs, which makes the explicit denial an availability statement in its own right rather
91
+ * than a bet on the approval path. On pi it is belt and braces, since a tool left out of
92
+ * `--tools` does not exist. Either way it satisfies OFTR-003.10.4 ("denied tools MUST win when
93
+ * projected") without depending on the filter alone.
94
+ * - Claude's three flags are all variadic. Each consumes every following token until the next
95
+ * `--flag`, so they are emitted in a fixed order — `--tools`, `--allowedTools`,
96
+ * `--disallowedTools` — where each present flag is terminated by the next one, and the launch
97
+ * plan places the whole group first, ahead of the prompt flags, which terminate the last. They
98
+ * must never sit immediately before pass-through positionals, which would be eaten as tool names.
99
+ * pi's flags take one comma-joined value each, so they are not exposed to this.
100
+ */
101
+ export const toolArgs = (harness, tools) => {
102
+ // Before filtering, so a poisoned name is rejected even when `deny` would have removed it, and on
103
+ // both harnesses: `projectComposition` is exported and reachable without schema validation, so
104
+ // this cannot assume the read boundary already ran.
105
+ assertProjectableToolNames(tools);
106
+ const allow = effectiveToolAllowlist(tools);
107
+ const deny = tools.deny ?? [];
108
+ if (harness === 'pi') {
109
+ return [
110
+ // No allowlist declared means no ceiling requested; `deny` below still applies.
111
+ ...(allow === undefined ? [] : allow.length === 0 ? ['--no-tools'] : ['--no-tools', '--tools', allow.join(',')]),
112
+ ...(deny.length === 0 ? [] : ['--exclude-tools', deny.join(',')]),
113
+ ];
114
+ }
115
+ return [
116
+ // The ceiling and the pre-approval within it. An allowlist emptied by `deny` projects to
117
+ // `--tools ""` — one empty-string argv element, Claude's documented "disable all tools" form —
118
+ // with no `--allowedTools`, because there is nothing left to pre-approve. Only the builtin set
119
+ // is emptied: MCP tools from selected servers survive `--tools ""` (see the scope caveat
120
+ // above), so this is close to, but not exactly, pi's zero-tool session.
121
+ ...(allow === undefined
122
+ ? []
123
+ : allow.length === 0
124
+ ? ['--tools', '']
125
+ : ['--tools', ...allow, '--allowedTools', ...allow]),
126
+ ...(deny.length === 0 ? [] : ['--disallowedTools', ...deny]),
127
+ ];
128
+ };
129
+ //# sourceMappingURL=Tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tools.js","sourceRoot":"","sources":["../../src/projection/Tools.ts"],"names":[],"mappings":"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"}
@@ -32,6 +32,17 @@ interface FrontmatterSplit {
32
32
  }
33
33
  /** Splits leading `---` YAML frontmatter from the markdown body. */
34
34
  export declare const splitFrontmatter: (content: string) => FrontmatterSplit | undefined;
35
+ /**
36
+ * The defect in a RAW `tools` value, before any normalization, or `undefined` when the shape is
37
+ * exactly `{allow?: string[], deny?: string[]}` with non-empty entries. This runs at the read
38
+ * boundary because silent normalization fails open: `asStringArray` turns an unknown key, a string
39
+ * where an array was meant, or a non-string entry into an empty or partial selection, and the
40
+ * agent then launches WITHOUT the restriction the author wrote — under `--strict` included, since
41
+ * nothing is left to warn about. A malformed selection must be a hard error naming the defect.
42
+ * `agent.md` frontmatter is shape-checked by `agent.schema.json`; this is the same invariant for
43
+ * the `config.json` overlay path that bypasses the schema.
44
+ */
45
+ export declare const toolsShapeDefect: (tools: unknown) => string | undefined;
35
46
  /**
36
47
  * Parses an agent definition. `configPaths` are highest-precedence first; each is a loadout-only
37
48
  * override that JSON-merges by key across layers over the frontmatter loadout.
@@ -1,5 +1,6 @@
1
1
  // Parses `agents/<id>/agent.md` frontmatter and merges per-layer config.json loadout overrides.
2
2
  import { readFileSync } from 'node:fs';
3
+ import { TOOL_NAME_RULE, invalidToolName } from '../projection/Tools.js';
3
4
  import { validateSchema } from '../validation/SchemaValidator.js';
4
5
  import { parseYamlDocument } from '../validation/YamlDocument.js';
5
6
  import { isPromptSourceReference } from '../composer/PromptSource.js';
@@ -53,20 +54,82 @@ const promptControlsFromRecord = (record) => ({
53
54
  promptTemplate: isPromptSourceReference(record.prompt_template) ? record.prompt_template : undefined,
54
55
  });
55
56
  const readMarkdownHeading = (body) => /^#\s+(.+)$/mu.exec(body)?.[1]?.trim();
56
- const loadoutFromRecord = (record) => {
57
- const tools = record.tools;
57
+ const loadoutFromRecord = (record) => ({
58
+ ...emptyLoadout(),
59
+ skills: asStringArray(record.skills),
60
+ subagents: asStringArray(record.subagents),
61
+ mcp: asStringArray(record.mcp),
62
+ extensions: asStringArray(record.extensions),
63
+ plugins: asStringArray(record.plugins),
64
+ model: asString(record.model),
65
+ thinking: asString(record.thinking),
66
+ tools: toolSelectionFromRecord(record.tools),
67
+ });
68
+ /**
69
+ * Narrows a raw `tools` value that `toolsShapeDefect` has already accepted. Key presence is
70
+ * preserved: an absent `allow` stays absent, because "no allowlist declared" and "an empty
71
+ * allowlist" project differently and normalizing one into the other would fabricate a ceiling.
72
+ */
73
+ const toolSelectionFromRecord = (value) => {
74
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
75
+ return undefined;
76
+ const record = value;
58
77
  return {
59
- ...emptyLoadout(),
60
- skills: asStringArray(record.skills),
61
- subagents: asStringArray(record.subagents),
62
- mcp: asStringArray(record.mcp),
63
- extensions: asStringArray(record.extensions),
64
- plugins: asStringArray(record.plugins),
65
- model: asString(record.model),
66
- thinking: asString(record.thinking),
67
- tools: tools === undefined ? undefined : { allow: asStringArray(tools.allow), deny: asStringArray(tools.deny) },
78
+ ...(record.allow === undefined ? {} : { allow: asStringArray(record.allow) }),
79
+ ...(record.deny === undefined ? {} : { deny: asStringArray(record.deny) }),
68
80
  };
69
81
  };
82
+ /**
83
+ * The defect in a RAW `tools` value, before any normalization, or `undefined` when the shape is
84
+ * exactly `{allow?: string[], deny?: string[]}` with non-empty entries. This runs at the read
85
+ * boundary because silent normalization fails open: `asStringArray` turns an unknown key, a string
86
+ * where an array was meant, or a non-string entry into an empty or partial selection, and the
87
+ * agent then launches WITHOUT the restriction the author wrote — under `--strict` included, since
88
+ * nothing is left to warn about. A malformed selection must be a hard error naming the defect.
89
+ * `agent.md` frontmatter is shape-checked by `agent.schema.json`; this is the same invariant for
90
+ * the `config.json` overlay path that bypasses the schema.
91
+ */
92
+ export const toolsShapeDefect = (tools) => {
93
+ if (tools === undefined)
94
+ return undefined;
95
+ if (tools === null || typeof tools !== 'object' || Array.isArray(tools)) {
96
+ return `\`tools\` must be an object of the form {allow?: [...], deny?: [...]}, not ${describeValue(tools)}.`;
97
+ }
98
+ const record = tools;
99
+ const unknownKey = Object.keys(record).find((key) => key !== 'allow' && key !== 'deny');
100
+ if (unknownKey !== undefined) {
101
+ return `\`tools\` has unknown key ${JSON.stringify(unknownKey)}; only "allow" and "deny" are accepted.`;
102
+ }
103
+ return toolListDefect('allow', record.allow) ?? toolListDefect('deny', record.deny);
104
+ };
105
+ /** Names the value's kind for a shape-defect message. */
106
+ const describeValue = (value) => value === null ? 'null' : Array.isArray(value) ? 'an array' : `a ${typeof value}`;
107
+ /** The defect in one raw `tools.allow`/`tools.deny` list, or `undefined` when it is well formed. */
108
+ const toolListDefect = (key, value) => {
109
+ if (value === undefined)
110
+ return undefined;
111
+ if (!Array.isArray(value)) {
112
+ return `\`tools.${key}\` must be an array of tool names, not ${describeValue(value)}.`;
113
+ }
114
+ const badIndex = value.findIndex((entry) => typeof entry !== 'string' || entry === '');
115
+ return badIndex === -1
116
+ ? undefined
117
+ : `\`tools.${key}[${badIndex}]\` must be a non-empty string, not ${JSON.stringify(value[badIndex])}.`;
118
+ };
119
+ /**
120
+ * Reports a tool name in the merged loadout that projection cannot carry. The shape is already
121
+ * guaranteed here — frontmatter by `agent.schema.json`, each `config.json` layer by
122
+ * `toolsShapeDefect` in `readConfigLoadout` — but the *names* must be re-checked on the merged
123
+ * record, because a `config.json` overlay can replace `tools` wholesale after the schema check.
124
+ */
125
+ const toolsIssue = (record, path) => {
126
+ if (record.tools === undefined)
127
+ return undefined;
128
+ const offending = invalidToolName(toolSelectionFromRecord(record.tools));
129
+ return offending === undefined
130
+ ? undefined
131
+ : { path, message: `declares an unusable tool name ${JSON.stringify(offending)}: ${TOOL_NAME_RULE}` };
132
+ };
70
133
  /** Reads one config.json, restricting it to loadout keys; parse/read/non-object failures are issues. */
71
134
  const readConfigLoadout = (configPath) => {
72
135
  let parsed;
@@ -79,7 +142,16 @@ const readConfigLoadout = (configPath) => {
79
142
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
80
143
  return { path: configPath, message: 'config.json must be a JSON object of loadout overrides.' };
81
144
  }
82
- return pickLoadoutKeys(parsed);
145
+ const picked = pickLoadoutKeys(parsed);
146
+ // Validate the RAW `tools` shape per layer, before any normalization and before the merge can
147
+ // hide which file is malformed. See `toolsShapeDefect` for why silent normalization fails open.
148
+ if ('tools' in picked && picked.tools !== undefined) {
149
+ const defect = toolsShapeDefect(picked.tools);
150
+ if (defect !== undefined) {
151
+ return { path: configPath, message: `config.json declares a malformed tool selection: ${defect}` };
152
+ }
153
+ }
154
+ return picked;
83
155
  };
84
156
  const parseFrontmatterRecord = (content, agentPath) => {
85
157
  const split = splitFrontmatter(content);
@@ -119,6 +191,13 @@ export const parseAgentDefinition = (content, configPaths, agentPath) => {
119
191
  }
120
192
  merged = { ...merged, ...config };
121
193
  }
194
+ // The JSON Schema only sees the frontmatter, and a config.json overlay can replace `tools`
195
+ // wholesale after that check. Validate the merged loadout so an unprojectable tool name is an
196
+ // error from `outfitter validate`, not a surprise at launch.
197
+ const toolIssue = toolsIssue(merged, configPaths[0] ?? agentPath);
198
+ if (toolIssue !== undefined) {
199
+ return toolIssue;
200
+ }
122
201
  return {
123
202
  name: frontmatter.record.name,
124
203
  label: asString(frontmatter.record.label)?.trim() || readMarkdownHeading(frontmatter.body),
@@ -1 +1 @@
1
- {"version":3,"file":"AgentDefinition.js","sourceRoot":"","sources":["../../src/resolver/AgentDefinition.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentDefinition.js","sourceRoot":"","sources":["../../src/resolver/AgentDefinition.ts"],"names":[],"mappings":"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"}
@@ -56,14 +56,20 @@
56
56
  "tools": {
57
57
  "type": "object",
58
58
  "properties": {
59
- "allow": { "type": "array", "items": { "type": "string", "minLength": 1 } },
60
- "deny": { "type": "array", "items": { "type": "string", "minLength": 1 } }
59
+ "allow": { "type": "array", "items": { "$ref": "#/$defs/toolName" } },
60
+ "deny": { "type": "array", "items": { "$ref": "#/$defs/toolName" } }
61
61
  },
62
62
  "additionalProperties": false
63
63
  }
64
64
  },
65
65
  "additionalProperties": true,
66
66
  "$defs": {
67
+ "toolName": {
68
+ "type": "string",
69
+ "minLength": 1,
70
+ "pattern": "^[^\\s,-][^\\s,]*$",
71
+ "description": "A tool name. A leading '-' would become an independent harness flag when projected, and a comma or whitespace would split the name inside a projected flag value, so all three are rejected."
72
+ },
67
73
  "slugList": {
68
74
  "type": "array",
69
75
  "items": { "type": "string", "minLength": 1 }
@@ -33,6 +33,7 @@ Outfitter lays out conventions for iterating on and sharing agent configuration
33
33
  The same composition runs on every surface; only the trigger changes.
34
34
 
35
35
  - [Running an agent in GitHub Actions](./actions.md) — headless runs on any workflow trigger.
36
+ - [Container images](./containers.md) — run the published Debian-based image persistently, extend it with apt, or use the `-nix` variant.
36
37
  - [Recurring runs](./recurring-runs.md) — loops three ways: the local loop extension, Actions cron, cluster schedules.
37
38
  - [In-cluster agents](./in-cluster.md) — resident agents, CronJobs, and subagent Jobs via Link Operator.
38
39
  - [Hooks](./hooks.md) — harness hook wiring and the protocol gap.
@@ -0,0 +1,187 @@
1
+ # Container images
2
+
3
+ The published image is a generic Outfitter runtime:
4
+
5
+ ```text
6
+ ghcr.io/ai-outfitter/outfitter:<version>
7
+ ```
8
+
9
+ It is Debian-based (`node:24-slim`), uses `outfitter` as its entrypoint, and
10
+ includes Node.js, npm, Git, SSH, and CA certificates at their conventional
11
+ Debian paths. It does not include agent profiles, credentials, channels, MCP
12
+ servers, or other use-case behavior. The default runtime user and group are
13
+ both `1000` (named `outfitter`), with `/tmp` as the home directory and
14
+ `/workspace` as the working directory.
15
+
16
+ A Nix closure variant of the image is also published under the `-nix` suffix;
17
+ see [the `-nix` variant](#the--nix-variant) below.
18
+
19
+ ## Run a resident agent
20
+
21
+ A resident container is an ordinary `outfitter run` whose harness stays in RPC
22
+ mode. Keep standard input open so the harness remains available while its
23
+ extensions wait for work:
24
+
25
+ ```yaml
26
+ apiVersion: apps/v1
27
+ kind: Deployment
28
+ metadata:
29
+ name: example-agent
30
+ spec:
31
+ replicas: 1
32
+ selector:
33
+ matchLabels:
34
+ app: example-agent
35
+ template:
36
+ metadata:
37
+ labels:
38
+ app: example-agent
39
+ spec:
40
+ securityContext:
41
+ # Pod-level, and not optional. HOME points at the mounted volume, and a
42
+ # freshly provisioned volume arrives owned by root — runAsUser does not
43
+ # change that. Extension installs and credential persistence both write
44
+ # below HOME, so without fsGroup the agent fails with EACCES on first
45
+ # write rather than at startup. A volume plugin that ignores fsGroup
46
+ # must be pre-provisioned with UID/GID 1000 ownership instead.
47
+ fsGroup: 1000
48
+ containers:
49
+ - name: agent
50
+ # The primary Debian-based image; append -nix for the Nix variant.
51
+ image: ghcr.io/ai-outfitter/outfitter:<version>
52
+ stdin: true
53
+ workingDir: /workspace
54
+ securityContext:
55
+ runAsNonRoot: true
56
+ runAsUser: 1000
57
+ runAsGroup: 1000
58
+ env:
59
+ - name: HOME
60
+ value: /workspace
61
+ args:
62
+ - run
63
+ - example-agent
64
+ - --strict
65
+ - --
66
+ - --mode
67
+ - rpc
68
+ - --no-session
69
+ volumeMounts:
70
+ - name: workspace
71
+ mountPath: /workspace
72
+ volumes:
73
+ - name: workspace
74
+ persistentVolumeClaim:
75
+ claimName: example-agent
76
+ ```
77
+
78
+ Mount `.agents` settings, credentials, and any channel configuration through
79
+ the workload that owns the container. The image does not interpret Kubernetes
80
+ resources or impose image, profile, or extension policy.
81
+
82
+ ## Build a derivative image
83
+
84
+ The image is a normal Debian base: extend it with an ordinary Dockerfile.
85
+ `apt-get` works, and so does `COPY`ing binaries. A dynamically linked binary
86
+ runs when it matches the image — same architecture, glibc-linked, and its
87
+ shared-library dependencies present. The standard ELF interpreter is where
88
+ tools expect it (unlike the `-nix` variant), but the slim base ships a small
89
+ library set: `apt-get install` a binary's runtime libraries when it needs more.
90
+ Switch to `root` for the layers that install, then drop back to `1000`:
91
+
92
+ ```dockerfile
93
+ FROM ghcr.io/ai-outfitter/outfitter:<version>
94
+
95
+ USER root
96
+ RUN apt-get update \
97
+ && apt-get install -y --no-install-recommends jq ripgrep \
98
+ && rm -rf /var/lib/apt/lists/*
99
+ COPY --chmod=0755 my-tool /usr/local/bin/my-tool
100
+ USER 1000
101
+ ```
102
+
103
+ Build and exercise the exact image:
104
+
105
+ ```sh
106
+ docker build -t example-agent .
107
+ docker run --rm example-agent --version
108
+ docker run --rm --entrypoint /bin/sh example-agent \
109
+ -c 'jq --version && rg --version && my-tool --version'
110
+ ```
111
+
112
+ The entrypoint stays `outfitter`; override `ENTRYPOINT` only when the derived
113
+ image wraps the launch itself.
114
+
115
+ ## The `-nix` variant
116
+
117
+ The Nix closure image that was previously the primary tag remains published
118
+ for `lib.mkContainer` consumers:
119
+
120
+ ```text
121
+ ghcr.io/ai-outfitter/outfitter:<version>-nix
122
+ ```
123
+
124
+ It is built by the flake, includes the Nix CLI, Bash, core utilities, Git,
125
+ SSH, and CA certificates, and its entrypoint is an absolute `/nix/store` path.
126
+ It is not conventionally extensible — there is no apt, and foreign dynamic
127
+ binaries do not run — so extend it through Nix instead: the flake exports
128
+ `lib.mkContainer` for reproducible derivative images:
129
+
130
+ ```nix
131
+ {
132
+ inputs = {
133
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
134
+ outfitter = {
135
+ url = "github:ai-outfitter/outfitter/v1.4.0";
136
+ inputs.nixpkgs.follows = "nixpkgs";
137
+ };
138
+ };
139
+
140
+ outputs =
141
+ { nixpkgs, outfitter, ... }:
142
+ let
143
+ system = "x86_64-linux";
144
+ pkgs = nixpkgs.legacyPackages.${system};
145
+ in
146
+ {
147
+ packages.${system}.default = outfitter.lib.mkContainer {
148
+ inherit pkgs;
149
+ outfitterPackage = outfitter.packages.${system}.outfitter;
150
+ name = "example-agent";
151
+ extraPackages = [
152
+ pkgs.jq
153
+ pkgs.ripgrep
154
+ ];
155
+ };
156
+ };
157
+ }
158
+ ```
159
+
160
+ Build and exercise the exact image:
161
+
162
+ ```sh
163
+ nix build
164
+ docker load < result
165
+ docker run --rm example-agent:latest --version
166
+ docker run --rm --entrypoint /bin/sh example-agent:latest \
167
+ -c 'nix --version && jq --version && rg --version'
168
+ ```
169
+
170
+ Prefer adding known runtime packages through `extraPackages`. The resulting
171
+ image stays reproducible, and it avoids the trap below.
172
+
173
+ **Do not mount an empty volume over `/nix` of the `-nix` variant.** That image
174
+ _is_ its Nix store: the entrypoint is an absolute store path and every binary
175
+ in `/bin` is a symlink into `/nix/store`. Mounting a fresh volume there hides
176
+ all of it, so the container cannot start — it fails before it could initialize
177
+ the very store you mounted the volume to populate. (The primary Debian image
178
+ has no `/nix` and is not affected.)
179
+
180
+ Runtime installation in the `-nix` variant therefore needs one of:
181
+
182
+ - a volume **pre-populated** with the image's closure, seeded from the image
183
+ before the agent starts (an init container copying `/nix` into the volume);
184
+ - an **overlay** whose lower layer is the image's `/nix`, so the closure stays
185
+ visible while writes land in the upper layer; or
186
+ - writable Nix **state** only — `/nix/var` and a per-user profile — leaving the
187
+ store itself as the image shipped it.
@@ -28,7 +28,7 @@ Tasks and bake are not in this matrix — they are the subject of a [separate up
28
28
  | Credentials and environment | Supported | Supported |
29
29
  | DeepWork job selection | Supported | Roadmap |
30
30
  | Hooks | Partial | Partial |
31
- | Tool availability | Roadmap | Roadmap |
31
+ | Tool availability (agent `tools:` loadout) | Supported | Supported |
32
32
  | Theme / UI presentation | Roadmap | Roadmap |
33
33
  | Working directory | Roadmap | Roadmap |
34
34
  | Pass-through arguments | Supported | Supported |
@@ -41,6 +41,7 @@ Tasks and bake are not in this matrix — they are the subject of a [separate up
41
41
  - **Skills (Partial)** — selected skills are materialized into the config directory's skills surface; remaining gaps are tracked per release. The bundled Outfitter skill ships through the plugin channel.
42
42
  - **Model selection (Partial)** — model maps to `--model` and thinking level to `--effort`; provider selection is not projected for Claude and warns if requested.
43
43
  - **Hooks (Partial)** — hook configuration is projected into the generated `settings.json`; there is no portable protocol hooks resource yet. See [Hooks](./hooks.md).
44
+ - **Tool availability** — `tools.allow` (after `tools.deny` removes entries) maps to both `--tools` (_availability_: an unlisted builtin is not in the session) and `--allowedTools` (_permission_: the granted tools are pre-approved, so a headless session is not stopped by a prompt); `tools.deny` always maps to `--disallowedTools`, including when both are declared, and a bare denied name removes the tool from context per Claude's docs. An allowlist that `tools.deny` empties maps to `--tools ""`, Claude's documented "disable all tools" form. Caveat: per the CLI reference, `--tools` governs the built-in set only — MCP tools (`mcp__server__*`) are unaffected and are governed by which MCP servers the loadout selects, so `--tools ""` is not exactly pi's zero-tool session when MCP servers are present. Claude's behavior here comes from `claude --help` and the CLI reference, not local measurement.
44
45
  - **DeepWork jobs** — job selection is Pi-only today and warns on Claude.
45
46
  - **Bundled Outfitter skill** — every launch also publishes Outfitter's own self-documentation skill as a bundled plugin, so the agent can explain Outfitter and this launch's configuration.
46
47
 
@@ -49,6 +50,7 @@ Tasks and bake are not in this matrix — they are the subject of a [separate up
49
50
  - Pi projects the full resource set: agent identity, subagents (via the subagent extension), skills (`--skill`), commands, model configuration, MCP, extensions (`--extension`) and plugins as first-class loadout elements, environment, pass-through args, session directory, and DeepWork job selection.
50
51
  - Selected skills resolve across layers following [layer precedence](./concepts.md#layer-precedence); `references`, `scripts`, and `assets` frontmatter materialize into a generated skill passed via `--skill`. `outfitter validate` checks selections and references before launch.
51
52
  - **Hooks (Partial)** — bootstrap behavior uses an explicit Pi extension via `--extension`; recurring per-event hooks are extension territory. See [Hooks](./hooks.md).
53
+ - **Tool availability** — `tools.allow` (after `tools.deny` removes entries) maps to `--no-tools --tools a,b,c`, and `tools.deny` maps to `--exclude-tools a,b,c`. `--tools` is a hard allowlist across built-in, extension, and custom tools, so the session's tool set is exactly that list. An allowlist that `tools.deny` empties maps to `--no-tools` alone, a session with no tools at all. Note that `--no-builtin-tools` is deliberately not used: it keeps extension and custom tools enabled, so it does not express an empty tool set.
52
54
  - Every launch also passes Outfitter's own self-documentation skill through `--skill`.
53
55
 
54
56
  For the architecture-level definitions behind each row, see [Controllable elements](../architecture/controllable-elements.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-outfitter/outfitter",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "Profile-oriented wrapper for launching pi, Claude Code, and future agent CLIs with reproducible configuration.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -56,14 +56,20 @@
56
56
  "tools": {
57
57
  "type": "object",
58
58
  "properties": {
59
- "allow": { "type": "array", "items": { "type": "string", "minLength": 1 } },
60
- "deny": { "type": "array", "items": { "type": "string", "minLength": 1 } }
59
+ "allow": { "type": "array", "items": { "$ref": "#/$defs/toolName" } },
60
+ "deny": { "type": "array", "items": { "$ref": "#/$defs/toolName" } }
61
61
  },
62
62
  "additionalProperties": false
63
63
  }
64
64
  },
65
65
  "additionalProperties": true,
66
66
  "$defs": {
67
+ "toolName": {
68
+ "type": "string",
69
+ "minLength": 1,
70
+ "pattern": "^[^\\s,-][^\\s,]*$",
71
+ "description": "A tool name. A leading '-' would become an independent harness flag when projected, and a comma or whitespace would split the name inside a projected flag value, so all three are rejected."
72
+ },
67
73
  "slugList": {
68
74
  "type": "array",
69
75
  "items": { "type": "string", "minLength": 1 }