@ctrl-spc/cs 0.6.0 → 0.7.1
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.js +70 -5
- package/dist/autostart.js +15 -1
- package/dist/browser.js +386 -0
- package/dist/codebases.js +15 -0
- package/dist/codex-home.js +562 -0
- package/dist/companion.js +9 -12
- package/dist/config.js +23 -0
- package/dist/daemon.js +33 -3
- package/dist/env.js +42 -0
- package/dist/failure-reason.js +98 -0
- package/dist/index.js +1 -1
- package/dist/mcp.js +7627 -298
- package/dist/orchestrator.js +6011 -0
- package/dist/panel3/answer.js +166 -0
- package/dist/panel3/checkout.js +29 -0
- package/dist/panel3/cli.js +83 -0
- package/dist/panel3/client.js +181 -0
- package/dist/panel3/coordinator.js +18 -0
- package/dist/panel3/presence.js +162 -0
- package/dist/panel3/prompt.js +945 -0
- package/dist/panel3/run.js +2516 -0
- package/dist/panel3/say.js +262 -0
- package/dist/panel3/secrets.js +98 -0
- package/dist/panel3/session.js +128 -0
- package/dist/panel3/show.js +997 -0
- package/dist/panel3/spawn.js +565 -0
- package/dist/panel3/tools.js +1906 -0
- package/dist/presence.js +178 -6
- package/dist/win-shell.js +162 -0
- package/dist/work-context.js +1484 -0
- package/dist/workflows.js +68 -0
- package/package.json +3 -2
|
@@ -0,0 +1,945 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ═══ AGENT PANEL v3: what an agent is told. ═══
|
|
3
|
+
*
|
|
4
|
+
* THIS FILE BELONGS TO AGENT PANEL v3. Nothing outside `src/panel3/` may
|
|
5
|
+
* import it.
|
|
6
|
+
*
|
|
7
|
+
* ---------------------------------------------------------------------------
|
|
8
|
+
* WHY THE WORDS LIVE HERE AND NOT IN THE DAEMON.
|
|
9
|
+
*
|
|
10
|
+
* `run.ts` is about leases, processes and rows: what may be taken, what is
|
|
11
|
+
* written before a spawn, what happens when a machine dies. This is product
|
|
12
|
+
* copy — it decides the words a person ends up reading — and the rules that
|
|
13
|
+
* govern it are not the rules that govern a lease. Kept in the daemon, the
|
|
14
|
+
* paragraph explaining why a reply may not say "dispatch" would sit three
|
|
15
|
+
* hundred lines from the sentence it is about.
|
|
16
|
+
*
|
|
17
|
+
* ---------------------------------------------------------------------------
|
|
18
|
+
* ═══ THE STRUCTURE IS ONE SENTENCE PLUS THE TOOL DESCRIPTIONS. NOTHING ELSE. ═══
|
|
19
|
+
*
|
|
20
|
+
* ux.md: "The structure v3 provides is exactly two things: a core responsibility
|
|
21
|
+
* per level, and the tools that level can reach. Everything else about how an
|
|
22
|
+
* agent behaves is left loose on purpose. There is no procedure, no required
|
|
23
|
+
* sequence, no router."
|
|
24
|
+
*
|
|
25
|
+
* So there is deliberately NO order of tool calls here, no mapping from a kind
|
|
26
|
+
* of request to the tool that answers it, and no instruction to read before
|
|
27
|
+
* writing. That flexibility is the thing the whole design exists to keep, and a
|
|
28
|
+
* prompt is exactly where it gets quietly spent.
|
|
29
|
+
*
|
|
30
|
+
* WHAT IS HERE BESIDES THE ONE SENTENCE IS NOT PROCEDURE. It is the facts of the
|
|
31
|
+
* situation an agent cannot discover for itself: that a person is reading the
|
|
32
|
+
* reply, that a person is watching while it works, that it cannot see code, and
|
|
33
|
+
* what that person has already said. None of those is a step.
|
|
34
|
+
*
|
|
35
|
+
* AND IT DOES NOT LIST WHAT THE RECORD HOLDS. An earlier draft enumerated
|
|
36
|
+
* projects, work items and artifacts here, which is the job ux.md gives to the
|
|
37
|
+
* tool's own description "because they travel with the tool" — and this is the
|
|
38
|
+
* one place in the file that would silently go stale the first time a tool is
|
|
39
|
+
* added and nobody thinks to look at a prompt.
|
|
40
|
+
*
|
|
41
|
+
* ---------------------------------------------------------------------------
|
|
42
|
+
* ═══ THE HOUSE VOCABULARY IS BANNED IN ANYTHING A PERSON READS. ═══
|
|
43
|
+
*
|
|
44
|
+
* ux.md: "The user perceives one voice, because no agent uses house vocabulary
|
|
45
|
+
* and the brief is the same at every level. Nothing in a reply reveals which
|
|
46
|
+
* level wrote it."
|
|
47
|
+
*
|
|
48
|
+
* The person asked about their own work. Agent, run, dispatch, level, card,
|
|
49
|
+
* turn, coordinator, orchestrator and worker all describe how the product went
|
|
50
|
+
* about answering them, which is not what they asked and not a vocabulary they
|
|
51
|
+
* should have to learn to read their own board. The ban is stated as a ban
|
|
52
|
+
* because it is not obvious: every one of those words is the natural one to
|
|
53
|
+
* reach for from inside, which is exactly why it leaks.
|
|
54
|
+
*
|
|
55
|
+
* ---------------------------------------------------------------------------
|
|
56
|
+
* ═══ WHAT IS MADE GOES IN THE THING. THE REPLY POINTS AT IT. ═══
|
|
57
|
+
*
|
|
58
|
+
* ux.md: "Output goes into the code or into a CTRL+SPC object, never into the
|
|
59
|
+
* chat. The chat carries pointers."
|
|
60
|
+
*
|
|
61
|
+
* THIS IS A FACT ABOUT THE SITUATION, WHICH IS WHY IT IS HERE AND NOT PROCEDURE.
|
|
62
|
+
* An agent writing into a plain chat window has no choice: prose is the only
|
|
63
|
+
* place its work can land, so restating what it produced is the only way the
|
|
64
|
+
* person sees it. Here it is the opposite, and the agent cannot tell from where
|
|
65
|
+
* it sits: every epic, item and document it creates is recorded against the
|
|
66
|
+
* conversation as it is made, and is already in front of the person as something
|
|
67
|
+
* they can open. Nothing about that is discoverable from a tool description,
|
|
68
|
+
* which says what a tool does and not what the screen already shows.
|
|
69
|
+
*
|
|
70
|
+
* SO THE FAILURE IS SPECIFIC, AND IT WAS MEASURED BEFORE THIS PARAGRAPH EXISTED.
|
|
71
|
+
* Asked for an epic with two items under it, level 1 created all three correctly
|
|
72
|
+
* and then wrote a summary of each item's description back into the reply. Both
|
|
73
|
+
* copies were real; only one of them is the one the person can edit, and the
|
|
74
|
+
* other starts going stale the moment they do. The rule is not about length.
|
|
75
|
+
*
|
|
76
|
+
* ---------------------------------------------------------------------------
|
|
77
|
+
* ═══ IT MUST NOT IMPLY A CODE TOOL, AND IT MUST NAME THE HONEST MOVE. ═══
|
|
78
|
+
*
|
|
79
|
+
* ux.md: "level 1 is never granted a code tool, so it cannot become the thing
|
|
80
|
+
* that does the work." That is already true mechanically — `spawn.ts` starts
|
|
81
|
+
* level 1 with `--tools ''` and the tools server serves no code tool at any
|
|
82
|
+
* level — so this file's job is not to enforce it. It is to stop the absence
|
|
83
|
+
* being expensive.
|
|
84
|
+
*
|
|
85
|
+
* THE FAILURE THAT COSTS THE USER IS NOT THE ABSENCE. It is an agent that
|
|
86
|
+
* answers a question about code from what it can infer and writes the answer in
|
|
87
|
+
* the voice of something that looked. The user cannot tell that apart from a
|
|
88
|
+
* real answer, and one of those makes every other answer untrustworthy. So the
|
|
89
|
+
* absence is stated plainly as a fact, and the honest move — send somebody who
|
|
90
|
+
* can look — is named beside it.
|
|
91
|
+
*
|
|
92
|
+
* ---------------------------------------------------------------------------
|
|
93
|
+
* ═══ A DISPATCHED WORK BRIEF HOLDS THE JOB, NOT THE CONVERSATION. ═══
|
|
94
|
+
*
|
|
95
|
+
* ux.md: "a fresh subagent needs its task, the interfaces it touches, and the
|
|
96
|
+
* global constraints. Nothing else", with the measured cost of the alternative —
|
|
97
|
+
* "a real session's dispatch hit 42k chars of which 99% was pasted history."
|
|
98
|
+
*
|
|
99
|
+
* So `workBrief` carries exactly two things written by judgement, the
|
|
100
|
+
* responsibility and the boundary, and the facts the product attaches. It
|
|
101
|
+
* carries NO messages, no earlier replies and no history. A level 3 worker gets
|
|
102
|
+
* that brief directly. A level 2 owner gets it inside `ownerActivationPrompt`,
|
|
103
|
+
* beside the visible conversation read from the card rather than a launcher
|
|
104
|
+
* retelling of it.
|
|
105
|
+
*
|
|
106
|
+
* ═══ AND IT IS THE SAME VOICE, WHICH IS WHY THE OUTBOUND RULES ARE SHARED. ═══
|
|
107
|
+
* ux.md: "the brief is the same at every level. Nothing in a reply reveals which
|
|
108
|
+
* level wrote it." Two copies of the ban on house vocabulary would be two copies
|
|
109
|
+
* that drift, and the drift is invisible: it shows up as one reply in ten
|
|
110
|
+
* mentioning a dispatch, from the level nobody re-read.
|
|
111
|
+
*
|
|
112
|
+
* ---------------------------------------------------------------------------
|
|
113
|
+
* ═══ WHAT SURVIVES A RESPAWN IS HANDED OVER AS THE INPUT, NOT OFFERED AS A
|
|
114
|
+
* TOOL. ═══
|
|
115
|
+
*
|
|
116
|
+
* ux.md: "The brief and the report are in the spawn's prompt, not behind a tool
|
|
117
|
+
* call the agent has to choose to make. An agent cannot fail to read its own
|
|
118
|
+
* input. This is the difference between a guarantee and a hope."
|
|
119
|
+
*
|
|
120
|
+
* `get_my_brief_and_report` still exists, for re-reading mid-run. It is not the
|
|
121
|
+
* delivery mechanism, and this file is: `resumePrompt` puts the brief and the
|
|
122
|
+
* report in front of an agent before it has decided anything.
|
|
123
|
+
*
|
|
124
|
+
* ═══ AND WHAT IT IS TOLD ABOUT WRITING ONE IS A FACT, NOT A PROCEDURE. ═══ The
|
|
125
|
+
* header above bans procedure here, and the ban holds: nothing below says when
|
|
126
|
+
* to write, in what order, or what to do first. What an agent CANNOT discover
|
|
127
|
+
* for itself is that it can be stopped and started again, and that the only
|
|
128
|
+
* thing that comes back with it is what it wrote down. Given that fact, writing
|
|
129
|
+
* it down is the obvious move rather than an instruction to comply with — and
|
|
130
|
+
* how to write it travels with `write_report`'s own description, where ux.md
|
|
131
|
+
* says the how belongs.
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* ═══ WHAT EVERY LEVEL IS TOLD ABOUT THE THING IT SENDS BACK. ═══
|
|
135
|
+
*
|
|
136
|
+
* One owner for the rules that decide how a reply reads, because the person is
|
|
137
|
+
* meant to hear one voice however many agents were involved. The one line that
|
|
138
|
+
* differs between levels is what an agent PRODUCED, and it differs for a real
|
|
139
|
+
* reason: level 1 makes objects the person can already open, and a dispatched
|
|
140
|
+
* agent changes code they cannot see from here.
|
|
141
|
+
*/
|
|
142
|
+
const whatYouSendBack = (produced) => [
|
|
143
|
+
'WHAT YOU SEND BACK',
|
|
144
|
+
'Your reply is the whole of what they read, so write it to them, about their own work.',
|
|
145
|
+
'- By default, be very brief: use the shortest accurate reply that lets them understand the',
|
|
146
|
+
' outcome, decide, or unblock the work.',
|
|
147
|
+
'- A routine update is absent unless it is useful, and then it is one short sentence or phrase.',
|
|
148
|
+
'- An ordinary completion starts with the outcome, includes only the result they need, and is one',
|
|
149
|
+
' or two short sentences; never add a third.',
|
|
150
|
+
'- If the work cannot continue without a decision or input only the person can supply, you MUST',
|
|
151
|
+
' call `ask_question`. This includes asking them to choose between options. That tool is the only',
|
|
152
|
+
' person-question path: writing the question in an ordinary reply or completing the card with it',
|
|
153
|
+
' is wrong. After the call, stop without repeating the question in an ordinary reply. Use only',
|
|
154
|
+
' the minimum context they need to answer safely.',
|
|
155
|
+
'- Do not copy explanations, logs, prompts, internal reasoning, test inventories or internal',
|
|
156
|
+
' orchestration into an ordinary reply.',
|
|
157
|
+
'- If they explicitly ask for detail, give it for that reply, then return to the brief default.',
|
|
158
|
+
' Accuracy and truthful failures are never shortened away.',
|
|
159
|
+
'- Name the real things: the actual items, projects and people, by the names they have.',
|
|
160
|
+
...produced,
|
|
161
|
+
'- Never name the machinery. Words like agent, run, dispatch, level, card, turn, coordinator,',
|
|
162
|
+
' orchestrator and worker are ours and not theirs, and none of them belongs in something a',
|
|
163
|
+
' person reads. The same goes for the line about what you are doing.',
|
|
164
|
+
'',
|
|
165
|
+
'Reply with the text they should read, and nothing else.',
|
|
166
|
+
];
|
|
167
|
+
/**
|
|
168
|
+
* ═══ WHAT A WORKER SENDS BACK, WHICH DOES NOT GO TO THE PERSON. ═══
|
|
169
|
+
*
|
|
170
|
+
* ux.md: "Each worker writes what it found into the record, and the level 2 agent
|
|
171
|
+
* reads them back", and the reply that reaches the person is written by that
|
|
172
|
+
* agent, once, covering all of them. So a worker's own words go to whoever sent
|
|
173
|
+
* it and nowhere else — `panel3_answer` puts them in its report rather than on
|
|
174
|
+
* the card — and it is TOLD so, because from where it sits the two are
|
|
175
|
+
* indistinguishable and the difference decides how it writes.
|
|
176
|
+
*
|
|
177
|
+
* THE BAN ON HOUSE VOCABULARY STAYS, for a reason it did not have at the other
|
|
178
|
+
* levels: what a worker writes is read by the agent that has to answer the
|
|
179
|
+
* person, and the phrases most likely to be quoted straight through are the ones
|
|
180
|
+
* that summarise. A sentence about dispatching a worker is one paste away from
|
|
181
|
+
* something a person reads.
|
|
182
|
+
*/
|
|
183
|
+
const whatAWorkerSendsBack = () => [
|
|
184
|
+
'WHAT YOU SEND BACK, AND WHO READS IT',
|
|
185
|
+
'What you write at the end goes to whoever sent you, and to nobody else. They asked several',
|
|
186
|
+
'people for several pieces and they put the answer together out of them, so nothing you write',
|
|
187
|
+
'reaches the person except through them.',
|
|
188
|
+
'- Write it to somebody who has not seen any of this. They know what they asked you for and',
|
|
189
|
+
' nothing about what you found, so say what you found or changed and where.',
|
|
190
|
+
'- Be complete about the things they could not know, and brief about everything else. Do not',
|
|
191
|
+
' paste the code back: they can read it.',
|
|
192
|
+
'- If part of what you were given turned out not to be yours, or not to be there, say so plainly',
|
|
193
|
+
' rather than working around it quietly.',
|
|
194
|
+
'- Never name the machinery. Words like agent, run, dispatch, level, card, turn, coordinator,',
|
|
195
|
+
' orchestrator and worker are ours, and what you write may go on to a person word for word.',
|
|
196
|
+
'',
|
|
197
|
+
'Reply with what they need to know, and nothing else.',
|
|
198
|
+
];
|
|
199
|
+
/** The one line about being watched, which is true at every level: the activity
|
|
200
|
+
* line is the only sign a person has that anything is happening. */
|
|
201
|
+
const WHILE_YOU_ARE_WORKING = [
|
|
202
|
+
'WHILE YOU ARE WORKING',
|
|
203
|
+
'One line saying what you are doing is on their screen for as long as you are here, and it is',
|
|
204
|
+
'the only sign that anything is happening at all. Say what you ARE doing, never what you are',
|
|
205
|
+
'about to do: an intention that then falls through leaves them watching a sentence that was',
|
|
206
|
+
'never true.',
|
|
207
|
+
];
|
|
208
|
+
/**
|
|
209
|
+
* ═══ WHAT THE PERSON ATTACHED, AS A FACT AND NOTHING MORE. ═══
|
|
210
|
+
*
|
|
211
|
+
* plan-work-items.md constraint 5: "no agent is asked in prose to go and find
|
|
212
|
+
* what a card is about." So the LIST is one sentence saying the attaching
|
|
213
|
+
* happened and to whom it belongs, and nothing more. What it cannot discover on
|
|
214
|
+
* its own is that these specific ids are the ones the person meant, and that is
|
|
215
|
+
* the fact the list states.
|
|
216
|
+
*
|
|
217
|
+
* ═══ EACH KIND THEN GETS ONE SENTENCE SAYING WHAT THAT WORD MEANS. ═══ Three
|
|
218
|
+
* of them now, and the work item's is the one added last (the
|
|
219
|
+
* `work-lands-on-the-item-6` slice, off ux.md's "before an agent begins working
|
|
220
|
+
* on it, it must read the description, and any artifacts"). An earlier draft of
|
|
221
|
+
* this comment said none of them may tell the agent what to DO; that was true
|
|
222
|
+
* when a work item was only a subject. It is not the same rule as constraint 5,
|
|
223
|
+
* which forbids sending an agent LOOKING for which item a card is about. The id
|
|
224
|
+
* is handed over here by the product; reading the item it was handed is not
|
|
225
|
+
* finding it.
|
|
226
|
+
*
|
|
227
|
+
* SHARED between `levelOnePrompt` and `workBrief` for the same reason
|
|
228
|
+
* `whatYouSendBack` is: one wording for "what was attached", never two that
|
|
229
|
+
* drift.
|
|
230
|
+
*/
|
|
231
|
+
const whatWasAttached = (attachments) => {
|
|
232
|
+
const codebases = attachments.filter((attachment) => attachment.startsWith('codebase '));
|
|
233
|
+
const personAttachments = attachments.filter((attachment) => !attachment.startsWith('codebase '));
|
|
234
|
+
const personAttachmentLines = personAttachments.length === 0 ? [] : [
|
|
235
|
+
'',
|
|
236
|
+
'WHAT THE PERSON ATTACHED',
|
|
237
|
+
'They attached these when they sent this, so what is asked belongs against them.',
|
|
238
|
+
...personAttachments,
|
|
239
|
+
/* ═══ READ BEFORE YOU DECIDE, WHICH IS THE WHOLE OF THIS SLICE. ═══ ux.md:
|
|
240
|
+
"Before an agent begins working on it, it must read the description, and
|
|
241
|
+
any artifacts, and intelligently decide what to do next." An agent that
|
|
242
|
+
decides first and reads afterwards writes a second plan onto an item that
|
|
243
|
+
already carries one, against a description it never opened.
|
|
244
|
+
|
|
245
|
+
═══ AND IT LIVES HERE, NOT UNDER `THE WORK ITEM THIS IS ABOUT`. ═══ That
|
|
246
|
+
header is the obvious home and it is the wrong one: `workItemId` comes
|
|
247
|
+
only from `dispatch`'s OPTIONAL `work_item_id`, which no prompt asks a
|
|
248
|
+
launcher to supply, and which the launcher MUST NOT supply when several
|
|
249
|
+
items are attached (the selection is the person's, below). So at exactly
|
|
250
|
+
the moment reading the items decides the answer, that header is absent.
|
|
251
|
+
This block is written from the person's own attachment rows, travels to
|
|
252
|
+
every dispatch on the card, and is shared by `levelOnePrompt` and
|
|
253
|
+
`workBrief`, so the sentence reaches all three levels. The tools it needs
|
|
254
|
+
— `get_work_item`, `list_artifacts`, `get_artifact` — are `levels: ALL`.
|
|
255
|
+
|
|
256
|
+
NO TOOL NAMES IN IT. `get_work_item` already says it returns "its
|
|
257
|
+
description, status, placement, and the artifacts on it", and
|
|
258
|
+
`list_artifacts` already ends "Use get_artifact to read one". The prompt
|
|
259
|
+
carries the obligation; the tool descriptions carry the mechanics. */
|
|
260
|
+
'A `work_item` is what the work is about. Read its description and the artifacts already on it',
|
|
261
|
+
'before you decide anything.',
|
|
262
|
+
/* ═══ THE KIND THAT IS A METHOD RATHER THAN A SUBJECT. ═══ A skill is not
|
|
263
|
+
one more thing the request belongs against: it is a method a person
|
|
264
|
+
chose, and an agent that read it as a subject would quote it back and
|
|
265
|
+
carry on doing the work its own way. That is the fact it cannot discover
|
|
266
|
+
from the line above, so it is the sentence added, and the tool's own
|
|
267
|
+
description carries the rest.
|
|
268
|
+
|
|
269
|
+
═══ SAID WHENEVER ANYTHING IS ATTACHED, WHICH IS A CHOICE AND NOT AN
|
|
270
|
+
ACCIDENT. ═══ Both callers hold the rows, `kind` and all, so narrowing
|
|
271
|
+
this to cards that actually have a skill is available and was not
|
|
272
|
+
taken. The sentence is a DEFINITION of what that word means in the list
|
|
273
|
+
above, not an instruction to go and do something: a card with only a
|
|
274
|
+
work item attached reads past it, exactly as it reads past any other
|
|
275
|
+
word it has no use for. What narrowing would buy is one line on some
|
|
276
|
+
cards, against a second rule to keep true in a second place.
|
|
277
|
+
|
|
278
|
+
AND THE WORST CASE IS CHEAP AND HONEST. An agent that called
|
|
279
|
+
`get_skill` with a work item's id gets a failure through `only`, which
|
|
280
|
+
says there is no such row, rather than somebody else's document. */
|
|
281
|
+
'A `skill` is a document written to be followed. Call `get_skill` with its id, read all of it,',
|
|
282
|
+
'and do what it says for the rest of this work.',
|
|
283
|
+
/* ═══ THE ONE SENTENCE THAT IS TRUE AT EVERY LEVEL, WHICH IS WHY IT IS ONE
|
|
284
|
+
SENTENCE. ═══ `get_credential` is levels 2 and 3 only (Lane, 2026-08-19:
|
|
285
|
+
level 1 reads the NAME, the levels that do the work read the value), and
|
|
286
|
+
this wording is shared by `levelOnePrompt` and `workBrief` both. So it
|
|
287
|
+
says where the name is and who may read the value, rather than telling
|
|
288
|
+
anybody to call a tool: a coordinator reading it learns to write "use the
|
|
289
|
+
Stripe test key" into what it dispatches, and a worker reading it learns
|
|
290
|
+
there is a tool and an id to call it with. A "call get_credential"
|
|
291
|
+
sentence would be an instruction level 1 cannot follow, in level 1's own
|
|
292
|
+
prompt. */
|
|
293
|
+
'A `credential` is a secret the person authorised for this work. Its name is above; the value',
|
|
294
|
+
'is readable only by the agents doing the work, with `get_credential`.',
|
|
295
|
+
/* ═══ THE KIND THAT IS A SEQUENCE RATHER THAN A METHOD OR A SUBJECT. ═══ A
|
|
296
|
+
workflow says what happens in what order, and the fact an agent cannot
|
|
297
|
+
discover from the line above is that ONE of the three levels is the one
|
|
298
|
+
that reads it. So this names the ROLE rather than instructing anybody,
|
|
299
|
+
following the credential sentence's shape of naming a tool the reader may
|
|
300
|
+
not hold: `get_workflow` is level 2's alone, and `levelOnePrompt` already
|
|
301
|
+
calls the level 2 agent "the owner", so the word lands at every level this
|
|
302
|
+
one sentence reaches.
|
|
303
|
+
|
|
304
|
+
AND IT SAYS NOTHING ABOUT WORKING STAGES ONE AT A TIME. That rule
|
|
305
|
+
overrides a default written in `workBrief`'s own IF YOU SPLIT IT block, so
|
|
306
|
+
it lives inside that block and nowhere else. A second copy here would be a
|
|
307
|
+
second place for it to drift. */
|
|
308
|
+
'A `workflow` is the process this work follows: stages, in an order it gives. The owner of this',
|
|
309
|
+
'conversation is the one who reads it, with `get_workflow`.',
|
|
310
|
+
];
|
|
311
|
+
return [
|
|
312
|
+
...(codebases.length === 0 ? [] : [
|
|
313
|
+
'',
|
|
314
|
+
'THE CODEBASES THIS CARD TOUCHES',
|
|
315
|
+
...codebases,
|
|
316
|
+
'For a dispatched piece, its one codebase is named separately below. The other selected',
|
|
317
|
+
'codebases are separate work, not missing folders, so do not look for or report on them.',
|
|
318
|
+
]),
|
|
319
|
+
...personAttachmentLines,
|
|
320
|
+
];
|
|
321
|
+
};
|
|
322
|
+
const projectCodebases = (codebases) => {
|
|
323
|
+
if (codebases === null)
|
|
324
|
+
return [];
|
|
325
|
+
return [
|
|
326
|
+
'',
|
|
327
|
+
'THE CODEBASES REGISTERED ON THIS PROJECT',
|
|
328
|
+
...(codebases.length === 0
|
|
329
|
+
? [
|
|
330
|
+
'There are none. If this needs code, ask the person to add a codebase to this project and',
|
|
331
|
+
'stop. Work that does not need code can continue.',
|
|
332
|
+
]
|
|
333
|
+
: [
|
|
334
|
+
...codebases.map((codebase) => (`${codebase.name} — id ${codebase.id} — ${codebase.identity} — `
|
|
335
|
+
+ (codebase.located ? 'located on this machine' : 'not located on this machine'))),
|
|
336
|
+
'Choose the codebase that contains the work and include its id when you send somebody.',
|
|
337
|
+
'If several codebases are needed, send one person per codebase. If the request does not make',
|
|
338
|
+
'the choice clear, call `ask_question` once with `answer_mode` set to `multi_select`, put the',
|
|
339
|
+
'plausible codebase names in `options`, and stop. Do not write a question as your reply.',
|
|
340
|
+
]),
|
|
341
|
+
];
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* The card's name and the messages waiting on it, as one prompt for a level 1
|
|
345
|
+
* agent. It carries no path, no id and nothing about this machine, so nothing in
|
|
346
|
+
* it can breach the rule that an absolute local path never reaches the database
|
|
347
|
+
* — which matters because the daemon writes it to `panel3_runs.brief`.
|
|
348
|
+
*
|
|
349
|
+
* ═══ `cardTitle` IS A NAME, NOT THE QUESTION, AND ITS LABEL SAYS SO. ═══ `say`
|
|
350
|
+
* fixes it once, as the truncated first line of the FIRST message, and never
|
|
351
|
+
* changes it. Labelled as what is being asked, a second turn would tell the
|
|
352
|
+
* agent the current question is a stale half-sentence and then hand it the real
|
|
353
|
+
* ones underneath — so the label names the conversation, which is all the title
|
|
354
|
+
* has ever been.
|
|
355
|
+
*
|
|
356
|
+
* ═══ `produced` IS LEVEL 1's REPORT, AND IT IS HERE FOR THE SAME REASON THE
|
|
357
|
+
* REPORT IS IN A RESPAWN'S PROMPT. ═══
|
|
358
|
+
*
|
|
359
|
+
* Every turn on a card is answered by a fresh level 1 agent that remembers
|
|
360
|
+
* nothing, and the thing it most needs from the last one is WHAT THAT ONE MADE.
|
|
361
|
+
* Measured before this existed, and it is user-visible: asked to add items to an
|
|
362
|
+
* epic it had created a turn earlier, level 1 could not find the epic — nothing
|
|
363
|
+
* served a card's own receipts and there is no `list_epics` — so it created two
|
|
364
|
+
* items under no epic at all. "Add three more to that epic" simply failed.
|
|
365
|
+
*
|
|
366
|
+
* A run's report and a card's receipts are the same instrument at the two
|
|
367
|
+
* levels: what is already done, carried across a death. So they arrive the same
|
|
368
|
+
* way, in the prompt rather than behind a tool call, and for the same stated
|
|
369
|
+
* reason — an agent cannot fail to read its own input.
|
|
370
|
+
*
|
|
371
|
+
* THE NAMES IN IT ARE READ NOW, NOT WHEN THE RECEIPT WAS WRITTEN, which is
|
|
372
|
+
* `show.ts`'s rule and its bug: a label written at creation and printed after a
|
|
373
|
+
* rename names a different, real object. The caller resolves them.
|
|
374
|
+
*/
|
|
375
|
+
export function levelOnePrompt(cardTitle, messages, produced, attachments = [], codebases = null) {
|
|
376
|
+
return [
|
|
377
|
+
'You are the short-lived launcher for one CTRL+SPC conversation. You do not do the work and',
|
|
378
|
+
'you do not answer the person. Your whole job is to clarify the destination once if you truly',
|
|
379
|
+
'cannot choose it, or launch exactly one owner and then exit.',
|
|
380
|
+
'',
|
|
381
|
+
'WHAT YOU CAN SEE',
|
|
382
|
+
'Your tools read the record. Read as much of it as the question needs.',
|
|
383
|
+
'If they ask what is happening, look rather than guess: everything already under way stays exactly',
|
|
384
|
+
'where it is while you read, and nothing about being asked slows it down.',
|
|
385
|
+
'',
|
|
386
|
+
'WHAT YOU CANNOT SEE',
|
|
387
|
+
'You have no way to open a file, search a codebase or run anything. That is deliberate and it',
|
|
388
|
+
'is not a fault to apologise for. NEVER guess at what the code does, and never write as though',
|
|
389
|
+
'you had looked: they cannot tell a real answer from a plausible one, so one invented answer',
|
|
390
|
+
'makes every true one worthless.',
|
|
391
|
+
'',
|
|
392
|
+
'WHAT YOU CAN SEND SOMEBODY TO DO',
|
|
393
|
+
'Launch one owner for the conversation. Give it the complete responsibility and boundary. If',
|
|
394
|
+
'the request belongs to a registered codebase, name it. If it is record-only work, launch it',
|
|
395
|
+
'without a codebase. The owner talks to the person and may send workers of its own.',
|
|
396
|
+
'Your only question is a destination or codebase you truly cannot choose. NEVER ask the person',
|
|
397
|
+
'to make a work or product decision. When one registered codebase clearly fits, launch its owner',
|
|
398
|
+
'even if the work itself needs the person to choose between options; the owner asks that question.',
|
|
399
|
+
...projectCodebases(codebases),
|
|
400
|
+
'',
|
|
401
|
+
...WHILE_YOU_ARE_WORKING,
|
|
402
|
+
'',
|
|
403
|
+
'WHAT THIS CONVERSATION IS CALLED',
|
|
404
|
+
cardTitle,
|
|
405
|
+
'',
|
|
406
|
+
'WHAT HAS ALREADY BEEN MADE HERE',
|
|
407
|
+
'Everything this conversation has produced so far, whatever it was that made it, with the id to',
|
|
408
|
+
'use for each. Anything they call "that epic" or "the item you made" is in this list. Put new',
|
|
409
|
+
'work inside what is already there rather than making a second one that means the same thing.',
|
|
410
|
+
...(produced.length === 0 ? ['Nothing has been made here yet.'] : produced),
|
|
411
|
+
...whatWasAttached(attachments),
|
|
412
|
+
'',
|
|
413
|
+
'WHAT THEY HAVE SAID THAT NOBODY HAS ANSWERED YET, OLDEST FIRST.',
|
|
414
|
+
'They are one sequence, and a later one may correct an earlier one.',
|
|
415
|
+
'',
|
|
416
|
+
...messages.map((body, i) => `${i + 1}. ${body}`),
|
|
417
|
+
'',
|
|
418
|
+
'AFTER YOU LAUNCH THE OWNER, EXIT IMMEDIATELY. Do not write a reply, wait for it, summarize what',
|
|
419
|
+
'it will do, or ask another question. CTRL+SPC publishes no stdout from this launcher.',
|
|
420
|
+
].join('\n');
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* What an agent sent to do a piece of work is told: its own job, the facts of
|
|
424
|
+
* where it is, and nothing about the conversation it came out of.
|
|
425
|
+
*
|
|
426
|
+
* ═══ THIS IS THE IMMUTABLE BRIEF. ═══ `panel3_runs.brief` stores it exactly.
|
|
427
|
+
* A level 3 process is started with it directly; a level 2 owner's activation
|
|
428
|
+
* prompt carries it verbatim with the live conversation around it.
|
|
429
|
+
*
|
|
430
|
+
* ═══ AND IT CARRIES NO PATH, WHICH IS CONSTRAINT 6. ═══ The working copy is
|
|
431
|
+
* the directory the process is started in, so it never has to be named — and
|
|
432
|
+
* because this text is stored, naming it would put an absolute filesystem path
|
|
433
|
+
* in the database. The same rule is passed on to the agent for what it writes,
|
|
434
|
+
* because its report and its reply are stored too.
|
|
435
|
+
*
|
|
436
|
+
* ═══ AND WHAT THE CARD HAS ATTACHED TRAVELS WITH EVERY DISPATCH ON IT, NOT
|
|
437
|
+
* JUST THE FIRST. ═══ `workItemId` is a single item the DISPATCHING agent
|
|
438
|
+
* chose to name for this one piece of work; `attachments` is the set the
|
|
439
|
+
* PERSON pointed the whole card at, and level 1 did not choose it and cannot
|
|
440
|
+
* un-choose it for whoever it sends. Without it, only the coordinator that
|
|
441
|
+
* happened to be spawned first would ever see what the conversation is about.
|
|
442
|
+
* Every child it starts, and every grandchild THEY start, would be working
|
|
443
|
+
* from nothing but a responsibility sentence somebody else wrote down.
|
|
444
|
+
*/
|
|
445
|
+
export function workBrief(level, responsibility, boundary, workItemId, attachments = [], codebase) {
|
|
446
|
+
/* ═══ ONLY WHEN A WORKFLOW IS ACTUALLY ON THE CARD. ═══ An owner is not
|
|
447
|
+
instructed about a process that is not there, which is the rule
|
|
448
|
+
`personAttachmentLines` already keeps by rendering nothing when nothing is
|
|
449
|
+
attached. The same prefix `whatWasAttached` partitions on, since both read
|
|
450
|
+
the one line `attachmentLine` writes. */
|
|
451
|
+
const hasWorkflow = attachments.some((attachment) => attachment.startsWith('workflow '));
|
|
452
|
+
return [
|
|
453
|
+
...(level === 2
|
|
454
|
+
? [
|
|
455
|
+
'You see one thing a person asked for through to done. Keep the whole picture and stay',
|
|
456
|
+
'available to the person while the work moves.',
|
|
457
|
+
'',
|
|
458
|
+
/* ═══ THE FIRST THING, EVERY TIME, AND IT IS NOT THE REPLY. ═══ Nothing
|
|
459
|
+
an owner writes reaches the person until its process exits, and a turn
|
|
460
|
+
that exits by asking writes no message at all. So on the walk of
|
|
461
|
+
2026-08-24 "Write a plan for this item" was silent for two and a half
|
|
462
|
+
minutes and then said a question, twice over, and Lane's requirement
|
|
463
|
+
is that every message he sends is acknowledged. `say` is the only
|
|
464
|
+
thing that can do that, because an acknowledgement cannot be an exit.
|
|
465
|
+
WRITTEN AS THE FIRST INSTRUCTION IN THE BRANCH because it is the first
|
|
466
|
+
act, and a rule about what to do first that arrives after four
|
|
467
|
+
paragraphs about the work is read after the work has started. */
|
|
468
|
+
'FIRST, SAY WHAT YOU ARE GOING TO DO',
|
|
469
|
+
'They have seen their own message and nothing else. Before you start, call `say` with one or',
|
|
470
|
+
'two sentences: what you are about to do about what they asked, and what they will get back.',
|
|
471
|
+
'Do this every time you are started with something new from them, including the very first',
|
|
472
|
+
'time, and before any reading that takes a while. It does not end your turn and it is not',
|
|
473
|
+
'your reply; you carry on straight afterwards.',
|
|
474
|
+
'',
|
|
475
|
+
'TRACEABILITY BEFORE A CODEBASE FILE CHANGES',
|
|
476
|
+
'A codebase-file change means ANY file change: source, config, docs, tests, migrations,',
|
|
477
|
+
'generated inputs, and assets all count. "Non-code" means no codebase file changes at all.',
|
|
478
|
+
'When this work has a Work Item and will change a codebase file, you may investigate',
|
|
479
|
+
'read-only first. Any investigation you send must explicitly forbid file edits. Create one',
|
|
480
|
+
'useful `analysis` or `plan` artifact on the exact Work Item, carrying the document the work',
|
|
481
|
+
'is missing: a spec, a PRD, user stories or a repro is its CONTENT, not a second artifact.',
|
|
482
|
+
'For a bug: what reproduces it, the behaviour it has now, the behaviour it should have, the',
|
|
483
|
+
'cause and the correction. For something new: what a person will be able to do, and how you',
|
|
484
|
+
'would know it works.',
|
|
485
|
+
'If several Work Items were supplied, one is selected only when the person identifies it by',
|
|
486
|
+
'name, id, or an unambiguous reference in a message or answer. That selection stays in force',
|
|
487
|
+
'unless the person changes it. An attachment label, its order, similarity, or which seems most',
|
|
488
|
+
'likely is NEVER a selection by itself; the person explicitly referring to that label IS a',
|
|
489
|
+
'selection.',
|
|
490
|
+
'Until one is selected, do not',
|
|
491
|
+
'call `update_work_item`, create or update an artifact, dispatch work, or change any file.',
|
|
492
|
+
'Call `ask_question` once with no `work_item_id`, `answer_mode: single_select`, and the exact',
|
|
493
|
+
'attached Work Item names as options, and then stop.',
|
|
494
|
+
'Present the artifact with `ask_question`: name both `work_item_id` and',
|
|
495
|
+
'`related_artifact_id`, use `answer_mode: single_select` with `approve` and',
|
|
496
|
+
'`request changes`, and stop. Do not edit any file OR dispatch implementation until the',
|
|
497
|
+
'artifact revision that question presented is approved. If changes are requested, use',
|
|
498
|
+
'`update_artifact` on the SAME artifact, present its new revision in a new approval question,',
|
|
499
|
+
'and apply that same no-edit, no-implementation-dispatch fence until it is approved.',
|
|
500
|
+
'The only bypass is the person explicitly asking you to change codebase files WITHOUT a',
|
|
501
|
+
'Work Item artifact. Urgency, smallness, silence, or a generic "go ahead" is not a bypass.',
|
|
502
|
+
'When no codebase file will change, this gate does not apply.',
|
|
503
|
+
'',
|
|
504
|
+
'WHO DOES THE WORK IS YOUR JUDGEMENT',
|
|
505
|
+
'Substantial implementation, investigation, testing, and other substantial work normally',
|
|
506
|
+
'goes to the people you send. Before you start substantial multi-part work, send at least',
|
|
507
|
+
'one meaningful independent implementation, investigation, or test part. Multiple independent',
|
|
508
|
+
'file, module, or test parts belong with the people you send by default; do not keep all of',
|
|
509
|
+
'them just because you could complete every piece yourself.',
|
|
510
|
+
'Skip sending only when the whole turn is very small, or when sending any meaningful',
|
|
511
|
+
'independent part would genuinely make the result slower or less accurate. You keep the full',
|
|
512
|
+
'picture, answer the person, and put the results together.',
|
|
513
|
+
/* ═══ SENDING COMES BEFORE READING, AND THAT IS ABOUT THE PERSON RATHER
|
|
514
|
+
THAN ABOUT THROUGHPUT. ═══ The person reads nothing until this process
|
|
515
|
+
exits, so every minute the owner spends reading the codebase before it
|
|
516
|
+
sends anybody is a minute of pulse and no words. Measured on
|
|
517
|
+
2026-08-24: 101 seconds to the first message, of which about 80 were
|
|
518
|
+
the owner reading first and sending second.
|
|
519
|
+
IT IS STILL NOT A GATE. `ux.md` deleted the manager that never touches
|
|
520
|
+
code because that shape "required the coordinator to predict, at
|
|
521
|
+
dispatch time, whether a request was big. It cannot", and Issue 3 made
|
|
522
|
+
this judgement "never a mechanical gate". So this is which order to do
|
|
523
|
+
two things the owner was already going to do, not a rule about
|
|
524
|
+
whether. The paragraph above still decides whether. */
|
|
525
|
+
'',
|
|
526
|
+
'WHEN YOU ARE GOING TO SEND SOMEBODY, SEND THEM FIRST',
|
|
527
|
+
'Read what you need to write their piece, and no more; the rest of the reading is theirs to',
|
|
528
|
+
'do. Nothing you write reaches the person until you stop, so every minute you spend reading',
|
|
529
|
+
'before you send anybody is a minute they watch a line with no answer coming.',
|
|
530
|
+
'',
|
|
531
|
+
'IF YOU SPLIT IT',
|
|
532
|
+
'Everybody you send works in the SAME copy of the codebase as everybody else, at the same',
|
|
533
|
+
'time. Nothing keeps them out of each other\'s way, so anybody CHANGING files needs a piece',
|
|
534
|
+
'that touches nobody else\'s files, or has to be sent on their own.',
|
|
535
|
+
/* ═══ AND A WORKFLOW OVERRIDES THAT DEFAULT, INSIDE THE BLOCK THAT SETS
|
|
536
|
+
IT. ═══ Six lines above, the owner is told the opposite: multiple
|
|
537
|
+
independent parts belong with the people it sends, all working at the
|
|
538
|
+
same time. A workflow's stages ARE multiple parts, so without a
|
|
539
|
+
precedence rule the owner sends them all at once, the read-back
|
|
540
|
+
barrier restarts it ONCE, and the order the person chose the workflow
|
|
541
|
+
for is gone. Nothing in the record refuses a second dispatch before
|
|
542
|
+
the first has ended — the barrier re-arms an owner when EVERY child
|
|
543
|
+
has finished, which sequences stages only because the owner sends one
|
|
544
|
+
at a time. So the rule is here, in the immutable brief that is
|
|
545
|
+
replayed verbatim on every restart, and therefore in front of the
|
|
546
|
+
owner at every stage boundary rather than only the first.
|
|
547
|
+
|
|
548
|
+
THE SECOND SENTENCE IS NOT DECORATION. A dispatched worker gets a
|
|
549
|
+
working copy and the responsibility and boundary AND NOTHING ELSE, and
|
|
550
|
+
`get_workflow` is this level's alone. Without it, the product takes
|
|
551
|
+
deliberate trouble to hand the owner an untruncated stage document and
|
|
552
|
+
the agent that performs the stage receives a paraphrase of it. */
|
|
553
|
+
...(hasWorkflow
|
|
554
|
+
? [
|
|
555
|
+
'A WORKFLOW IS ATTACHED, AND ITS STAGES GO OUT ONE AT A TIME. Send somebody for the',
|
|
556
|
+
'first stage only, and do not start the next stage until the one before it has come',
|
|
557
|
+
'back. When you send somebody for a stage, put that stage\'s own document into what you',
|
|
558
|
+
'give them, in full: they cannot read the workflow themselves.',
|
|
559
|
+
]
|
|
560
|
+
: []),
|
|
561
|
+
'What you have already sent somebody to do is written down, and you are handed that list',
|
|
562
|
+
'rather than having to remember it. Anything on it has been sent. Nothing on it is sent again.',
|
|
563
|
+
'When every one of them has finished you are started again, with what each of them wrote,',
|
|
564
|
+
'to put it together and answer. Nothing any of them writes reaches the person except',
|
|
565
|
+
'through you, so the reply that covers all of it is yours to write and nobody else\'s.',
|
|
566
|
+
'WHICH MEANS THE REPLY YOU WRITE NOW IS NOT THAT ANSWER. If you have sent anybody, you are',
|
|
567
|
+
'stopping in the middle, and what you write as you stop goes to the person as a message from',
|
|
568
|
+
'you. Write it. One short sentence, naming what has gone out and what you are waiting on:',
|
|
569
|
+
'the real answer is the one you write when you come back. Never stop silently. What you said',
|
|
570
|
+
'with `say` was about to happen; this is what has happened and what is still to come.',
|
|
571
|
+
]
|
|
572
|
+
: [
|
|
573
|
+
'You own one piece of the work, the one below, and nothing else. Whoever sent you is doing',
|
|
574
|
+
'the rest. If your responsibility is investigation-only, read and report without changing',
|
|
575
|
+
'any file. The conversation owner handles the Work Item artifact and approval; do not',
|
|
576
|
+
'duplicate that record. If you were given approved implementation work, make that change.',
|
|
577
|
+
'',
|
|
578
|
+
'Somebody else may be working in this same copy of the codebase at the same time as you, on',
|
|
579
|
+
'a different piece. Stay inside what you were given and leave the rest of it alone.',
|
|
580
|
+
]),
|
|
581
|
+
'',
|
|
582
|
+
'WHAT YOU OWN',
|
|
583
|
+
responsibility,
|
|
584
|
+
'',
|
|
585
|
+
'WHAT YOU MUST LEAVE ALONE',
|
|
586
|
+
boundary,
|
|
587
|
+
...(workItemId ? ['', 'THE WORK ITEM THIS IS ABOUT', workItemId] : []),
|
|
588
|
+
...whatWasAttached(attachments),
|
|
589
|
+
...(codebase ? [
|
|
590
|
+
'',
|
|
591
|
+
'THE CODEBASE THIS WORK BELONGS TO',
|
|
592
|
+
`${codebase.name} — id ${codebase.id} — ${codebase.identity}`,
|
|
593
|
+
] : []),
|
|
594
|
+
'',
|
|
595
|
+
'WHERE YOU ARE',
|
|
596
|
+
...(codebase
|
|
597
|
+
? [
|
|
598
|
+
'The directory you are in is a working copy of the codebase. Read it, run things in it, and',
|
|
599
|
+
'change it where that is what you were asked for. Name a file by its path inside this copy,',
|
|
600
|
+
'never by the full path from the root of this machine: the full one means nothing to the person',
|
|
601
|
+
'reading and is not ours to hand around.',
|
|
602
|
+
]
|
|
603
|
+
: [
|
|
604
|
+
'The directory you are in is an empty private workspace. No codebase checkout was attached to',
|
|
605
|
+
'this conversation. Work from the record and the tools you have; do not claim to have read or',
|
|
606
|
+
'changed project files.',
|
|
607
|
+
]),
|
|
608
|
+
'',
|
|
609
|
+
'WHAT YOU WERE AND WERE NOT GIVEN',
|
|
610
|
+
...(level === 2
|
|
611
|
+
? [
|
|
612
|
+
'This brief is your immutable responsibility and boundary. The visible conversation is',
|
|
613
|
+
'handed to you separately, in chronological order, whenever you are started. Read it as the',
|
|
614
|
+
'person wrote it; the launcher does not stand in for it. If something you genuinely need is',
|
|
615
|
+
'missing and only the person can supply it, you MUST call `ask_question` and stop instead',
|
|
616
|
+
'of saying it in your reply. Otherwise say what is missing plainly rather than guessing.',
|
|
617
|
+
]
|
|
618
|
+
: [
|
|
619
|
+
'What is above is the whole of it. You have not seen what anybody said, and you are not meant',
|
|
620
|
+
'to: what matters was written into what you own. If something you genuinely need is missing,',
|
|
621
|
+
'say so plainly in your reply rather than guessing at it.',
|
|
622
|
+
]),
|
|
623
|
+
'',
|
|
624
|
+
'WHAT CANNOT BE DECIDED HERE',
|
|
625
|
+
'Some things are not yours to settle, and guessing at one of those quietly is worse than any',
|
|
626
|
+
'delay: the work goes on top of an assumption nobody made and nobody can see. Asking stops you',
|
|
627
|
+
'where you are, and you are started again with the answer when there is one, so nothing you have',
|
|
628
|
+
'done is lost by asking. Questions already asked on this work, and what they were answered with,',
|
|
629
|
+
'are there to be read, and one of them may be yours.',
|
|
630
|
+
'',
|
|
631
|
+
'YOU CAN BE STOPPED, AND WHAT YOU WROTE DOWN IS ALL THAT COMES BACK WITH YOU',
|
|
632
|
+
'This machine can go down and take you with it, mid-sentence, with no warning and no chance to',
|
|
633
|
+
'tidy up. When that happens you are started again with what is above and with the notes you last',
|
|
634
|
+
'wrote down, and with nothing else: not what you had in your head, not what you had just decided,',
|
|
635
|
+
'not what you were halfway through. Notes written at the end of the work are notes that were',
|
|
636
|
+
'never written.',
|
|
637
|
+
'',
|
|
638
|
+
...WHILE_YOU_ARE_WORKING,
|
|
639
|
+
'',
|
|
640
|
+
...(level === 2
|
|
641
|
+
? whatYouSendBack([
|
|
642
|
+
'- Nobody rewrites what you send. It goes to them as you wrote it, so it has to stand on its',
|
|
643
|
+
' own: answer the thing that was asked, and say what you changed and where, without pasting',
|
|
644
|
+
' the code back. They can read the code; they cannot read your reasoning.',
|
|
645
|
+
])
|
|
646
|
+
: whatAWorkerSendsBack()),
|
|
647
|
+
].join('\n');
|
|
648
|
+
}
|
|
649
|
+
export const presentedArtifactAnswerContext = (artifact) => artifact === null
|
|
650
|
+
? []
|
|
651
|
+
: artifact.answer === 'request changes'
|
|
652
|
+
? [
|
|
653
|
+
'',
|
|
654
|
+
'ARTIFACT REVISION THIS ANSWER SENT BACK FOR CHANGES',
|
|
655
|
+
`Artifact ${artifact.id}, revision ${artifact.revision}.`,
|
|
656
|
+
'Do not edit any codebase file or dispatch implementation. Revise this SAME artifact as the',
|
|
657
|
+
'person requested, present its new revision in a new approval question, and stop.',
|
|
658
|
+
]
|
|
659
|
+
: [
|
|
660
|
+
'',
|
|
661
|
+
'ARTIFACT REVISION THIS ANSWER APPROVED',
|
|
662
|
+
`Artifact ${artifact.id}, revision ${artifact.revision}.`,
|
|
663
|
+
'Before editing any file or dispatching implementation, call `get_artifact` and compare its',
|
|
664
|
+
'current revision with this one. If they differ, this answer does not approve the current body:',
|
|
665
|
+
'present the current revision in a new approval question and stop.',
|
|
666
|
+
];
|
|
667
|
+
/**
|
|
668
|
+
* ═══ WHAT A RUN IS TOLD WHEN A PROCESS OF ITS OWN ENDED BEFORE IT FINISHED.
|
|
669
|
+
* ═══
|
|
670
|
+
*
|
|
671
|
+
* Named once here rather than written inline, because `respawnPrompt` says both
|
|
672
|
+
* of these too, in its own longer wording (`WHAT YOU ALREADY SENT SOMEBODY TO
|
|
673
|
+
* DO`, and `CARRY ON FROM WHERE THAT LEAVES YOU`). **The two must move
|
|
674
|
+
* together.** They are not folded into one string because doing that would
|
|
675
|
+
* rewrite prose that is already shipped and proven on the retry and recovery
|
|
676
|
+
* paths, which is not this slice's assignment; naming them is what makes the
|
|
677
|
+
* pairing findable by the next person to edit either.
|
|
678
|
+
*
|
|
679
|
+
* ═══ THEY SAY WHAT TO DO, AND NEVER WHY IT HAPPENED. ═══ Three things end a
|
|
680
|
+
* process early and they are not the same event: a harness that crashed, a
|
|
681
|
+
* machine that went down, and a person who said the work was going the wrong
|
|
682
|
+
* way. `resumePrompt` and `retryPrompt` exist as two functions precisely
|
|
683
|
+
* because telling an agent an untrue reason for its restart is what this file
|
|
684
|
+
* exists to prevent, so a sentence shared by all three may only say the part
|
|
685
|
+
* that is true of all three.
|
|
686
|
+
*/
|
|
687
|
+
const DO_NOT_SEND_SOMEBODY_TWICE = 'Do not send somebody to do a piece of work that is already in this list. Anything of theirs '
|
|
688
|
+
+ 'still going is still going, and asking again for what they are already doing costs the person twice.';
|
|
689
|
+
const LOOK_AT_THE_WORK_ITSELF = 'A process of yours ended before it finished, so what you last wrote down is what you had time to '
|
|
690
|
+
+ 'write, not everything that happened. Where your notes are silent, look at the work itself rather '
|
|
691
|
+
+ 'than starting it over.';
|
|
692
|
+
/**
|
|
693
|
+
* The complete one-shot context for the durable conversational owner.
|
|
694
|
+
*
|
|
695
|
+
* Visible history is supplied as one ordered list. A newly leased message is
|
|
696
|
+
* marked in place rather than appended, and a question plus its canonical
|
|
697
|
+
* answer is one event, so neither can occur twice in the prompt.
|
|
698
|
+
*/
|
|
699
|
+
export function ownerActivationPrompt(brief, report, events, children, childQuestion, currentArtifactAnswer = null,
|
|
700
|
+
/** A process of this run's own ended before it finished, so the working copy
|
|
701
|
+
* may hold work no report mentions and the worker list may hold one it
|
|
702
|
+
* dispatched a moment before. Never says WHICH ending it was: see
|
|
703
|
+
* `LOOK_AT_THE_WORK_ITSELF`. */
|
|
704
|
+
afterAProcessEnded = false) {
|
|
705
|
+
const history = events.map((event) => {
|
|
706
|
+
if (event.kind === 'question') {
|
|
707
|
+
return [
|
|
708
|
+
`CTRL+SPC QUESTION ${event.body}`,
|
|
709
|
+
event.answer ? `PERSON ANSWER ${event.answer}` : 'PERSON ANSWER waiting',
|
|
710
|
+
...(event.relatedArtifactId && event.relatedArtifactRevision
|
|
711
|
+
? [`PRESENTED ARTIFACT ${event.relatedArtifactId}, revision ${event.relatedArtifactRevision}`]
|
|
712
|
+
: []),
|
|
713
|
+
...presentedArtifactAnswerContext(currentArtifactAnswer?.questionId === event.id
|
|
714
|
+
? currentArtifactAnswer
|
|
715
|
+
: null),
|
|
716
|
+
].join('\n');
|
|
717
|
+
}
|
|
718
|
+
const who = event.kind === 'user' ? 'PERSON' : 'CTRL+SPC';
|
|
719
|
+
return `${who}${event.needsReply ? ` — REPLY TO THIS NOW (turn ${event.id})` : ''}\n${event.body}`;
|
|
720
|
+
});
|
|
721
|
+
return [
|
|
722
|
+
'YOU OWN THIS CTRL+SPC CONVERSATION.',
|
|
723
|
+
'You are this conversation\'s one owner, started for one serialized activation. Speak directly to the',
|
|
724
|
+
'person as CTRL+SPC. The launcher is finished and cannot answer or receive an escalation.',
|
|
725
|
+
'',
|
|
726
|
+
'VISIBLE CONVERSATION, OLDEST FIRST',
|
|
727
|
+
...(history.length === 0 ? ['Nothing has been said yet.'] : history),
|
|
728
|
+
...(childQuestion === null ? [] : [
|
|
729
|
+
'',
|
|
730
|
+
'A WORKER NEEDS YOUR DECISION',
|
|
731
|
+
`Question ${childQuestion.id}: ${childQuestion.question}`,
|
|
732
|
+
'Answer it with answer_escalation if you can. If the person must decide, ask them directly.',
|
|
733
|
+
]),
|
|
734
|
+
'',
|
|
735
|
+
'WHAT YOU LAST WROTE DOWN',
|
|
736
|
+
report ?? 'Nothing yet.',
|
|
737
|
+
'',
|
|
738
|
+
'PRIVATE WORKER SNAPSHOT',
|
|
739
|
+
...(children.length === 0 ? ['You have sent nobody.'] : children),
|
|
740
|
+
...(afterAProcessEnded ? ['', LOOK_AT_THE_WORK_ITSELF, DO_NOT_SEND_SOMEBODY_TWICE] : []),
|
|
741
|
+
'',
|
|
742
|
+
// THE BRIEF IS LAST, and these go before it: the brief ends with how to
|
|
743
|
+
// write the reply, and anything after it pushes that ending into the middle.
|
|
744
|
+
'YOUR IMMUTABLE BRIEF',
|
|
745
|
+
brief,
|
|
746
|
+
].join('\n');
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* What an agent that was stopped is started again with: the same brief, its own
|
|
750
|
+
* notes, and what it already sent others to do.
|
|
751
|
+
*
|
|
752
|
+
* ═══ THE BRIEF IS CARRIED VERBATIM AND IS THE LAST THING IN IT. ═══ Verbatim
|
|
753
|
+
* because ux.md fixes it at dispatch — "a respawn is given the same instruction
|
|
754
|
+
* the first attempt was" — so this must not summarise, shorten or re-derive it.
|
|
755
|
+
* Last because the brief ENDS with how to write the reply, and that ending is
|
|
756
|
+
* the one an ordinary spawn has: putting the resumption after it would push the
|
|
757
|
+
* outbound rules into the middle of the prompt and leave a note about a dead
|
|
758
|
+
* process as the final word.
|
|
759
|
+
*
|
|
760
|
+
* ═══ THE REPORT IS WHAT THE RUN WROTE, AND NOTHING ELSE. ═══ It is deliberately
|
|
761
|
+
* lossy (ux.md), because the record holds the detail — the receipts, the
|
|
762
|
+
* activity and the commits are all still there to be read. This carries the
|
|
763
|
+
* state to resume from, not an archive.
|
|
764
|
+
*
|
|
765
|
+
* ═══ `children` IS READ LIVE BY THE CALLER, WHICH IS THE RULE THAT DECIDES
|
|
766
|
+
* WHAT GOES IN A REPORT AT ALL. ═══ ux.md: "anything that can change while
|
|
767
|
+
* you are not running is read live and never carried in your report." What was
|
|
768
|
+
* dispatched changes while a run is dead — its children finish — so it is a fact
|
|
769
|
+
* to be read rather than something to be remembered, and the sentence below says
|
|
770
|
+
* so out loud. NULL means this agent cannot have sent anybody: at the deepest
|
|
771
|
+
* level there is no `dispatch` at all, and printing "you sent nobody" would
|
|
772
|
+
* describe an absence as a choice.
|
|
773
|
+
*/
|
|
774
|
+
export function resumePrompt(brief, report, children) {
|
|
775
|
+
return respawnPrompt([
|
|
776
|
+
'YOU WERE ALREADY DOING THIS, AND YOU WERE STOPPED BEFORE YOU FINISHED.',
|
|
777
|
+
'Not by anybody: the machine went down and took you with it. You are the same agent, started',
|
|
778
|
+
'again, and everything below is what survived. Nothing about what you were sent to do has',
|
|
779
|
+
'changed, and nothing you did before you stopped has been undone.',
|
|
780
|
+
], brief, report, children);
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* ═══ WHAT AN AGENT IS STARTED AGAIN WITH WHEN THE PROCESS IT WAS RUNNING IN
|
|
784
|
+
* DIED ON SOMETHING UPSTREAM. ═══
|
|
785
|
+
*
|
|
786
|
+
* recovery-1/ux.md, Slice 3: "the agent's harness fails on something that is
|
|
787
|
+
* nobody's fault: a rate limit, a dropped connection, a provider outage. The
|
|
788
|
+
* product tries again rather than failing the card."
|
|
789
|
+
*
|
|
790
|
+
* ═══ IT EXISTS BECAUSE `resumePrompt`'s OPENING IS FALSE HERE. ═══ That one
|
|
791
|
+
* says "the machine went down and took you with it", and on this path the
|
|
792
|
+
* machine did not go down: it is the same daemon, still running, that watched
|
|
793
|
+
* the harness exit. Telling an agent something untrue about why it is running is
|
|
794
|
+
* what this file exists to prevent, and a fifth opening onto the one respawn
|
|
795
|
+
* body is the shape the other four already established rather than a new idea.
|
|
796
|
+
*
|
|
797
|
+
* ═══ AND IT SAYS THE FAILURE WAS NOT THE AGENT'S, BECAUSE THE ALTERNATIVE IS
|
|
798
|
+
* WORSE. ═══ An agent told only that it stopped has no way to tell a harness
|
|
799
|
+
* that died from work it got wrong, and the cheapest guess from inside is that
|
|
800
|
+
* the last thing it tried was the problem. That is how a resume turns into a
|
|
801
|
+
* rewrite of work that was already right.
|
|
802
|
+
*/
|
|
803
|
+
export function retryPrompt(brief, report, children) {
|
|
804
|
+
return respawnPrompt([
|
|
805
|
+
'YOU WERE ALREADY DOING THIS, AND THE PROCESS YOU WERE RUNNING IN DIED BEFORE YOU FINISHED.',
|
|
806
|
+
'Not because of anything you did, and not because anybody stopped you: the harness itself',
|
|
807
|
+
'failed, which is what a rate limit, a dropped connection or a provider outage looks like from',
|
|
808
|
+
'here. You are the same agent, started again, and everything below is what survived. Nothing',
|
|
809
|
+
'about what you were sent to do has changed, and nothing you did before it died has been undone.',
|
|
810
|
+
], brief, report, children);
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* ═══ WHAT AN AGENT THAT ASKED IS STARTED AGAIN WITH. ═══
|
|
814
|
+
*
|
|
815
|
+
* ux.md: "The run that asked is spawned again, fresh, with the question and the
|
|
816
|
+
* answer in its brief. Not level 1 passing it down through level 2. Not a
|
|
817
|
+
* message. A respawn, which is the same mechanism as redirect." So this is
|
|
818
|
+
* `resumePrompt` with one more thing in it, and the sameness is the point: the
|
|
819
|
+
* agent picks up where the old one left off for the same reason a crash resume
|
|
820
|
+
* does, because the work was never inside the process.
|
|
821
|
+
*
|
|
822
|
+
* ═══ AND IT SAYS OUT LOUD THAT AN ANSWER CAN BE BIGGER THAN THE QUESTION. ═══
|
|
823
|
+
* ux.md: "If the answer turns out to change more than that worker's own piece,
|
|
824
|
+
* it escalates again rather than proceeding. Whether an answer is local or global
|
|
825
|
+
* is judgement, and building a classifier for it would be the product deciding
|
|
826
|
+
* something the agent is better placed to see." So it is named as a judgement
|
|
827
|
+
* the agent makes, in one sentence, rather than a rule about when to ask.
|
|
828
|
+
*/
|
|
829
|
+
export function answerPrompt(brief, report, children, question, answer, relatedArtifact = null) {
|
|
830
|
+
return respawnPrompt([
|
|
831
|
+
'YOU STOPPED BECAUSE YOU COULD NOT GO ON WITHOUT KNOWING SOMETHING. HERE IT IS.',
|
|
832
|
+
'You are the same agent, started again, and everything you did before you asked is still there.',
|
|
833
|
+
'',
|
|
834
|
+
'WHAT YOU ASKED',
|
|
835
|
+
question,
|
|
836
|
+
'',
|
|
837
|
+
'WHAT CAME BACK',
|
|
838
|
+
answer,
|
|
839
|
+
...presentedArtifactAnswerContext(relatedArtifact),
|
|
840
|
+
'',
|
|
841
|
+
'If this answers less than you asked, or turns out to change more than the piece you were sent',
|
|
842
|
+
'to do, ask again rather than deciding it yourself. Carrying on from a guess about something this',
|
|
843
|
+
'big is how work gets done twice.',
|
|
844
|
+
], brief, report, children);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* ═══ WHAT AN AGENT IS STARTED AGAIN WITH WHEN EVERYBODY IT SENT HAS FINISHED.
|
|
848
|
+
* ═══
|
|
849
|
+
*
|
|
850
|
+
* ux.md's third re-arm, and the one nothing else could fire: "every child of a
|
|
851
|
+
* run has finished → that run, so it can read them back and carry on."
|
|
852
|
+
*
|
|
853
|
+
* ═══ WHAT THEY FOUND IS IN THE PROMPT, NOT BEHIND A TOOL CALL. ═══ This is the
|
|
854
|
+
* one respawn whose entire purpose is reading them back, and ux.md is explicit
|
|
855
|
+
* about the difference between a guarantee and a hope: "an agent cannot fail to
|
|
856
|
+
* read its own input." `childrenOf` carries each of their reports, so the
|
|
857
|
+
* synthesis starts from what they wrote rather than from remembering to ask.
|
|
858
|
+
*
|
|
859
|
+
* ═══ AND IT SAYS OUT LOUD THAT NOBODY ELSE WILL SAY IT. ═══ A worker's words go
|
|
860
|
+
* into the record and stop there, by `panel3_answer`. From inside, an agent
|
|
861
|
+
* cannot tell whether the person has already read three replies; told wrongly it
|
|
862
|
+
* writes a covering note over answers nobody ever saw. So it is told, as a fact
|
|
863
|
+
* about the situation rather than as an instruction.
|
|
864
|
+
*/
|
|
865
|
+
export function readBackPrompt(brief, report, children) {
|
|
866
|
+
return respawnPrompt([
|
|
867
|
+
'EVERYBODY YOU SENT HAS FINISHED, AND YOU ARE STARTED AGAIN TO READ WHAT THEY FOUND.',
|
|
868
|
+
'You are the same agent, started again, and nothing you did before has been undone. Nothing you',
|
|
869
|
+
'sent anybody to do is still running.',
|
|
870
|
+
'',
|
|
871
|
+
'WHAT THEY WROTE IS BELOW, and it has gone nowhere else. The person has not seen any of it and',
|
|
872
|
+
'will not: what reaches them about this is what you write now. If what came back leaves the',
|
|
873
|
+
'work finished, answer with the whole of it in one piece. If it leaves something undone, that',
|
|
874
|
+
'is still yours, and you may do it or send somebody for it exactly as before.',
|
|
875
|
+
], brief, report, children);
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* ═══ WHAT AN AGENT IS STARTED AGAIN WITH WHEN SOMEBODY IT SENT CANNOT GO ON.
|
|
879
|
+
* ═══
|
|
880
|
+
*
|
|
881
|
+
* ux.md: "the level 2 agent is spawned, sees a child asked something, and either
|
|
882
|
+
* answers it or escalates and exits... Each hop is one cheap spawn that reads and
|
|
883
|
+
* decides. Against a question that waits on a human, three spawns are noise, and
|
|
884
|
+
* each one is a real chance to settle it without interrupting anyone."
|
|
885
|
+
*
|
|
886
|
+
* THE COST OF PASSING IT ON IS STATED, because that is the whole reason the hop
|
|
887
|
+
* exists: an agent that passes everything up is a level that bought nothing.
|
|
888
|
+
*/
|
|
889
|
+
export function escalationPrompt(brief, report, children, questionId, question) {
|
|
890
|
+
return respawnPrompt([
|
|
891
|
+
'SOMEBODY YOU SENT CANNOT GO ON UNTIL SOMETHING IS DECIDED, AND IT CAME TO YOU.',
|
|
892
|
+
'You are the same agent, started again to deal with it. Nothing you did before has been undone.',
|
|
893
|
+
'',
|
|
894
|
+
'WHAT THEY NEED TO KNOW',
|
|
895
|
+
question,
|
|
896
|
+
'',
|
|
897
|
+
`Its id is ${questionId}. That is what you answer it by, and it is also what you name if you`,
|
|
898
|
+
'cannot settle it either and have to send it further up: name it, and it goes up as one question',
|
|
899
|
+
'with your wording. Leave it out and you have asked a second, separate question of your own,',
|
|
900
|
+
'and this one stays here with nobody dealing with it.',
|
|
901
|
+
'',
|
|
902
|
+
'ANSWER IT IF YOU CAN. You know what this work is for and they do not, so most of what reaches',
|
|
903
|
+
'you is yours to settle. Read what you need to first. Only what you genuinely cannot decide goes',
|
|
904
|
+
'further up, and everything that goes up eventually stops a person and asks them instead.',
|
|
905
|
+
'Whichever you do, you stop afterwards: they are started again with the answer, not by you.',
|
|
906
|
+
], brief, report, children);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* What every respawn shares: what it wrote down, what it already sent others to
|
|
910
|
+
* do, and the brief it was given, verbatim and last.
|
|
911
|
+
*
|
|
912
|
+
* ONE OWNER, because the three reasons a run is started again — the machine went
|
|
913
|
+
* down, its question was answered, somebody it sent needs a decision — differ
|
|
914
|
+
* only in what is NEW. Everything else about resuming is the same fact, and two
|
|
915
|
+
* copies of it would drift in the way ux.md's own note about the brief describes:
|
|
916
|
+
* invisibly, in whichever one nobody re-read.
|
|
917
|
+
*/
|
|
918
|
+
function respawnPrompt(opening, brief, report, children) {
|
|
919
|
+
return [
|
|
920
|
+
...opening,
|
|
921
|
+
'',
|
|
922
|
+
'WHAT YOU WROTE DOWN, THE LAST TIME YOU WROTE ANYTHING DOWN',
|
|
923
|
+
report ?? 'Nothing. You were stopped before you wrote anything down, so what you did before that '
|
|
924
|
+
+ 'is not known here — check the work itself before you assume any of it is undone.',
|
|
925
|
+
'',
|
|
926
|
+
...(children === null ? [] : [
|
|
927
|
+
'WHAT YOU ALREADY SENT SOMEBODY TO DO, AS IT STANDS RIGHT NOW, AND WHAT EACH OF THEM WROTE',
|
|
928
|
+
'Read from the record a moment ago, not from your notes, so it is right even where they are',
|
|
929
|
+
'silent: this is what exists, whatever you meant to do. Do not send somebody to do a piece of',
|
|
930
|
+
'work that is already in this list. Anything of theirs still going is still going, and asking',
|
|
931
|
+
'again for what they are already doing costs the person twice. What is under each one is what',
|
|
932
|
+
'that one wrote down, in its own words.',
|
|
933
|
+
...(children.length === 0 ? ['You have sent nobody.'] : children),
|
|
934
|
+
'',
|
|
935
|
+
]),
|
|
936
|
+
'CARRY ON FROM WHERE THAT LEAVES YOU. What your notes say is done is done and is not to be done',
|
|
937
|
+
'again; what they leave open is what is left. Where they are silent, look at the work itself',
|
|
938
|
+
'rather than starting it over: they are what you had time to write down, not everything that',
|
|
939
|
+
'happened. And write them again as you go, for the same reason.',
|
|
940
|
+
'',
|
|
941
|
+
'WHAT YOU WERE SENT TO DO, WORD FOR WORD AS YOU WERE SENT IT',
|
|
942
|
+
'',
|
|
943
|
+
brief,
|
|
944
|
+
].join('\n');
|
|
945
|
+
}
|