-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagents.mjs
More file actions
144 lines (139 loc) · 5.25 KB
/
Copy pathagents.mjs
File metadata and controls
144 lines (139 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
* Which coding agent runs the AI steps.
*
* WHAT THIS IS FOR
*
* Two steps here hand a prompt to an agent and let it write files: drafting a
* script, and generating the scene HTML for a composition. Both shelled out to
* `claude` with the argv written inline at the call site, twice, identically.
*
* That hardcodes two things that are really one decision — WHICH agent, and
* therefore whose account and whose bill. `claude` runs on a Claude Code
* subscription, which is the right default and is not free.
*
* Pi (https://pi.dev) is the same shape of tool: a local agent harness driving a
* model, with a non-interactive mode, a JSON event stream and an approve flag.
* It is NOT an inference provider — it routes to 15+ of them — so it does not
* reduce cost by itself. It reduces cost by letting the same work run against a
* cheaper model than Claude.
*
* STATUS: THE PI PATH IS A STUB AND HAS NOT BEEN RUN.
*
* The flags below come from Pi's CLI reference, not from a run against a real
* install, and nothing here has produced a script or a scene. It is wired up so
* that trying it is a config change rather than a patch, and left switched off
* so nobody discovers it by having a render fail. Before turning it on, see
* docs/AGENTS.md — the open questions are listed there and they are not
* cosmetic.
*
* WHY A MODULE RATHER THAN AN IF AT EACH CALL SITE
*
* The two call sites drifted once already in the direction that matters: the
* comment explaining `--output-format stream-json` is copied verbatim in both,
* which is what a shared decision looks like just before it stops being shared.
*/
/**
* The agents this app knows how to start.
*
* `bin` must also be in lib/jobs.mjs BINARIES, or the job runner refuses it —
* deliberately, because that allowlist is the thing standing between a prompt
* and an arbitrary process.
*/
export const AGENTS = {
claude: {
id: "claude",
label: "Claude Code · Fable 5",
bin: "claude",
/*
* Studio's work is deliberately long-horizon: inspect several recordings,
* compare their evidence against a script, and leave a reviewable edit.
* Do not quietly inherit whichever Claude Code model happened to be selected
* in a terminal. Every Studio agent run should make the same explicit choice.
*/
model: "claude-fable-5",
/** Whose bill this lands on, said plainly because it is the reason to change it. */
billing: "your Claude Code subscription",
ready: true,
/**
* stream-json, not the default text output.
*
* `claude -p` in text mode prints one blob when it finishes, so a long
* render showed an empty Console for minutes and looked hung. stream-json
* emits an event per step; --verbose is required alongside it. The Studio
* renders those events rather than showing raw NDJSON.
*/
args: (prompt, { additionalDirectories = [] } = {}) => [
"-p",
prompt,
"--model",
"claude-fable-5",
...(additionalDirectories.length ? ["--add-dir", ...additionalDirectories] : []),
"--permission-mode",
"acceptEdits",
"--output-format",
"stream-json",
"--verbose",
],
/** How the Console should read this agent's stdout. */
stream: "claude-stream-json",
},
pi: {
id: "pi",
label: "Pi (unverified)",
bin: "pi",
billing: "whichever provider Pi is configured for",
/*
* Not ready, and that is a claim about testing rather than about Pi.
*
* `ready: false` keeps it out of the default and makes the UI say so. It
* flips to true when someone has run both steps end to end and a scene
* generated this way has actually rendered.
*/
ready: false,
/*
* Mapped from Pi's CLI reference:
*
* claude -p <prompt> → pi -p <prompt>
* claude --permission-mode acceptEdits → pi --approve
* claude --output-format stream-json → pi --mode json
* --verbose
*
* Provider and model are deliberately NOT set here. Pi resolves its own
* configured default, so the choice of model — which is the entire cost
* question — stays with whoever set Pi up rather than being frozen into
* this file by someone who cannot see their account.
*/
args: (prompt) => ["-p", prompt, "--approve", "--mode", "json"],
stream: "pi-json",
},
};
/** The agent used when nothing has chosen one. */
export const DEFAULT_AGENT = "claude";
/**
* Resolve a stored setting to an agent, refusing to guess.
*
* An unknown id falls back to the default rather than throwing: a settings file
* naming an agent this build does not have should degrade to a working render,
* not to a broken app. An agent that is present but not `ready` is only used
* when it was asked for explicitly, which is the point of the flag.
*/
export function agentFor(id) {
const want = AGENTS[String(id ?? "")];
return want ?? AGENTS[DEFAULT_AGENT];
}
/**
* The step this agent runs, in the shape lib/jobs.mjs expects.
*
* One builder, so the two call sites cannot drift again.
*/
export function agentStep(id, { prompt, cwd, label, additionalDirectories }) {
const agent = agentFor(id);
return {
label: label ?? `${agent.id} · ${cwd}`,
bin: agent.bin,
args: agent.args(prompt, { additionalDirectories }),
cwd,
/** Read by the Console to decide how to render this job's output. */
stream: agent.stream,
};
}