@proagentstore/cli 0.4.103 → 0.4.105
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.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { runObjectiveBlock } from "./run-objective.js";
|
|
1
2
|
import { spawn } from "node:child_process";
|
|
2
3
|
import { classifyCommand, commandFromToolInput, fillTargetFromResult, toolCallOk, toolResultMark } from "./engine-acts.js";
|
|
3
4
|
import { parseEngineUsage } from "./engine-usage.js";
|
|
@@ -103,6 +104,8 @@ export class HeadlessSession {
|
|
|
103
104
|
turnInputId = null;
|
|
104
105
|
/** Ids of inputs a COMPLETED turn consumed, newest last and bounded (#1019). See {@link consumedInputs}. */
|
|
105
106
|
consumed = [];
|
|
107
|
+
/** Ids of inputs handed to the engine, newest last and bounded (#1019): what a retry is reconciled against. */
|
|
108
|
+
accepted = [];
|
|
106
109
|
/**
|
|
107
110
|
* The engine's own last output line of the turn in flight (#545).
|
|
108
111
|
*
|
|
@@ -320,6 +323,10 @@ export class HeadlessSession {
|
|
|
320
323
|
get consumedInputs() {
|
|
321
324
|
return this.consumed;
|
|
322
325
|
}
|
|
326
|
+
/** Inputs this session accepted — handed to the engine, whether or not a turn has finished on them. */
|
|
327
|
+
get acceptedInputs() {
|
|
328
|
+
return this.accepted;
|
|
329
|
+
}
|
|
323
330
|
credit(id) {
|
|
324
331
|
if (!id)
|
|
325
332
|
return;
|
|
@@ -505,6 +512,11 @@ export class HeadlessSession {
|
|
|
505
512
|
* session-start brief, being the same record composed later.
|
|
506
513
|
*/
|
|
507
514
|
input(text, opts = {}) {
|
|
515
|
+
if (opts.inputId) {
|
|
516
|
+
this.accepted.push(opts.inputId);
|
|
517
|
+
if (this.accepted.length > MAX_CONSUMED_INPUTS)
|
|
518
|
+
this.accepted.shift();
|
|
519
|
+
}
|
|
508
520
|
// The Engine reads the preamble, the pane the short marker; the instruction stays evidence.
|
|
509
521
|
const sent = authoredTurn(text, opts.author);
|
|
510
522
|
// The brief leads the turn and is spent in the reading (#693). It goes to the ENGINE only:
|
|
@@ -521,6 +533,10 @@ export class HeadlessSession {
|
|
|
521
533
|
this.push("[pags] this engine keeps no memory between turns, so ProAgentStore's record of the conversation so far was delivered with this turn — a reconstruction, not the engine's own memory");
|
|
522
534
|
else if (seed)
|
|
523
535
|
this.push("[pags] a context brief from ProAgentStore's record was delivered with this turn — a reconstruction, not the previous conversation");
|
|
536
|
+
// The run's objective (#1019): to the ENGINE in full, the pane a one-line marker (same budget as the brief).
|
|
537
|
+
const objective = opts.objective ? runObjectiveBlock(opts.objective) : null;
|
|
538
|
+
if (opts.objective)
|
|
539
|
+
this.push(`[pags] the run's objective (${opts.objective.text.length} characters, verbatim) was delivered with this turn`);
|
|
524
540
|
this.push(`\n❯ [${stamp()}] ${authorTag(opts.author)}${text}`); // ❯ — your turn, timestamped
|
|
525
541
|
this.run = "thinking";
|
|
526
542
|
const now = Date.now();
|
|
@@ -531,7 +547,8 @@ export class HeadlessSession {
|
|
|
531
547
|
try {
|
|
532
548
|
// Brief first, instruction second, and never the other way round: it is background for
|
|
533
549
|
// the request, and an engine that reads the request last acts on the request.
|
|
534
|
-
|
|
550
|
+
// The objective sits between them: background like the brief, and above the instruction it frames.
|
|
551
|
+
const withSeed = [seed, objective, sent].filter(Boolean).join("\n\n");
|
|
535
552
|
if (this.adapter.persistent) {
|
|
536
553
|
// `role` stays "user" — the only role this protocol accepts, which is why the
|
|
537
554
|
// disambiguation rides in the text instead (#505).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// The run's objective, delivered verbatim with a turn and attributed to the platform (#1019).
|
|
2
|
+
const ORIGIN_KINDS = new Set(["account", "supervisor", "trigger", "unverified"]);
|
|
3
|
+
/** A run objective as the cloud sent it, or undefined — narrowed, because any caller can POST here (#1019). */
|
|
4
|
+
export function runObjective(raw) {
|
|
5
|
+
const o = raw;
|
|
6
|
+
if (!(o && typeof o.runId === "string" && o.runId.length <= 128 && typeof o.text === "string" && o.text.trim()))
|
|
7
|
+
return undefined;
|
|
8
|
+
const origin = o.origin;
|
|
9
|
+
const kind = typeof origin?.kind === "string" && ORIGIN_KINDS.has(origin.kind) ? origin.kind : "unverified";
|
|
10
|
+
// A label longer than a label is not shortened into a different claim — it is dropped.
|
|
11
|
+
const label = typeof origin?.by === "string" ? origin.by.replace(/\s+/g, " ").trim() : "";
|
|
12
|
+
const by = label.length <= 128 ? label : "";
|
|
13
|
+
return { runId: o.runId, text: o.text, origin: by ? { kind, by } : { kind } };
|
|
14
|
+
}
|
|
15
|
+
/** The attribution sentence: only what the platform verified about the author, never more. */
|
|
16
|
+
function attribution(runId, origin) {
|
|
17
|
+
const by = origin.by ? ` (${origin.by})` : "";
|
|
18
|
+
switch (origin.kind) {
|
|
19
|
+
case "account":
|
|
20
|
+
return `the objective submitted for run ${runId} through the owner's authenticated account${by}, exactly as ProAgentStore accepted it. Who composed the text is not verified.`;
|
|
21
|
+
case "supervisor":
|
|
22
|
+
return `the objective a supervising agent${by} delegated for run ${runId}, exactly as ProAgentStore accepted it. A supervisor's goal is not an instruction from the owner.`;
|
|
23
|
+
case "trigger":
|
|
24
|
+
return `the objective a trigger${by} composed for run ${runId}, exactly as ProAgentStore accepted it — standing wiring plus facts about another run, not a message from the owner.`;
|
|
25
|
+
default:
|
|
26
|
+
return `the objective ProAgentStore accepted for run ${runId}. Who wrote it is not verified.`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The run's objective as the engine reads it (#1019): verbatim, fenced, attributed only as far as the
|
|
31
|
+
* platform verified — the Pilot's instruction that follows is its own turn, and owner directives sent
|
|
32
|
+
* later outrank both.
|
|
33
|
+
*/
|
|
34
|
+
export function runObjectiveBlock(o) {
|
|
35
|
+
return [
|
|
36
|
+
`[pags] RUN OBJECTIVE — ${attribution(o.runId, o.origin ?? { kind: "unverified" })} It is not the Pilot's instruction, which follows as its own turn. Instructions the owner sends later in this run take precedence over it.`,
|
|
37
|
+
"<<<OBJECTIVE",
|
|
38
|
+
o.text,
|
|
39
|
+
"OBJECTIVE>>>",
|
|
40
|
+
].join("\n");
|
|
41
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { runObjective } from "./run-objective.js";
|
|
1
2
|
import { homedir } from "node:os";
|
|
2
3
|
import { join, resolve } from "node:path";
|
|
3
4
|
import { RunnerInputError } from "../errors.js";
|
|
@@ -65,7 +66,12 @@ export class CodingRuntime {
|
|
|
65
66
|
// a reader of the registration can see which machines can restore a branch invariant; the
|
|
66
67
|
// cloud still never trusts it, because an older runner simply 404s and the policy reports
|
|
67
68
|
// that it asked and was not answered.
|
|
68
|
-
|
|
69
|
+
// `coding.run-objective` (#1019): a message may carry the run's objective, delivered verbatim and
|
|
70
|
+
// attributed to the platform. `.v2`: attributed by its VERIFIED origin (run-objective.ts) — a cloud
|
|
71
|
+
// sends the objective only to a v2 runner, so an older one is never made to call it the owner's.
|
|
72
|
+
// `coding.input-idempotent`: a message whose inputId this session already accepted is not run again,
|
|
73
|
+
// and every snapshot lists the accepted ids, so a retry after a lost reply can be reconciled.
|
|
74
|
+
return ["coding.sessions", "coding.stream", "human.takeover", "coding.inspect", "coding.repo-write", "coding.run-objective", "coding.run-objective.v2", "coding.input-idempotent"];
|
|
69
75
|
}
|
|
70
76
|
/**
|
|
71
77
|
* Resolve the workDir for a read-only inspection. Prefer the tracked session's real
|
|
@@ -241,6 +247,7 @@ export class CodingRuntime {
|
|
|
241
247
|
// the omitted-when-null shape keeps "not measured" distinguishable from a verdict.
|
|
242
248
|
...(session.lastTurn ? { lastTurn: session.lastTurn } : {}),
|
|
243
249
|
...(session.consumedInputs.length ? { consumedInputs: [...session.consumedInputs] } : {}),
|
|
250
|
+
acceptedInputs: [...session.acceptedInputs],
|
|
244
251
|
...(opts.drainUsage ? { usage: session.takeUsage(), acts: session.takeActs() } : {}),
|
|
245
252
|
};
|
|
246
253
|
}
|
|
@@ -248,15 +255,21 @@ export class CodingRuntime {
|
|
|
248
255
|
act(sessionId, action) {
|
|
249
256
|
const session = this.require(sessionId);
|
|
250
257
|
switch (action.kind) {
|
|
251
|
-
case "message":
|
|
258
|
+
case "message": {
|
|
259
|
+
const inputId = typeof action.inputId === "string" && action.inputId.length <= 128 ? action.inputId : undefined;
|
|
260
|
+
// The same input twice is a retry after a lost reply, not a second instruction (#1019).
|
|
261
|
+
if (inputId && session.acceptedInputs.includes(inputId))
|
|
262
|
+
return { ...this.snapshot(sessionId), duplicateInput: inputId };
|
|
252
263
|
// Narrowed rather than trusted: this runner is a published package any caller can
|
|
253
264
|
// POST to, and an unrecognised author must read as "unstated", not become a label.
|
|
254
265
|
session.input(action.text, {
|
|
255
266
|
author: asTurnAuthor(action.author),
|
|
256
267
|
replay: typeof action.replay === "string" ? action.replay : undefined,
|
|
257
|
-
inputId
|
|
268
|
+
inputId,
|
|
269
|
+
objective: runObjective(action.objective),
|
|
258
270
|
});
|
|
259
271
|
break;
|
|
272
|
+
}
|
|
260
273
|
case "keys": {
|
|
261
274
|
// A snapshot is no longer the whole answer (#448). `key()` records the attempt and
|
|
262
275
|
// reports that it was not delivered; answering 200 with a pane that simply did not
|