baro-ai 0.86.0 → 0.87.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/cli.mjs +164 -3
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -74788,6 +74788,86 @@ function extractPath2(item) {
|
|
|
74788
74788
|
return null;
|
|
74789
74789
|
}
|
|
74790
74790
|
|
|
74791
|
+
// ../baro-orchestrator/src/harness/cooperative-suspension.ts
|
|
74792
|
+
function laneQuiescenceWitness(backend) {
|
|
74793
|
+
return backend === "openai" ? "self" : "process";
|
|
74794
|
+
}
|
|
74795
|
+
var CooperativeSuspension = class {
|
|
74796
|
+
constructor(storyId, quiesce, timeoutMs) {
|
|
74797
|
+
this.storyId = storyId;
|
|
74798
|
+
this.quiesce = quiesce;
|
|
74799
|
+
this.timeoutMs = timeoutMs;
|
|
74800
|
+
}
|
|
74801
|
+
blockId = null;
|
|
74802
|
+
/** The accepted block this story is suspending for, if any. */
|
|
74803
|
+
get acceptedBlockId() {
|
|
74804
|
+
return this.blockId;
|
|
74805
|
+
}
|
|
74806
|
+
/** True once no further work may begin, which is what makes a witness one. */
|
|
74807
|
+
get blocksNewWork() {
|
|
74808
|
+
return this.blockId !== null;
|
|
74809
|
+
}
|
|
74810
|
+
/** How a settled suspension reads in the story's outcome. */
|
|
74811
|
+
get outcomeText() {
|
|
74812
|
+
return `suspended on dependency block ${this.blockId}`;
|
|
74813
|
+
}
|
|
74814
|
+
/**
|
|
74815
|
+
* Record the request and wait for the lane's witness. Throws rather than
|
|
74816
|
+
* returning a summary the host would read as a certificate: a refusal
|
|
74817
|
+
* costs a retained worktree, a false certificate costs a silent write into
|
|
74818
|
+
* a branch already snapshotted.
|
|
74819
|
+
*/
|
|
74820
|
+
async request(blockId, summary) {
|
|
74821
|
+
this.record(blockId);
|
|
74822
|
+
if (!await this.quiesce(this.timeoutMs)) {
|
|
74823
|
+
throw new Error(
|
|
74824
|
+
`story ${this.storyId} quiescence could not be certified for dependency block ${blockId}`
|
|
74825
|
+
);
|
|
74826
|
+
}
|
|
74827
|
+
return summary();
|
|
74828
|
+
}
|
|
74829
|
+
record(blockId) {
|
|
74830
|
+
if (typeof blockId !== "string" || blockId.trim() !== blockId || !blockId) {
|
|
74831
|
+
throw new TypeError(
|
|
74832
|
+
"suspension blockId must be a non-empty trimmed string"
|
|
74833
|
+
);
|
|
74834
|
+
}
|
|
74835
|
+
if (this.blockId !== null && this.blockId !== blockId) {
|
|
74836
|
+
throw new Error(
|
|
74837
|
+
`story ${this.storyId} is already suspending for block ${this.blockId}`
|
|
74838
|
+
);
|
|
74839
|
+
}
|
|
74840
|
+
this.blockId ??= blockId;
|
|
74841
|
+
}
|
|
74842
|
+
};
|
|
74843
|
+
async function settledWithinBound(inFlight, timeoutMs) {
|
|
74844
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
74845
|
+
const awaited = /* @__PURE__ */ new Set();
|
|
74846
|
+
for (; ; ) {
|
|
74847
|
+
const pending = inFlight().filter((entry) => !awaited.has(entry));
|
|
74848
|
+
if (pending.length === 0) return true;
|
|
74849
|
+
const remaining = deadline - Date.now();
|
|
74850
|
+
if (remaining <= 0) return false;
|
|
74851
|
+
if (!await raceBound(Promise.allSettled(pending), remaining)) {
|
|
74852
|
+
return false;
|
|
74853
|
+
}
|
|
74854
|
+
for (const entry of pending) awaited.add(entry);
|
|
74855
|
+
}
|
|
74856
|
+
}
|
|
74857
|
+
async function raceBound(work, timeoutMs) {
|
|
74858
|
+
let timer;
|
|
74859
|
+
try {
|
|
74860
|
+
return await Promise.race([
|
|
74861
|
+
work.then(() => true),
|
|
74862
|
+
new Promise((resolve9) => {
|
|
74863
|
+
timer = setTimeout(() => resolve9(false), timeoutMs);
|
|
74864
|
+
})
|
|
74865
|
+
]);
|
|
74866
|
+
} finally {
|
|
74867
|
+
if (timer) clearTimeout(timer);
|
|
74868
|
+
}
|
|
74869
|
+
}
|
|
74870
|
+
|
|
74791
74871
|
// ../baro-orchestrator/src/harness/codex/stream-mapper.ts
|
|
74792
74872
|
function mapCodexEvent(agentId, event) {
|
|
74793
74873
|
const threadId = typeof event.thread_id === "string" ? event.thread_id : null;
|
|
@@ -75092,6 +75172,18 @@ var TurnMessageMailbox = class {
|
|
|
75092
75172
|
this.queued.push(item);
|
|
75093
75173
|
if (this.active) this.active(this.queued.shift());
|
|
75094
75174
|
}
|
|
75175
|
+
/**
|
|
75176
|
+
* Everything waiting, without blocking for more. A backend that owns its
|
|
75177
|
+
* own turn loop reads peer messages between inference rounds rather than
|
|
75178
|
+
* only at the turn boundary, which is where `waitForNext` leaves them.
|
|
75179
|
+
*/
|
|
75180
|
+
drainAll() {
|
|
75181
|
+
return this.queued.splice(0, this.queued.length);
|
|
75182
|
+
}
|
|
75183
|
+
/** End an active wait now — a suspension must not sit out the quiet timeout. */
|
|
75184
|
+
cancel() {
|
|
75185
|
+
this.active?.(null);
|
|
75186
|
+
}
|
|
75095
75187
|
waitForNext(options) {
|
|
75096
75188
|
if (this.queued.length > 0) return Promise.resolve(this.queued.shift());
|
|
75097
75189
|
if (this.active) throw new Error("already waiting for the next turn message");
|
|
@@ -77678,6 +77770,20 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
77678
77770
|
done;
|
|
77679
77771
|
turnMessages = new TurnMessageMailbox();
|
|
77680
77772
|
turnReviews = new TurnReviewMailbox();
|
|
77773
|
+
/**
|
|
77774
|
+
* Tool invocations that have started and not yet settled.
|
|
77775
|
+
*
|
|
77776
|
+
* A CLI backend gets its quiescence proof from the operating system: the
|
|
77777
|
+
* process is gone, so nothing of its can still be writing. In process
|
|
77778
|
+
* there is no such witness, and aborting does not make one — the abort
|
|
77779
|
+
* wins its race while the write it interrupted carries on. Owning the
|
|
77780
|
+
* loop supplies a better witness than the process table ever did: no
|
|
77781
|
+
* invocation can start once a suspension is requested, so an empty set is
|
|
77782
|
+
* proof rather than inference.
|
|
77783
|
+
*/
|
|
77784
|
+
inFlightTools = /* @__PURE__ */ new Set();
|
|
77785
|
+
suspension;
|
|
77786
|
+
attemptsMade = 0;
|
|
77681
77787
|
constructor(spec, opts = {}) {
|
|
77682
77788
|
super();
|
|
77683
77789
|
this.spec = {
|
|
@@ -77708,6 +77814,10 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
77708
77814
|
0,
|
|
77709
77815
|
Math.floor(opts.transportRetryDelayMs ?? 1e3)
|
|
77710
77816
|
),
|
|
77817
|
+
suspensionQuiescenceTimeoutMs: Math.max(
|
|
77818
|
+
0,
|
|
77819
|
+
Math.floor(opts.suspensionQuiescenceTimeoutMs ?? 3e4)
|
|
77820
|
+
),
|
|
77711
77821
|
baseUrl: opts.baseUrl ?? "",
|
|
77712
77822
|
apiKey: opts.apiKey ?? "",
|
|
77713
77823
|
runtimeReplanDecisionAuthority: opts.runtimeReplanDecisionAuthority ?? null,
|
|
@@ -77731,6 +77841,11 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
77731
77841
|
...this.runtimeReplanEnabled() ? [createRuntimeReplanTool(this.runtimeGraphVersion)] : []
|
|
77732
77842
|
];
|
|
77733
77843
|
setModelTools(this.model, this.tools);
|
|
77844
|
+
this.suspension = new CooperativeSuspension(
|
|
77845
|
+
this.spec.id,
|
|
77846
|
+
(timeoutMs) => settledWithinBound(() => [...this.inFlightTools], timeoutMs),
|
|
77847
|
+
this.opts.suspensionQuiescenceTimeoutMs
|
|
77848
|
+
);
|
|
77734
77849
|
this.done = new Promise((res) => {
|
|
77735
77850
|
this.resolveDone = res;
|
|
77736
77851
|
});
|
|
@@ -77800,6 +77915,7 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
77800
77915
|
}, this.spec.hardTimeoutSecs * 1e3) : null;
|
|
77801
77916
|
for (let i2 = 0; i2 < maxAttempts; i2++) {
|
|
77802
77917
|
if (this.abortController.signal.aborted) break;
|
|
77918
|
+
if (this.suspension.blocksNewWork) break;
|
|
77803
77919
|
if (i2 > 0) {
|
|
77804
77920
|
this.transition("waiting", `retrying (attempt ${i2 + 1}/${maxAttempts})`);
|
|
77805
77921
|
try {
|
|
@@ -77810,6 +77926,7 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
77810
77926
|
if (this.abortController.signal.aborted) break;
|
|
77811
77927
|
}
|
|
77812
77928
|
attempts += 1;
|
|
77929
|
+
this.attemptsMade = attempts;
|
|
77813
77930
|
try {
|
|
77814
77931
|
const result = await this.runOneAttempt(attempts);
|
|
77815
77932
|
if (this.currentPhase === "done") {
|
|
@@ -77833,6 +77950,11 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
77833
77950
|
lastError = this.abortReason ?? "story was aborted";
|
|
77834
77951
|
lastFailure ??= this.abortFailure;
|
|
77835
77952
|
}
|
|
77953
|
+
if (this.suspension.blocksNewWork) {
|
|
77954
|
+
lastError = this.suspension.outcomeText;
|
|
77955
|
+
lastFailure = void 0;
|
|
77956
|
+
this.transition("aborted", lastError);
|
|
77957
|
+
}
|
|
77836
77958
|
const durationSecs = this.startedAt ? Math.round((Date.now() - this.startedAt) / 1e3) : 0;
|
|
77837
77959
|
const success = this.currentPhase === "done";
|
|
77838
77960
|
this.envRef?.deliverSemanticEvent(
|
|
@@ -78172,6 +78294,16 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
78172
78294
|
}
|
|
78173
78295
|
context = context.addContextItem(outItem);
|
|
78174
78296
|
}
|
|
78297
|
+
if (this.suspension.blocksNewWork) {
|
|
78298
|
+
return {
|
|
78299
|
+
context,
|
|
78300
|
+
success: false,
|
|
78301
|
+
assistantText,
|
|
78302
|
+
usage,
|
|
78303
|
+
error: this.suspension.outcomeText,
|
|
78304
|
+
retryable: false
|
|
78305
|
+
};
|
|
78306
|
+
}
|
|
78175
78307
|
if (controlPlaneOutcomeUnknown || this.abortController.signal.aborted) {
|
|
78176
78308
|
return {
|
|
78177
78309
|
context,
|
|
@@ -78286,7 +78418,36 @@ var OpenAIStoryAgent = class extends BaseObserver {
|
|
|
78286
78418
|
}
|
|
78287
78419
|
async runOrdinaryTool(call) {
|
|
78288
78420
|
const tool = this.tools.find((candidate) => candidate.name === call.name);
|
|
78289
|
-
|
|
78421
|
+
if (!tool) return `Error: tool '${call.name}' not registered`;
|
|
78422
|
+
if (this.suspension.blocksNewWork) return this.suspendedToolOutput();
|
|
78423
|
+
const invocation = runToolSafely(
|
|
78424
|
+
tool,
|
|
78425
|
+
call.args,
|
|
78426
|
+
this.abortController.signal
|
|
78427
|
+
);
|
|
78428
|
+
this.inFlightTools.add(invocation);
|
|
78429
|
+
const settled = () => {
|
|
78430
|
+
this.inFlightTools.delete(invocation);
|
|
78431
|
+
};
|
|
78432
|
+
invocation.then(settled, settled);
|
|
78433
|
+
return invocation;
|
|
78434
|
+
}
|
|
78435
|
+
suspendedToolOutput() {
|
|
78436
|
+
return "Error: this story is suspending on an accepted dependency block; no further tool calls will run in this session.";
|
|
78437
|
+
}
|
|
78438
|
+
/**
|
|
78439
|
+
* Stop taking work and resolve only once nothing of this story's can still
|
|
78440
|
+
* write, so the host may snapshot the worktree. Inference is left to end
|
|
78441
|
+
* on its own bound — a model call touches no files, and killing it would
|
|
78442
|
+
* buy nothing the witness does not already give.
|
|
78443
|
+
*/
|
|
78444
|
+
async suspend(blockId) {
|
|
78445
|
+
const pending = this.suspension.request(blockId, () => ({
|
|
78446
|
+
attempts: this.attemptsMade,
|
|
78447
|
+
durationSecs: this.startedAt ? Math.round((Date.now() - this.startedAt) / 1e3) : 0
|
|
78448
|
+
}));
|
|
78449
|
+
if (this.suspension.blocksNewWork) this.turnMessages.cancel();
|
|
78450
|
+
return pending;
|
|
78290
78451
|
}
|
|
78291
78452
|
async proposeRuntimeReplan(call) {
|
|
78292
78453
|
const parsed = parseRuntimeReplanArgs(call.args);
|
|
@@ -79652,7 +79813,7 @@ function failureSummary2(failure) {
|
|
|
79652
79813
|
// ../baro-orchestrator/src/execution/story-executor.ts
|
|
79653
79814
|
var LocalStoryExecutor = class {
|
|
79654
79815
|
supportsCooperativeSuspend(route) {
|
|
79655
|
-
return route.backend
|
|
79816
|
+
return laneQuiescenceWitness(route.backend) === "self" ? true : PROCESS_TREE_CAPABILITIES.cooperativeQuiescenceObservation;
|
|
79656
79817
|
}
|
|
79657
79818
|
start(req, route, cwd, env, opts) {
|
|
79658
79819
|
if (opts.requireProcessQuiescenceCertification === true && route.backend !== "openai" && !PROCESS_TREE_CAPABILITIES.cooperativeQuiescenceObservation) {
|
|
@@ -82954,7 +83115,7 @@ var ContinuousGateRunner = class extends BaseObserver {
|
|
|
82954
83115
|
if (result.commands.some((command) => wasKilled(command))) {
|
|
82955
83116
|
throw new Error("a gate command was killed before it reported");
|
|
82956
83117
|
}
|
|
82957
|
-
return result.commands.map((command) => ({
|
|
83118
|
+
return result.commands.filter((command) => command.status !== "skipped").map((command) => ({
|
|
82958
83119
|
label: command.command,
|
|
82959
83120
|
passed: command.status === "passed",
|
|
82960
83121
|
detail: command.status === "passed" ? "" : command.tail ?? ""
|