omp-conductor 0.18.1 → 0.19.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/README.md +106 -41
- package/REFERENCE.md +866 -31
- package/agents/to-spec.md +6 -2
- package/package.json +1 -1
- package/schema/config.schema.json +32 -1
- package/src/admission.ts +212 -26
- package/src/arm-challenge.ts +250 -57
- package/src/ask.ts +288 -1
- package/src/briefs/orchestrator.md +27 -13
- package/src/briefs/to-spec.md +6 -2
- package/src/cli.ts +127 -2
- package/src/command-help.ts +9 -1
- package/src/command-manifest.ts +52 -8
- package/src/commands/arm.ts +6 -2
- package/src/commands/context.ts +2 -0
- package/src/commands/intake.ts +4 -19
- package/src/commands/message.ts +26 -2
- package/src/commands/reconcile-units.ts +104 -0
- package/src/commands/release-composition.ts +232 -0
- package/src/commands/resume.ts +2 -27
- package/src/commands/setup.ts +101 -16
- package/src/commands/stats.ts +11 -30
- package/src/commands/tail.ts +31 -1
- package/src/commands/upgrade.ts +20 -3
- package/src/commands/verb.ts +2 -1
- package/src/commands/watch.ts +4 -17
- package/src/config-schema.ts +38 -6
- package/src/config.ts +103 -8
- package/src/credential-class.ts +366 -0
- package/src/daemon.ts +1368 -529
- package/src/dashboard/app.js +504 -2
- package/src/dashboard/controls.ts +336 -0
- package/src/dashboard/index.html +30 -0
- package/src/dashboard/server.ts +271 -30
- package/src/dashboard/style.css +116 -0
- package/src/dashboard/transcript.ts +173 -0
- package/src/decisions.ts +19 -11
- package/src/doctor.ts +431 -148
- package/src/escalate.ts +22 -11
- package/src/failure-class.ts +59 -0
- package/src/fleet.ts +587 -230
- package/src/host.ts +6 -455
- package/src/omp-settings.ts +19 -0
- package/src/omp.ts +40 -56
- package/src/orchestrator-tick.ts +564 -121
- package/src/pause.ts +233 -0
- package/src/session-host.ts +6 -41
- package/src/settlement.ts +159 -2
- package/src/setup-answers.ts +97 -0
- package/src/setup-host.ts +343 -1160
- package/src/setup-install.ts +204 -27
- package/src/setup-wizard.ts +252 -51
- package/src/setup.ts +87 -4
- package/src/spend-telemetry.ts +117 -0
- package/src/stats.ts +35 -0
- package/src/status-render.ts +485 -19
- package/src/store.ts +1229 -55
- package/src/telegram-freshness.ts +269 -0
- package/src/to-spec.ts +50 -2
- package/src/types.ts +759 -10
- package/src/unblock.ts +22 -0
- package/src/unit-reconcile.ts +303 -0
- package/src/upgrade-verify.ts +8 -1
- package/src/upgrade.ts +299 -12
- package/src/verbs/actions.ts +124 -10
- package/src/verbs/protocol.ts +70 -2
- package/src/verbs/server.ts +485 -11
- package/src/wake.ts +48 -0
- package/src/worker.ts +401 -14
package/src/escalate.ts
CHANGED
|
@@ -1040,13 +1040,14 @@ export interface TelegramPlumbingProbe {
|
|
|
1040
1040
|
}
|
|
1041
1041
|
|
|
1042
1042
|
/**
|
|
1043
|
-
* The
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1043
|
+
* The transcript-surface input for {@link telegramPlumbingVerdict}'s session
|
|
1044
|
+
* identity checks: the directories the claim-only proof judges recipients
|
|
1045
|
+
* against (a claim's, the DM owner's, or the local poll owner's session file
|
|
1046
|
+
* must land inside, or a flat reply could land where no challenge would have
|
|
1047
|
+
* looked). Since #614 the *challenge* proof reads conductor acknowledgement
|
|
1048
|
+
* state and never opens a transcript, so callers judging challenge-mode
|
|
1049
|
+
* plumbing omit this — passing it there could report arming impossible while
|
|
1050
|
+
* `arm` succeeds.
|
|
1050
1051
|
*/
|
|
1051
1052
|
export type TelegramPlumbingScan = { dirs: readonly string[] };
|
|
1052
1053
|
|
|
@@ -1074,7 +1075,7 @@ export type TelegramPlumbingScan = { dirs: readonly string[] };
|
|
|
1074
1075
|
*/
|
|
1075
1076
|
export function telegramPlumbingVerdict(
|
|
1076
1077
|
sendTopic: number | undefined,
|
|
1077
|
-
scan: TelegramPlumbingScan,
|
|
1078
|
+
scan: TelegramPlumbingScan | undefined,
|
|
1078
1079
|
probe: TelegramPlumbingProbe,
|
|
1079
1080
|
): TelegramPlumbingVerdict {
|
|
1080
1081
|
const { registry, poll, dmOwner, channel, alive, lockAlive, fresh } = probe;
|
|
@@ -1085,14 +1086,21 @@ export function telegramPlumbingVerdict(
|
|
|
1085
1086
|
const claim = registry.claims.find((c) => c.threadId === sendTopic);
|
|
1086
1087
|
if (claim === undefined) return { ok: false, reason: "no-topic-claim" };
|
|
1087
1088
|
if (claim.pid === undefined || !alive(claim.pid)) return { ok: false, reason: "claim-dead" };
|
|
1088
|
-
if (
|
|
1089
|
+
if (
|
|
1090
|
+
scan !== undefined &&
|
|
1091
|
+
claim.sessionFile !== undefined &&
|
|
1092
|
+
!sessionWithinScan(claim.sessionFile, scan.dirs)
|
|
1093
|
+
) {
|
|
1089
1094
|
return { ok: false, reason: "claim-session-outside" };
|
|
1090
1095
|
}
|
|
1091
1096
|
recipient = "claim";
|
|
1092
1097
|
} else {
|
|
1093
1098
|
if (dmOwner.kind === "ok") {
|
|
1094
1099
|
if (dmOwner.owner.pid === undefined || !alive(dmOwner.owner.pid)) return { ok: false, reason: "dm-owner-dead" };
|
|
1095
|
-
if (
|
|
1100
|
+
if (
|
|
1101
|
+
scan !== undefined &&
|
|
1102
|
+
(dmOwner.owner.sessionFile === undefined || !sessionWithinScan(dmOwner.owner.sessionFile, scan.dirs))
|
|
1103
|
+
) {
|
|
1096
1104
|
return { ok: false, reason: "dm-owner-unrelated" };
|
|
1097
1105
|
}
|
|
1098
1106
|
recipient = "dm-owner";
|
|
@@ -1102,7 +1110,10 @@ export function telegramPlumbingVerdict(
|
|
|
1102
1110
|
// record must name that session. A daemon-only or unrelated owner is no
|
|
1103
1111
|
// recipient: the reply would land in a transcript arm never scans.
|
|
1104
1112
|
if (poll.kind !== "ok") return { ok: false, reason: "no-dm-owner" };
|
|
1105
|
-
if (
|
|
1113
|
+
if (
|
|
1114
|
+
scan !== undefined &&
|
|
1115
|
+
(poll.owner.sessionFile === undefined || !sessionWithinScan(poll.owner.sessionFile, scan.dirs))
|
|
1116
|
+
) {
|
|
1106
1117
|
return { ok: false, reason: "no-dm-owner" };
|
|
1107
1118
|
}
|
|
1108
1119
|
recipient = "poller";
|
package/src/failure-class.ts
CHANGED
|
@@ -103,6 +103,23 @@ export function infraLogSignature(log: string): string | undefined {
|
|
|
103
103
|
return INFRA_LOG_SIGNATURES.find((signature) => lower.includes(signature));
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* The cap kills that produced nothing: the worker reached a ceiling with no PR,
|
|
108
|
+
* no observed head and no salvage commit.
|
|
109
|
+
*
|
|
110
|
+
* Named here — beside the table that assigns them — because two callers need
|
|
111
|
+
* exactly the same set and neither may guess at it: the escalate recovery
|
|
112
|
+
* quotes the transcript's last tool calls for these classes, and the one-shot
|
|
113
|
+
* model escalation (#807) fires only for these. Their `-progress` siblings are
|
|
114
|
+
* deliberately absent: a cap kill *with* work to continue from is a
|
|
115
|
+
* decomposition verdict, and retrying it on a stronger tier would hide an
|
|
116
|
+
* oversized slice behind a capability problem.
|
|
117
|
+
*/
|
|
118
|
+
export const SPINNING_CAP_CLASSES: readonly FailureClass[] = [
|
|
119
|
+
"turn-cap-spinning",
|
|
120
|
+
"wall-clock-cap-spinning",
|
|
121
|
+
];
|
|
122
|
+
|
|
106
123
|
/**
|
|
107
124
|
* A stable fingerprint of the infrastructure signature list (#638). The
|
|
108
125
|
* historical reconciliation persists a per-project review cursor stamped with
|
|
@@ -315,6 +332,48 @@ export function dispatchInfra(
|
|
|
315
332
|
// front of the thrown message, so a bare /^git / would never match a real row.
|
|
316
333
|
const DISPATCH_GIT_ERROR = /^(?:Error: )?git .+ exited \d+/s;
|
|
317
334
|
|
|
335
|
+
/**
|
|
336
|
+
* Evidence that one review round's resumed session never got as far as a turn,
|
|
337
|
+
* or `undefined` when the round actually ran (#903).
|
|
338
|
+
*
|
|
339
|
+
* The measurement is the round's OWN session, never the run row's cumulative
|
|
340
|
+
* counters: a revision reuses its run, so `run.turns` already carries every
|
|
341
|
+
* turn the original attempt spent and would never read zero.
|
|
342
|
+
*
|
|
343
|
+
* Deliberately wider than {@link neverStarted}: any zero-turn round that
|
|
344
|
+
* produced nothing counts, whatever the error text says. The reference
|
|
345
|
+
* incident is why. During the 2026-08-21/22 worker-UID outage every revision
|
|
346
|
+
* dispatch died at turn 0 on `EACCES .../config.yml` — an error string no
|
|
347
|
+
* signature list contained — and each death still consumed one of the PR's
|
|
348
|
+
* three review rounds, until the ceiling refused any further round and the PR
|
|
349
|
+
* could not be finished at all. The discriminator that matters is "did the
|
|
350
|
+
* resumed session take a turn", not which errno the host produced; matching on
|
|
351
|
+
* errno text is how the next outage spells its failure differently and lands
|
|
352
|
+
* back in the same deadlock.
|
|
353
|
+
*
|
|
354
|
+
* Two things keep the widening honest, and both are the caller's:
|
|
355
|
+
* - it decides only whether a round is CHARGED, never whether a run is
|
|
356
|
+
* classified — run-level classification is untouched, so an unrecognised
|
|
357
|
+
* turn-0 run still reads `unknown` and still spends its attempt;
|
|
358
|
+
* - a retried round is bounded by {@link REVIEW_ROUND_INFRA_MAX_RETRIES}, so
|
|
359
|
+
* a permanently broken host escalates instead of retrying forever.
|
|
360
|
+
*
|
|
361
|
+
* A round that produced a PR, a head or a salvage did work no matter what its
|
|
362
|
+
* turn counter says, and is never this.
|
|
363
|
+
*/
|
|
364
|
+
export function reviewRoundNeverWorked(round: {
|
|
365
|
+
turns: number;
|
|
366
|
+
prUrl?: string;
|
|
367
|
+
headSha?: string;
|
|
368
|
+
salvageSha?: string;
|
|
369
|
+
}): string | undefined {
|
|
370
|
+
if (round.turns !== 0) return undefined;
|
|
371
|
+
if (round.prUrl !== undefined || round.headSha !== undefined || round.salvageSha !== undefined) {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
return "the resumed session never took a turn: no turn, no commit and no pushed head";
|
|
375
|
+
}
|
|
376
|
+
|
|
318
377
|
/** The prefix the draining/restarting process writes to a run it killed. */
|
|
319
378
|
const ADMIN_RESTART_MARKER = "admin restart:";
|
|
320
379
|
|