@proagentstore/cli 0.4.56 → 0.4.57
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.
|
@@ -60,6 +60,16 @@ export class HeadlessSession {
|
|
|
60
60
|
run = "idle";
|
|
61
61
|
/** Claude Code's own session id (from the init event) — used to --resume. */
|
|
62
62
|
claudeSessionId = null;
|
|
63
|
+
/**
|
|
64
|
+
* The cloud's context brief, until the first turn spends it (ADR 0005, #693). Null once
|
|
65
|
+
* delivered, and null from the start when the engine resumed its own conversation.
|
|
66
|
+
*
|
|
67
|
+
* Consumed ONCE, by construction rather than by convention: it is cleared in the same expression
|
|
68
|
+
* that reads it. A brief re-sent on every turn would be the "unbounded brief… a token bill that
|
|
69
|
+
* grows every turn" the ADR names as the cost of owning the conversation, and it would also start
|
|
70
|
+
* contradicting the engine's own memory of the turns since.
|
|
71
|
+
*/
|
|
72
|
+
pendingSeed = null;
|
|
63
73
|
/** "stream-json" for Claude (structured) · "raw" for any other CLI (stdout capture). */
|
|
64
74
|
mode;
|
|
65
75
|
cmdBin;
|
|
@@ -181,6 +191,17 @@ export class HeadlessSession {
|
|
|
181
191
|
get resumedConversation() {
|
|
182
192
|
return this.mode === "stream-json" && this.claudeSessionId !== null;
|
|
183
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Did this engine come up cold AND with a brief to lead its first turn (ADR 0005, #693)?
|
|
196
|
+
*
|
|
197
|
+
* Read by `/coding/start` so the sentence the user is shown is one this side CONFIRMED. It is
|
|
198
|
+
* armed-not-delivered: the brief goes out with the first `input()`, which may never arrive if
|
|
199
|
+
* the session is closed first. That is why the cloud's wording is "it was given a brief" — true
|
|
200
|
+
* the moment the engine holds one — and never "it has read your history".
|
|
201
|
+
*/
|
|
202
|
+
get seededConversation() {
|
|
203
|
+
return this.pendingSeed !== null;
|
|
204
|
+
}
|
|
184
205
|
constructor(config) {
|
|
185
206
|
this.config = config;
|
|
186
207
|
this.engineLabel = `${config.clientType}:${config.id}`;
|
|
@@ -188,6 +209,10 @@ export class HeadlessSession {
|
|
|
188
209
|
this.claudeSessionId = readState(config.statePath, config.id) ?? (config.resumeFrom ? readState(config.statePath, config.resumeFrom) : null);
|
|
189
210
|
// Claude is the structured engine; everything else is a raw CLI.
|
|
190
211
|
this.mode = config.clientType === "claude" ? "stream-json" : "raw";
|
|
212
|
+
// AFTER both lines above, because `resumedConversation` reads them: the brief is the fallback,
|
|
213
|
+
// so an engine that found its own conversation drops it unread rather than being handed a
|
|
214
|
+
// summary of the conversation it is already in.
|
|
215
|
+
this.pendingSeed = this.resumedConversation ? null : config.seed?.trim() || null;
|
|
191
216
|
const { bin, args } = parseCommand(config.command);
|
|
192
217
|
// When no explicit command is configured, fall back to THIS engine's default
|
|
193
218
|
// command (codex/gemini/grok/…) — not a hard-coded "claude", which would drive a
|
|
@@ -381,8 +406,17 @@ export class HeadlessSession {
|
|
|
381
406
|
input(text, opts = {}) {
|
|
382
407
|
// The Engine reads the preamble, the pane the short marker; the instruction stays evidence.
|
|
383
408
|
const sent = authoredTurn(text, opts.author);
|
|
409
|
+
// The brief leads the turn and is spent in the reading (#693). It goes to the ENGINE only:
|
|
410
|
+
// the pane gets the one-line marker below, because the transcript is what the owner and the
|
|
411
|
+
// Pilot re-read through `/coding/capture` and twelve thousand characters of reconstructed
|
|
412
|
+
// history would evict the live output they are looking at — the same budget argument
|
|
413
|
+
// `turn-author.ts` makes for its two-word marker.
|
|
414
|
+
const seed = this.pendingSeed;
|
|
415
|
+
this.pendingSeed = null;
|
|
384
416
|
if (!this.alive)
|
|
385
417
|
this.start();
|
|
418
|
+
if (seed)
|
|
419
|
+
this.push("[pags] a context brief from ProAgentStore's record was delivered with this turn — a reconstruction, not the previous conversation");
|
|
386
420
|
this.push(`\n❯ [${stamp()}] ${authorTag(opts.author)}${text}`); // ❯ — your turn, timestamped
|
|
387
421
|
this.run = "thinking";
|
|
388
422
|
const now = Date.now();
|
|
@@ -390,16 +424,19 @@ export class HeadlessSession {
|
|
|
390
424
|
this.turnStartedAt = now;
|
|
391
425
|
this.sawOutputSinceInput = false; // arm the persistent-raw idle heuristic for THIS turn
|
|
392
426
|
try {
|
|
427
|
+
// Brief first, instruction second, and never the other way round: it is background for
|
|
428
|
+
// the request, and an engine that reads the request last acts on the request.
|
|
429
|
+
const withSeed = seed ? `${seed}\n\n${sent}` : sent;
|
|
393
430
|
if (this.mode === "stream-json") {
|
|
394
431
|
// `role` stays "user" — the only role this protocol accepts, which is why the
|
|
395
432
|
// disambiguation rides in the text instead (#505).
|
|
396
|
-
const msg = JSON.stringify({ type: "user", message: { role: "user", content: [{ type: "text", text:
|
|
433
|
+
const msg = JSON.stringify({ type: "user", message: { role: "user", content: [{ type: "text", text: withSeed }] } });
|
|
397
434
|
this.proc?.stdin?.write(`${msg}\n`);
|
|
398
435
|
}
|
|
399
436
|
else {
|
|
400
437
|
// Raw CLI: spawn THIS turn. See `oneShot` — there is no persistent process to
|
|
401
438
|
// write to, because a non-interactive binary would never have read it.
|
|
402
|
-
this.runOneShot(
|
|
439
|
+
this.runOneShot(withSeed);
|
|
403
440
|
}
|
|
404
441
|
}
|
|
405
442
|
catch {
|
|
@@ -117,6 +117,7 @@ export class CodingRuntime {
|
|
|
117
117
|
env: input.env,
|
|
118
118
|
statePath: defaultStatePath(this.reposBaseDir),
|
|
119
119
|
resumeFrom: input.resumeFrom,
|
|
120
|
+
seed: input.seed,
|
|
120
121
|
ghScope: input.ghScope,
|
|
121
122
|
bin: input.bin,
|
|
122
123
|
});
|
|
@@ -126,8 +127,12 @@ export class CodingRuntime {
|
|
|
126
127
|
// clears its own key on that exit. Reporting after would say "started clean" about a launch
|
|
127
128
|
// that did carry a conversation, and the transcript (which shows the crash) would disagree.
|
|
128
129
|
const resumed = session.resumedConversation;
|
|
130
|
+
// Read alongside `resumed`, and BEFORE `start()` for the same reason: a bad `--resume` that
|
|
131
|
+
// kills the process on spawn clears the engine's key, and a seed answer read afterwards would
|
|
132
|
+
// describe a different launch from the one the caller asked about.
|
|
133
|
+
const seeded = session.seededConversation;
|
|
129
134
|
session.start();
|
|
130
|
-
return { ...this.snapshot(input.sessionId), resumed };
|
|
135
|
+
return { ...this.snapshot(input.sessionId), resumed, seeded };
|
|
131
136
|
}
|
|
132
137
|
/**
|
|
133
138
|
* The pane the brain reasons over + the inferred run state.
|