@ai-outfitter/outfitter 1.3.0 → 1.4.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.
- package/dist/agents/AgentLaunch.d.ts +17 -0
- package/dist/agents/AgentLaunch.js +53 -2
- package/dist/agents/AgentLaunch.js.map +1 -1
- package/dist/cli/commands/RunAgentCommand.d.ts +2 -0
- package/dist/cli/commands/RunAgentCommand.js +14 -1
- package/dist/cli/commands/RunAgentCommand.js.map +1 -1
- package/dist/projection/ProjectHarness.js +40 -3
- package/dist/projection/ProjectHarness.js.map +1 -1
- package/dist/projection/Projection.d.ts +6 -0
- package/docs/documentation/README.md +1 -0
- package/docs/documentation/concepts.md +1 -1
- package/docs/documentation/containers.md +138 -0
- package/docs/documentation/personas.md +33 -7
- package/docs/documentation/usecases/persona-reviews.md +6 -5
- package/package.json +1 -1
|
@@ -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
|
|
49
|
-
child.on('
|
|
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":"AAAA,6FAA6F;AAC7F,kGAAkG;AAClG,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAQzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,QAA8B,EAC9B,UAA2B,EAC3B,OAAe,EACE,EAAE;IACnB,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/F,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,+FAA+F;AAC/F,kGAAkG;AAClG,mGAAmG;AACnG,+FAA+F;AAC/F,yBAAyB;AACzB,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,UAA2B,EAAmB,EAAE;IAC3F,IAAI,UAAU,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAChC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,eAAe,GAAG,sBAAsB,EAAE,CAAC;IAEjD,oGAAoG;IACpG,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO;QACL,GAAG,UAAU;QACb,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,IAAI,EAAE,CAAC,GAAG,eAAe,CAAC,UAAU,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC;QACzD,8FAA8F;QAC9F,gGAAgG;QAChG,gGAAgG;QAChG,4FAA4F;QAC5F,GAAG,EAAE,EAAE,qBAAqB,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE;KACvD,CAAC;AACJ,CAAC,CAAC;AAEF,iGAAiG;AACjG,MAAM,CAAC,MAAM,aAAa,GAAyB;IACjD,KAAK,CAAC,MAAM,CAAC,IAAqB;QAChC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QACvD,OAAO,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC9G,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,mDAAmD;
|
|
1
|
+
{"version":3,"file":"AgentLaunch.js","sourceRoot":"","sources":["../../src/agents/AgentLaunch.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,8 @@ export interface RunAgentInput {
|
|
|
19
19
|
readonly harness?: string;
|
|
20
20
|
readonly strict?: boolean;
|
|
21
21
|
readonly passThroughArgs?: readonly string[];
|
|
22
|
+
/** `--append-prompt` documents appended to the system prompt after the agent's own, in order. */
|
|
23
|
+
readonly appendPromptPaths?: readonly string[];
|
|
22
24
|
readonly launcher: AgentProcessLauncher;
|
|
23
25
|
/** Onboarding to run once when no agent is selected and settings define no `default_agent`. */
|
|
24
26
|
readonly setup?: SetupRunner;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// `outfitter run [agent]` — resolve → compose → project → launch on the .agents model.
|
|
2
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
2
|
+
import { mkdtempSync, rmSync, statSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { Option } from 'commander';
|
|
@@ -67,6 +67,15 @@ const resolvePiExtensions = async (input, harness, extensionSpecs) => {
|
|
|
67
67
|
});
|
|
68
68
|
};
|
|
69
69
|
const piConfigurationOverlays = (plan, selectedAgent) => [...(plan.contributingAgents ?? [selectedAgent])].reverse().flatMap((agent) => agent.piConfigDirectories ?? []);
|
|
70
|
+
// Validated before launch rather than inside projection: pi never reads these paths itself, so an
|
|
71
|
+
// unreadable one would otherwise fail differently per harness — a raw ENOENT out of the Claude
|
|
72
|
+
// concatenation, and a harness-generated error on pi.
|
|
73
|
+
const assertReadableAppendPrompts = (paths) => {
|
|
74
|
+
const unreadable = (paths ?? []).filter((path) => statSync(path, { throwIfNoEntry: false })?.isFile() !== true);
|
|
75
|
+
if (unreadable.length > 0) {
|
|
76
|
+
throw new Error(`--append-prompt: not a readable file: ${unreadable.join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
70
79
|
export const executeRunAgentCommand = async (input) => {
|
|
71
80
|
// Flush messages to the terminal (before launch); they are also returned so callers can inspect them.
|
|
72
81
|
const emit = (messages) => {
|
|
@@ -75,6 +84,7 @@ export const executeRunAgentCommand = async (input) => {
|
|
|
75
84
|
};
|
|
76
85
|
let resolved = resolveEffectiveSet(input);
|
|
77
86
|
assertNoSettingsIssues(resolved.settingsIssues);
|
|
87
|
+
assertReadableAppendPrompts(input.appendPromptPaths);
|
|
78
88
|
emit(resolved.warnings.map((warning) => `warning: ${warning}`));
|
|
79
89
|
// First run: nothing selected and no default configured — onboard, then resolve again.
|
|
80
90
|
const setupMessages = [];
|
|
@@ -110,6 +120,7 @@ export const executeRunAgentCommand = async (input) => {
|
|
|
110
120
|
homeDirectory: input.homeDirectory,
|
|
111
121
|
sessionDirectory: resolveSessionDirectory(input, harness),
|
|
112
122
|
passThroughArgs: input.passThroughArgs,
|
|
123
|
+
appendPromptPaths: input.appendPromptPaths,
|
|
113
124
|
extensionLoadDirs: harness === 'pi' ? extensions.loadDirs : undefined,
|
|
114
125
|
// ProjectHarness only overlays these for the pi harness, so pass them through unconditionally.
|
|
115
126
|
configurationOverlayDirectories: configurationOverlays,
|
|
@@ -163,6 +174,7 @@ export const createRunAgentCommand = (dependencies = {}) => ({
|
|
|
163
174
|
.argument('[args...]', 'Arguments passed through to the harness after --.')
|
|
164
175
|
.addOption(new Option('--harness <harness>', 'Harness to launch.').choices([...HARNESSES]))
|
|
165
176
|
.option('--strict', 'Treat composition warnings and unsupported loadout elements as fatal.')
|
|
177
|
+
.option('--append-prompt <path>', 'Append a Markdown document to the system prompt. Repeatable; applied in the order given.', (value, previous = []) => [...previous, value])
|
|
166
178
|
.allowUnknownOption(true)
|
|
167
179
|
.action(async (agent, passThroughArgs, options) => {
|
|
168
180
|
/* v8 ignore next 3 -- process/launcher defaults are exercised by the CLI entrypoint, not unit tests. */
|
|
@@ -176,6 +188,7 @@ export const createRunAgentCommand = (dependencies = {}) => ({
|
|
|
176
188
|
harness: options.harness,
|
|
177
189
|
strict: options.strict,
|
|
178
190
|
passThroughArgs,
|
|
191
|
+
appendPromptPaths: options.appendPrompt,
|
|
179
192
|
launcher,
|
|
180
193
|
setup: dependencies.setup ?? interactiveSetupRunner,
|
|
181
194
|
// executeRunAgentCommand emits messages (before launch) through this sink.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunAgentCommand.js","sourceRoot":"","sources":["../../../src/cli/commands/RunAgentCommand.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"RunAgentCommand.js","sourceRoot":"","sources":["../../../src/cli/commands/RunAgentCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Projects a harness-neutral CompositionPlan to a native pi or Claude Code launch.
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
2
4
|
import { PI_SESSION_DIRECTORY_ENV } from '../agents/PiSessionDirectory.js';
|
|
3
5
|
import { materializeComposition, materializeConfigurationOverlays } from './Materialize.js';
|
|
4
6
|
// Loadout elements a projection actually maps to native config. Anything else is reported
|
|
@@ -38,10 +40,41 @@ const loadoutElementsInUse = (composition) => {
|
|
|
38
40
|
return present;
|
|
39
41
|
};
|
|
40
42
|
export const unsupportedElements = (composition, input) => loadoutElementsInUse(composition).filter((element) => !supportedElements(input).includes(element));
|
|
43
|
+
/**
|
|
44
|
+
* The two harnesses take prompt documents through incompatible flags, verified against pi 0.x via
|
|
45
|
+
* `outfitter run` and Claude Code 2.1.x directly:
|
|
46
|
+
*
|
|
47
|
+
* | | pi | claude |
|
|
48
|
+
* | `--system-prompt <path>` / `--append-system-prompt <path>` | reads the file | appends the path *text* |
|
|
49
|
+
* | `--system-prompt-file` / `--append-system-prompt-file` | rejected: `Unknown option` | reads the file |
|
|
50
|
+
* | repeated append flags | accumulate | last one wins |
|
|
51
|
+
*
|
|
52
|
+
* pi therefore takes paths on the bare flags, and Claude takes the `-file` forms. Claude's are
|
|
53
|
+
* undocumented — neither appears in `claude --help` — but both apply the file's contents, while the
|
|
54
|
+
* bare flags silently append the path string and drop the document. Getting this wrong produces no
|
|
55
|
+
* error, just an agent launched without its identity.
|
|
56
|
+
*
|
|
57
|
+
* Claude also gets a single append flag over a concatenation, because repeats overwrite.
|
|
58
|
+
*/
|
|
59
|
+
const promptPathArg = (harness, flag) => harness === 'pi' ? `--${flag}` : `--${flag}-file`;
|
|
60
|
+
const appendPromptArgs = (harness, rootDirectory, paths) => {
|
|
61
|
+
/* v8 ignore next 2 -- unreachable through composition, which always contributes the agent body;
|
|
62
|
+
kept because projectComposition is exported and an empty list must not name an empty file. */
|
|
63
|
+
if (paths.length === 0)
|
|
64
|
+
return [];
|
|
65
|
+
if (harness === 'pi')
|
|
66
|
+
return paths.flatMap((path) => ['--append-system-prompt', path]);
|
|
67
|
+
const combinedPath = join(rootDirectory, 'append-system-prompt.md');
|
|
68
|
+
// A blank line between documents: one newline would let a document that ends mid-sentence merge
|
|
69
|
+
// into the next one's opening paragraph, or turn it into a setext heading.
|
|
70
|
+
const documents = paths.map((path) => readFileSync(path, 'utf8').replace(/\n*$/, '\n'));
|
|
71
|
+
writeFileSync(combinedPath, documents.join('\n'));
|
|
72
|
+
return [promptPathArg(harness, 'append-system-prompt'), combinedPath];
|
|
73
|
+
};
|
|
41
74
|
const promptArgs = (composition, input, systemPromptPath, appendPromptPaths) => [
|
|
42
|
-
'
|
|
75
|
+
promptPathArg(input.harness, 'system-prompt'),
|
|
43
76
|
systemPromptPath,
|
|
44
|
-
...
|
|
77
|
+
...appendPromptArgs(input.harness, input.rootDirectory, appendPromptPaths),
|
|
45
78
|
...(input.harness === 'pi' && composition.identity.promptTemplate !== undefined
|
|
46
79
|
? ['--prompt-template', `${input.rootDirectory}/prompt-template.md`]
|
|
47
80
|
: []),
|
|
@@ -86,7 +119,11 @@ export const projectComposition = (composition, input) => {
|
|
|
86
119
|
materializeConfigurationOverlays(input.configurationOverlayDirectories ?? [], input.rootDirectory);
|
|
87
120
|
}
|
|
88
121
|
const materialized = materializeComposition(composition, input.rootDirectory);
|
|
89
|
-
|
|
122
|
+
// Caller documents follow the composition's own, so a persona is read against the agent it adopts.
|
|
123
|
+
const launch = buildLaunchPlan(composition, input, materialized.systemPromptPath, [
|
|
124
|
+
...materialized.appendPromptPaths,
|
|
125
|
+
...(input.appendPromptPaths ?? []),
|
|
126
|
+
]);
|
|
90
127
|
const unsupported = [
|
|
91
128
|
...unsupportedElements(composition, input),
|
|
92
129
|
...materialized.skippedSkills.map((slug) => `skill:${slug} (escaping symlink)`),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectHarness.js","sourceRoot":"","sources":["../../src/projection/ProjectHarness.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAG3E,OAAO,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,MAAM,kBAAkB,CAAC;AAG5F,0FAA0F;AAC1F,gGAAgG;AAChG,iGAAiG;AACjG,+FAA+F;AAC/F,kGAAkG;AAClG,UAAU;AACV,MAAM,iBAAiB,GAAG,CAAC,KAAsB,EAAqB,EAAE;IACtE,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IACjD,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IAC5C,MAAM,EAAE,GAAG,CAAC,GAAG,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAChE,OAAO,KAAK,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,CAAC;AAC5E,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAAC,WAA4B,EAAqB,EAAE;IAC/E,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC;IAChC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC9D,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAEvF,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,WAA4B,EAAE,KAAsB,EAAqB,EAAE,CAC7G,oBAAoB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAErG,MAAM,UAAU,GAAG,CACjB,WAA4B,EAC5B,KAAsB,EACtB,gBAAwB,EACxB,iBAAoC,EACjB,EAAE,CAAC;IACtB,
|
|
1
|
+
{"version":3,"file":"ProjectHarness.js","sourceRoot":"","sources":["../../src/projection/ProjectHarness.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAG3E,OAAO,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,MAAM,kBAAkB,CAAC;AAG5F,0FAA0F;AAC1F,gGAAgG;AAChG,iGAAiG;AACjG,+FAA+F;AAC/F,kGAAkG;AAClG,UAAU;AACV,MAAM,iBAAiB,GAAG,CAAC,KAAsB,EAAqB,EAAE;IACtE,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IACjD,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IAC5C,MAAM,EAAE,GAAG,CAAC,GAAG,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAChE,OAAO,KAAK,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,CAAC;AAC5E,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAAC,WAA4B,EAAqB,EAAE;IAC/E,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC;IAChC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC9D,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAEvF,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,WAA4B,EAAE,KAAsB,EAAqB,EAAE,CAC7G,oBAAoB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAErG;;;;;;;;;;;;;;;GAeG;AACH,MAAM,aAAa,GAAG,CAAC,OAAgB,EAAE,IAA8C,EAAU,EAAE,CACjG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC;AAEpD,MAAM,gBAAgB,GAAG,CAAC,OAAgB,EAAE,aAAqB,EAAE,KAAwB,EAAqB,EAAE;IAChH;oGACgG;IAChG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC,CAAC;IAEvF,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;IACpE,gGAAgG;IAChG,2EAA2E;IAC3E,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IACxF,aAAa,CAAC,YAAY,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,aAAa,CAAC,OAAO,EAAE,sBAAsB,CAAC,EAAE,YAAY,CAAC,CAAC;AACxE,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CACjB,WAA4B,EAC5B,KAAsB,EACtB,gBAAwB,EACxB,iBAAoC,EACjB,EAAE,CAAC;IACtB,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,eAAe,CAAC;IAC7C,gBAAgB;IAChB,GAAG,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1E,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,KAAK,SAAS;QAC7E,CAAC,CAAC,CAAC,mBAAmB,EAAE,GAAG,KAAK,CAAC,aAAa,qBAAqB,CAAC;QACpE,CAAC,CAAC,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,WAA4B,EAAE,OAAgB,EAAqB,EAAE;IACtF,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,IAAI,WAAW,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,WAAW,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxF,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CACtB,WAA4B,EAC5B,KAAsB,EACtB,gBAAwB,EACxB,iBAAoC,EACnB,EAAE;IACnB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrG,MAAM,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzG,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ;QAC/B,IAAI,EAAE;YACJ,GAAG,UAAU,CAAC,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,CAAC;YACtE,GAAG,SAAS;YACZ,GAAG,aAAa;YAChB,GAAG,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC;YACxC,GAAG,CAAC,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC;SACjC;QACD,8FAA8F;QAC9F,4FAA4F;QAC5F,iGAAiG;QACjG,GAAG,EAAE,IAAI;YACP,CAAC,CAAC;gBACE,mBAAmB,EAAE,KAAK,CAAC,aAAa;gBACxC,GAAG,CAAC,KAAK,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,wBAAwB,CAAC,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC;aACxG;YACH,CAAC,CAAC,EAAE,iBAAiB,EAAE,KAAK,CAAC,aAAa,EAAE;KAC/C,CAAC;AACJ,CAAC,CAAC;AAEF,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,WAA4B,EAAE,KAAsB,EAAuB,EAAE;IAC9G,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC3B,gCAAgC,CAAC,KAAK,CAAC,+BAA+B,IAAI,EAAE,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IACrG,CAAC;IACD,MAAM,YAAY,GAAG,sBAAsB,CAAC,WAAW,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC9E,mGAAmG;IACnG,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,EAAE,KAAK,EAAE,YAAY,CAAC,gBAAgB,EAAE;QAChF,GAAG,YAAY,CAAC,iBAAiB;QACjC,GAAG,CAAC,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC;KACnC,CAAC,CAAC;IACH,MAAM,WAAW,GAAG;QAClB,GAAG,mBAAmB,CAAC,WAAW,EAAE,KAAK,CAAC;QAC1C,GAAG,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,qBAAqB,CAAC;QAC/E,GAAG,YAAY,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,IAAI,uBAAuB,CAAC;KACxF,CAAC;IAEF,OAAO,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACrE,CAAC,CAAC"}
|
|
@@ -18,6 +18,12 @@ export interface ProjectionInput {
|
|
|
18
18
|
/** Durable session store for the run (pi only); omitted to leave the harness default in place. */
|
|
19
19
|
readonly sessionDirectory?: string;
|
|
20
20
|
readonly passThroughArgs?: readonly string[];
|
|
21
|
+
/**
|
|
22
|
+
* Caller-supplied documents appended to the system prompt after the composition's own, in the
|
|
23
|
+
* order given — typically a persona, by absolute path from outside the projection root. Projected
|
|
24
|
+
* per harness, since pi and claude take append-prompt documents through incompatible flags.
|
|
25
|
+
*/
|
|
26
|
+
readonly appendPromptPaths?: readonly string[];
|
|
21
27
|
/** Local pi extension install directories to load with `--extension` (pi only). */
|
|
22
28
|
readonly extensionLoadDirs?: readonly string[];
|
|
23
29
|
/** Harness-native configuration directories, highest precedence first, overlaid into the root. */
|
|
@@ -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 image persistently or add Nix-packaged tools.
|
|
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.
|
|
@@ -49,7 +49,7 @@ The protocol resources Outfitter resolves and composes:
|
|
|
49
49
|
Three related terms, none of which is a settings key or a separate file format:
|
|
50
50
|
|
|
51
51
|
- A **[profile](./profiles.md)** is just an agent and its loadout. "The engineer profile" is the `engineer` agent with everything it composes. There is no `profile.yml` and no `profiles:` map — the loadout lives on the agent.
|
|
52
|
-
- A **[persona](./personas.md)** is a _convention_, not a resource: one portable, committed Markdown document per persona — for
|
|
52
|
+
- A **[persona](./personas.md)** is a _convention_, not a resource: one portable, committed Markdown document per persona — `docs/personas/platform-lead.md` for a project's own readers, or `~/.agents/personas/platform-lead.md` for a reader who outlives one repository — appended at launch to a shared review agent, or pasted into any tool as stakeholder context. Swapping the file swaps the persona.
|
|
53
53
|
- A **[subagent](./subagents.md)** is an agent projected into the harness's native delegation mechanism, selected in another agent's `subagents` loadout. A leader agent delegates to local coding-harness subagents or to issue- and action-backed subagents.
|
|
54
54
|
|
|
55
55
|
The same agent definition can be run directly or selected as a subagent elsewhere; its loadout decides what it composes.
|
|
@@ -0,0 +1,138 @@
|
|
|
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 uses `outfitter` as its entrypoint and includes the Nix CLI, Bash, core
|
|
10
|
+
utilities, Git, SSH, and CA certificates. It does not include agent profiles,
|
|
11
|
+
credentials, channels, MCP servers, or other use-case behavior. The default
|
|
12
|
+
runtime user and group are both `1000`, with `/tmp` as the home directory.
|
|
13
|
+
|
|
14
|
+
## Run a resident agent
|
|
15
|
+
|
|
16
|
+
A resident container is an ordinary `outfitter run` whose harness stays in RPC
|
|
17
|
+
mode. Keep standard input open so the harness remains available while its
|
|
18
|
+
extensions wait for work:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
apiVersion: apps/v1
|
|
22
|
+
kind: Deployment
|
|
23
|
+
metadata:
|
|
24
|
+
name: example-agent
|
|
25
|
+
spec:
|
|
26
|
+
replicas: 1
|
|
27
|
+
selector:
|
|
28
|
+
matchLabels:
|
|
29
|
+
app: example-agent
|
|
30
|
+
template:
|
|
31
|
+
metadata:
|
|
32
|
+
labels:
|
|
33
|
+
app: example-agent
|
|
34
|
+
spec:
|
|
35
|
+
securityContext:
|
|
36
|
+
# Pod-level, and not optional. HOME points at the mounted volume, and a
|
|
37
|
+
# freshly provisioned volume arrives owned by root — runAsUser does not
|
|
38
|
+
# change that. Extension installs and credential persistence both write
|
|
39
|
+
# below HOME, so without fsGroup the agent fails with EACCES on first
|
|
40
|
+
# write rather than at startup. A volume plugin that ignores fsGroup
|
|
41
|
+
# must be pre-provisioned with UID/GID 1000 ownership instead.
|
|
42
|
+
fsGroup: 1000
|
|
43
|
+
containers:
|
|
44
|
+
- name: agent
|
|
45
|
+
image: ghcr.io/ai-outfitter/outfitter:<version>
|
|
46
|
+
stdin: true
|
|
47
|
+
workingDir: /workspace
|
|
48
|
+
securityContext:
|
|
49
|
+
runAsNonRoot: true
|
|
50
|
+
runAsUser: 1000
|
|
51
|
+
runAsGroup: 1000
|
|
52
|
+
env:
|
|
53
|
+
- name: HOME
|
|
54
|
+
value: /workspace
|
|
55
|
+
args:
|
|
56
|
+
- run
|
|
57
|
+
- example-agent
|
|
58
|
+
- --strict
|
|
59
|
+
- --
|
|
60
|
+
- --mode
|
|
61
|
+
- rpc
|
|
62
|
+
- --no-session
|
|
63
|
+
volumeMounts:
|
|
64
|
+
- name: workspace
|
|
65
|
+
mountPath: /workspace
|
|
66
|
+
volumes:
|
|
67
|
+
- name: workspace
|
|
68
|
+
persistentVolumeClaim:
|
|
69
|
+
claimName: example-agent
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Mount `.agents` settings, credentials, and any channel configuration through
|
|
73
|
+
the workload that owns the container. The image does not interpret Kubernetes
|
|
74
|
+
resources or impose image, profile, or extension policy.
|
|
75
|
+
|
|
76
|
+
## Build a derivative image
|
|
77
|
+
|
|
78
|
+
Being built with Nix does not normally imply that an image contains the Nix
|
|
79
|
+
CLI. The published Outfitter image includes it intentionally, and the flake also
|
|
80
|
+
exports `lib.mkContainer` for reproducible derivative images:
|
|
81
|
+
|
|
82
|
+
```nix
|
|
83
|
+
{
|
|
84
|
+
inputs = {
|
|
85
|
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
86
|
+
outfitter = {
|
|
87
|
+
url = "github:ai-outfitter/outfitter/v1.2.0";
|
|
88
|
+
inputs.nixpkgs.follows = "nixpkgs";
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
outputs =
|
|
93
|
+
{ nixpkgs, outfitter, ... }:
|
|
94
|
+
let
|
|
95
|
+
system = "x86_64-linux";
|
|
96
|
+
pkgs = nixpkgs.legacyPackages.${system};
|
|
97
|
+
in
|
|
98
|
+
{
|
|
99
|
+
packages.${system}.default = outfitter.lib.mkContainer {
|
|
100
|
+
inherit pkgs;
|
|
101
|
+
outfitterPackage = outfitter.packages.${system}.outfitter;
|
|
102
|
+
name = "example-agent";
|
|
103
|
+
extraPackages = [
|
|
104
|
+
pkgs.jq
|
|
105
|
+
pkgs.ripgrep
|
|
106
|
+
];
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Build and exercise the exact image:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
nix build
|
|
116
|
+
docker load < result
|
|
117
|
+
docker run --rm example-agent:latest --version
|
|
118
|
+
docker run --rm --entrypoint /bin/sh example-agent:latest \
|
|
119
|
+
-c 'nix --version && jq --version && rg --version'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Prefer adding known runtime packages through `extraPackages`. The resulting
|
|
123
|
+
image stays reproducible, and it avoids the trap below.
|
|
124
|
+
|
|
125
|
+
**Do not mount an empty volume over `/nix`.** The image _is_ its Nix store: the
|
|
126
|
+
entrypoint is an absolute store path and every binary in `/bin` is a symlink
|
|
127
|
+
into `/nix/store`. Mounting a fresh volume there hides all of it, so the
|
|
128
|
+
container cannot start — it fails before it could initialize the very store you
|
|
129
|
+
mounted the volume to populate.
|
|
130
|
+
|
|
131
|
+
Runtime installation therefore needs one of:
|
|
132
|
+
|
|
133
|
+
- a volume **pre-populated** with the image's closure, seeded from the image
|
|
134
|
+
before the agent starts (an init container copying `/nix` into the volume);
|
|
135
|
+
- an **overlay** whose lower layer is the image's `/nix`, so the closure stays
|
|
136
|
+
visible while writes land in the upper layer; or
|
|
137
|
+
- writable Nix **state** only — `/nix/var` and a per-user profile — leaving the
|
|
138
|
+
store itself as the image shipped it.
|
|
@@ -13,30 +13,56 @@ A persona file is plain Markdown with no frontmatter and no schema:
|
|
|
13
13
|
- First-person prose: an unheaded opening paragraph, then the recommended sections `## My work and context`, `## What I need`, `## How I decide`, and `## How I communicate`. Sections may be renamed or combined; connected prose beats bullet stacks.
|
|
14
14
|
- Everything the persona knows — role, goals, constraints, decision signals, voice — is ordinary Markdown.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
The authoring template and completed reference personas ship in the community catalog's [`persona-authoring`](https://github.com/ai-outfitter/community-profiles/tree/main/skills/persona-authoring) skill.
|
|
17
|
+
|
|
18
|
+
## Where personas live
|
|
19
|
+
|
|
20
|
+
A persona file lives in normal documentation, deliberately outside `.agents/`, in one of two tiers:
|
|
17
21
|
|
|
18
22
|
```text
|
|
19
|
-
docs/personas/
|
|
23
|
+
docs/personas/ # project tier — readers of this project
|
|
20
24
|
platform-lead.md
|
|
25
|
+
~/.agents/personas/ # cross-project tier — readers who outlive one repository
|
|
21
26
|
founder-operator.md
|
|
22
27
|
```
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
Use the **project tier** when the persona only makes sense next to the work it describes. Use the **cross-project tier** when the reader exists independently of any single repository — the file is then reachable from any working directory, including `~`. Filenames are lowercase and hyphenated and name the role rather than the person.
|
|
30
|
+
|
|
31
|
+
Neither tier is a resource Outfitter resolves; both are ordinary directories of Markdown. A persona is project steering context rather than agent configuration, which is why `outfitter dump` does not carry it. The `persona-review` launcher searches the project tier before the cross-project one, so `--persona platform-lead` picks up a project override of a shared role without renaming anything.
|
|
25
32
|
|
|
26
33
|
## Three ways to consume the same file
|
|
27
34
|
|
|
28
|
-
- **Appended at launch**: `outfitter run persona-reviewer --
|
|
35
|
+
- **Appended at launch**: `outfitter run persona-reviewer --append-prompt docs/personas/platform-lead.md -- …` — the direct run is the underlying interface, and the reviewer adopts the file as its identity for that session only. Pass `--append-prompt` rather than spelling the harness flag yourself after `--`: pi and Claude Code take append-prompt documents through different flags, so a hand-written passthrough only works on the harness it was written for, and fails silently on the other. An agent using the [`persona-review`](https://github.com/ai-outfitter/community-profiles/tree/main/skills/persona-review) skill can drive the same run in the background or synchronously and capture its report in a durable file. See [Persona reviews](./usecases/persona-reviews.md) for the runnable form of both.
|
|
29
36
|
- **Pasted into a web agent**: upload or paste the file unchanged into claude.ai project knowledge or a ChatGPT project as stakeholder context. Same artifact, zero conversion.
|
|
30
37
|
- **Ordinary reading context**: any agent doing product planning, research, or writing can read the file to know who the work is for.
|
|
31
38
|
|
|
39
|
+
## When one file is not enough
|
|
40
|
+
|
|
41
|
+
Start with one file. Reach for a second only when part of an identity varies independently and would otherwise be copied into every persona that shares it — most often an **organization**: sector, scale, regulatory exposure, how the business weighs risk against speed. Three organizations against four roles is seven documents rather than twelve.
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
outfitter run persona-reviewer \
|
|
45
|
+
--append-prompt docs/personas/org.enterprise-insurance.md \
|
|
46
|
+
--append-prompt docs/personas/platform-lead.md \
|
|
47
|
+
-- --print "Review the onboarding flow. @README.md"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Order is meaning: earlier documents establish the context later ones are read against, so organization comes before role, and a named individual comes last. A role document written for composition must not name a sector or an employer — that is exactly what lets it read correctly under any organization.
|
|
51
|
+
|
|
52
|
+
Each file is still ordinary Markdown that reads on its own, names no other file, and leaves no placeholder for one. Splitting for any other reason — to store a schema in filenames, to make files shorter, to mirror a template — is the failure mode the next section describes, and it remains the wrong move.
|
|
53
|
+
|
|
54
|
+
Portability survives intact. Every document uploads unchanged into claude.ai project knowledge or a ChatGPT project; when an identity spans several, upload them in the same order, or `cat org.md role.md > persona.md` and upload the one file. The claim was never _one artifact_ — it is _no conversion step_, and `cat` is not a conversion step.
|
|
55
|
+
|
|
32
56
|
## Why a convention, not a key
|
|
33
57
|
|
|
34
|
-
Modeling personas as their own resource —
|
|
58
|
+
Modeling personas as their own resource — a `personas:` key that Outfitter resolves, merges across layers, validates, and projects — duplicates what an agent already is and grows the surface area of the system. Naming documents at launch is not that: Outfitter resolves nothing, merges nothing, validates nothing, and `outfitter dump` still carries no persona. Order is whatever you typed on the command line, not a precedence rule the system has to define and defend.
|
|
59
|
+
|
|
60
|
+
Keeping personas as "shared agent plus committed Markdown" means a team maintains one review agent and a directory of cheap files, instead of a fleet of near-identical agents. A tenth persona is a tenth file; a second organization is one more file, not a copy of every role.
|
|
35
61
|
|
|
36
|
-
Organization research, interviews, and individual detail are **authoring inputs**, not committed schema: interview from them, keep research notes however you like, but
|
|
62
|
+
Organization research, interviews, and individual detail are **authoring inputs**, not committed schema: interview from them, keep research notes however you like, but what gets committed is Markdown a person can read. Do not invent demographics, income, or biography the research does not support, and never generate an Outfitter agent per persona.
|
|
37
63
|
|
|
38
64
|
## Status
|
|
39
65
|
|
|
40
|
-
Personas ride entirely on existing CLI behavior
|
|
66
|
+
Personas ride entirely on existing CLI behavior — `outfitter run` plus `--append-prompt`, which projects each document through whichever flag the selected harness actually reads — so no `OFTR-*` requirement covers them beyond the composition order in `OFTR-005.3.1`. The reviewer runs as the selected agent; native Pi subagent projection is not a prerequisite. An `.agents`-native persona form (a protocol resource, or a settings layer pointing at persona documents) is a separate, deferred design; the portable file must never depend on it.
|
|
41
67
|
|
|
42
68
|
See [Persona reviews](./usecases/persona-reviews.md) for the worked author → run → paste-anywhere story, and the community catalog's [persona boundary doc](https://github.com/ai-outfitter/community-profiles/blob/main/docs/persona-review.md) for the setup and runtime responsibility split.
|
|
@@ -76,6 +76,8 @@ docs/personas/
|
|
|
76
76
|
founder-operator.md
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
A persona whose reader exists independently of any one repository goes in `~/.agents/personas/` instead, where every working directory can reach it. See [Where personas live](../personas.md#where-personas-live).
|
|
80
|
+
|
|
79
81
|
Prefer generic role archetypes over named individuals. Research and interviews are authoring inputs; commit only the self-contained file, and invent nothing the research does not support.
|
|
80
82
|
|
|
81
83
|
## Run it under Outfitter
|
|
@@ -84,13 +86,12 @@ After `outfitter setup`, launch the shared reviewer directly with the persona ap
|
|
|
84
86
|
|
|
85
87
|
```sh
|
|
86
88
|
mkdir -p docs/persona-reviews
|
|
87
|
-
outfitter run persona-reviewer -- \
|
|
88
|
-
--append-system-prompt docs/personas/platform-lead.md \
|
|
89
|
+
outfitter run persona-reviewer --append-prompt docs/personas/platform-lead.md -- \
|
|
89
90
|
--print "Review the onboarding flow and write the report. @README.md" \
|
|
90
91
|
> docs/persona-reviews/platform-lead-onboarding.md
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
This is the portable interface: it works from the project containing the persona
|
|
94
|
+
This is the portable interface: it works from the project containing the persona, does not assume a particular catalog checkout path, and does not assume a harness — `--append-prompt` projects the document through whichever flag pi or Claude Code actually reads. Repeat it to compose an identity from several documents; see [When one file is not enough](../personas.md#when-one-file-is-not-enough). One shared agent adopts the file as its identity for that session only and writes a first-person, sourced report — evidence cited to the exact page or UI moment, assumptions labeled. The reviewer inherits the caller's configured model; reviews benefit from a strong reasoning model.
|
|
94
95
|
|
|
95
96
|
### Optional orchestration with the skill
|
|
96
97
|
|
|
@@ -98,12 +99,12 @@ Use the [`persona-review`](https://github.com/ai-outfitter/community-profiles/tr
|
|
|
98
99
|
|
|
99
100
|
```sh
|
|
100
101
|
bash skills/persona-review/scripts/persona-review.sh \
|
|
101
|
-
--persona
|
|
102
|
+
--persona platform-lead \
|
|
102
103
|
--report docs/persona-reviews/platform-lead-onboarding.md \
|
|
103
104
|
-- --print "Review the onboarding flow and write the report. @README.md"
|
|
104
105
|
```
|
|
105
106
|
|
|
106
|
-
The reviewer runs directly as the selected agent. This journey does not require Pi's native subagent projection.
|
|
107
|
+
A bare `--persona` name resolves against `docs/personas/`, then `.agents/personas/`, then `~/.agents/personas/`, so the same command works whether the persona is project-local or cross-project; pass a path to name a file directly. The reviewer runs directly as the selected agent. This journey does not require Pi's native subagent projection.
|
|
107
108
|
|
|
108
109
|
## Take the same file to the web
|
|
109
110
|
|
package/package.json
CHANGED