agy-worker-mcp 0.3.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/CHANGELOG.md +141 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/commands/agy-ceiling.md +31 -0
- package/dist/broker/blockers.d.ts +86 -0
- package/dist/broker/blockers.d.ts.map +1 -0
- package/dist/broker/blockers.js +390 -0
- package/dist/broker/blockers.js.map +1 -0
- package/dist/broker/outcome.d.ts +85 -0
- package/dist/broker/outcome.d.ts.map +1 -0
- package/dist/broker/outcome.js +147 -0
- package/dist/broker/outcome.js.map +1 -0
- package/dist/broker/reconcile.d.ts +74 -0
- package/dist/broker/reconcile.d.ts.map +1 -0
- package/dist/broker/reconcile.js +415 -0
- package/dist/broker/reconcile.js.map +1 -0
- package/dist/broker/result.d.ts +88 -0
- package/dist/broker/result.d.ts.map +1 -0
- package/dist/broker/result.js +234 -0
- package/dist/broker/result.js.map +1 -0
- package/dist/broker/verify.d.ts +52 -0
- package/dist/broker/verify.d.ts.map +1 -0
- package/dist/broker/verify.js +174 -0
- package/dist/broker/verify.js.map +1 -0
- package/dist/contract/errors.d.ts +119 -0
- package/dist/contract/errors.d.ts.map +1 -0
- package/dist/contract/errors.js +99 -0
- package/dist/contract/errors.js.map +1 -0
- package/dist/contract/index.d.ts +10 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +10 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/paths.d.ts +139 -0
- package/dist/contract/paths.d.ts.map +1 -0
- package/dist/contract/paths.js +359 -0
- package/dist/contract/paths.js.map +1 -0
- package/dist/contract/schema.sql +64 -0
- package/dist/contract/types.d.ts +1113 -0
- package/dist/contract/types.d.ts.map +1 -0
- package/dist/contract/types.js +114 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/events/cursor.d.ts +65 -0
- package/dist/events/cursor.d.ts.map +1 -0
- package/dist/events/cursor.js +286 -0
- package/dist/events/cursor.js.map +1 -0
- package/dist/events/detect.d.ts +58 -0
- package/dist/events/detect.d.ts.map +1 -0
- package/dist/events/detect.js +183 -0
- package/dist/events/detect.js.map +1 -0
- package/dist/events/normalize.d.ts +30 -0
- package/dist/events/normalize.d.ts.map +1 -0
- package/dist/events/normalize.js +158 -0
- package/dist/events/normalize.js.map +1 -0
- package/dist/events/parse.d.ts +41 -0
- package/dist/events/parse.d.ts.map +1 -0
- package/dist/events/parse.js +89 -0
- package/dist/events/parse.js.map +1 -0
- package/dist/gate/bind.d.ts +28 -0
- package/dist/gate/bind.d.ts.map +1 -0
- package/dist/gate/bind.js +154 -0
- package/dist/gate/bind.js.map +1 -0
- package/dist/gate/gate.d.ts +139 -0
- package/dist/gate/gate.d.ts.map +1 -0
- package/dist/gate/gate.js +488 -0
- package/dist/gate/gate.js.map +1 -0
- package/dist/gate/hooks-file.d.ts +55 -0
- package/dist/gate/hooks-file.d.ts.map +1 -0
- package/dist/gate/hooks-file.js +104 -0
- package/dist/gate/hooks-file.js.map +1 -0
- package/dist/gate.d.ts +3 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +20 -0
- package/dist/gate.js.map +1 -0
- package/dist/policy/ceiling-review.d.ts +22 -0
- package/dist/policy/ceiling-review.d.ts.map +1 -0
- package/dist/policy/ceiling-review.js +170 -0
- package/dist/policy/ceiling-review.js.map +1 -0
- package/dist/policy/ceiling.d.ts +128 -0
- package/dist/policy/ceiling.d.ts.map +1 -0
- package/dist/policy/ceiling.js +273 -0
- package/dist/policy/ceiling.js.map +1 -0
- package/dist/policy/containment.d.ts +100 -0
- package/dist/policy/containment.d.ts.map +1 -0
- package/dist/policy/containment.js +1071 -0
- package/dist/policy/containment.js.map +1 -0
- package/dist/policy/hard-deny.d.ts +28 -0
- package/dist/policy/hard-deny.d.ts.map +1 -0
- package/dist/policy/hard-deny.js +78 -0
- package/dist/policy/hard-deny.js.map +1 -0
- package/dist/policy/profiles.d.ts +82 -0
- package/dist/policy/profiles.d.ts.map +1 -0
- package/dist/policy/profiles.js +317 -0
- package/dist/policy/profiles.js.map +1 -0
- package/dist/policy/rules.d.ts +138 -0
- package/dist/policy/rules.d.ts.map +1 -0
- package/dist/policy/rules.js +1180 -0
- package/dist/policy/rules.js.map +1 -0
- package/dist/policy/seatbelt.d.ts +32 -0
- package/dist/policy/seatbelt.d.ts.map +1 -0
- package/dist/policy/seatbelt.js +59 -0
- package/dist/policy/seatbelt.js.map +1 -0
- package/dist/policy/tools.d.ts +36 -0
- package/dist/policy/tools.d.ts.map +1 -0
- package/dist/policy/tools.js +94 -0
- package/dist/policy/tools.js.map +1 -0
- package/dist/runner/gate-watchdog.d.ts +93 -0
- package/dist/runner/gate-watchdog.d.ts.map +1 -0
- package/dist/runner/gate-watchdog.js +153 -0
- package/dist/runner/gate-watchdog.js.map +1 -0
- package/dist/runner/idle.d.ts +51 -0
- package/dist/runner/idle.d.ts.map +1 -0
- package/dist/runner/idle.js +81 -0
- package/dist/runner/idle.js.map +1 -0
- package/dist/runner/inbox.d.ts +56 -0
- package/dist/runner/inbox.d.ts.map +1 -0
- package/dist/runner/inbox.js +134 -0
- package/dist/runner/inbox.js.map +1 -0
- package/dist/runner/reap.d.ts +65 -0
- package/dist/runner/reap.d.ts.map +1 -0
- package/dist/runner/reap.js +168 -0
- package/dist/runner/reap.js.map +1 -0
- package/dist/runner/runner.d.ts +51 -0
- package/dist/runner/runner.d.ts.map +1 -0
- package/dist/runner/runner.js +289 -0
- package/dist/runner/runner.js.map +1 -0
- package/dist/runner/spawn.d.ts +98 -0
- package/dist/runner/spawn.d.ts.map +1 -0
- package/dist/runner/spawn.js +230 -0
- package/dist/runner/spawn.js.map +1 -0
- package/dist/runner/verify.d.ts +24 -0
- package/dist/runner/verify.d.ts.map +1 -0
- package/dist/runner/verify.js +108 -0
- package/dist/runner/verify.js.map +1 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +15 -0
- package/dist/runner.js.map +1 -0
- package/dist/server/context.d.ts +41 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +59 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/instructions.d.ts +13 -0
- package/dist/server/instructions.d.ts.map +1 -0
- package/dist/server/instructions.js +121 -0
- package/dist/server/instructions.js.map +1 -0
- package/dist/server/server.d.ts +20 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +55 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/tools/cancel.d.ts +24 -0
- package/dist/server/tools/cancel.d.ts.map +1 -0
- package/dist/server/tools/cancel.js +59 -0
- package/dist/server/tools/cancel.js.map +1 -0
- package/dist/server/tools/capabilities.d.ts +36 -0
- package/dist/server/tools/capabilities.d.ts.map +1 -0
- package/dist/server/tools/capabilities.js +116 -0
- package/dist/server/tools/capabilities.js.map +1 -0
- package/dist/server/tools/ceiling.d.ts +31 -0
- package/dist/server/tools/ceiling.d.ts.map +1 -0
- package/dist/server/tools/ceiling.js +121 -0
- package/dist/server/tools/ceiling.js.map +1 -0
- package/dist/server/tools/index.d.ts +38 -0
- package/dist/server/tools/index.d.ts.map +1 -0
- package/dist/server/tools/index.js +115 -0
- package/dist/server/tools/index.js.map +1 -0
- package/dist/server/tools/listJobs.d.ts +30 -0
- package/dist/server/tools/listJobs.d.ts.map +1 -0
- package/dist/server/tools/listJobs.js +37 -0
- package/dist/server/tools/listJobs.js.map +1 -0
- package/dist/server/tools/logs.d.ts +31 -0
- package/dist/server/tools/logs.d.ts.map +1 -0
- package/dist/server/tools/logs.js +96 -0
- package/dist/server/tools/logs.js.map +1 -0
- package/dist/server/tools/result.d.ts +32 -0
- package/dist/server/tools/result.d.ts.map +1 -0
- package/dist/server/tools/result.js +95 -0
- package/dist/server/tools/result.js.map +1 -0
- package/dist/server/tools/send.d.ts +37 -0
- package/dist/server/tools/send.d.ts.map +1 -0
- package/dist/server/tools/send.js +71 -0
- package/dist/server/tools/send.js.map +1 -0
- package/dist/server/tools/sessions.d.ts +28 -0
- package/dist/server/tools/sessions.d.ts.map +1 -0
- package/dist/server/tools/sessions.js +54 -0
- package/dist/server/tools/sessions.js.map +1 -0
- package/dist/server/tools/start.d.ts +107 -0
- package/dist/server/tools/start.d.ts.map +1 -0
- package/dist/server/tools/start.js +482 -0
- package/dist/server/tools/start.js.map +1 -0
- package/dist/server/tools/wait.d.ts +29 -0
- package/dist/server/tools/wait.d.ts.map +1 -0
- package/dist/server/tools/wait.js +120 -0
- package/dist/server/tools/wait.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +17 -0
- package/dist/server.js.map +1 -0
- package/dist/setup/install.d.ts +52 -0
- package/dist/setup/install.d.ts.map +1 -0
- package/dist/setup/install.js +108 -0
- package/dist/setup/install.js.map +1 -0
- package/dist/setup.d.ts +3 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +24 -0
- package/dist/setup.js.map +1 -0
- package/dist/store/db.d.ts +49 -0
- package/dist/store/db.d.ts.map +1 -0
- package/dist/store/db.js +101 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/jobs.d.ts +45 -0
- package/dist/store/jobs.d.ts.map +1 -0
- package/dist/store/jobs.js +150 -0
- package/dist/store/jobs.js.map +1 -0
- package/dist/store/locks.d.ts +60 -0
- package/dist/store/locks.d.ts.map +1 -0
- package/dist/store/locks.js +172 -0
- package/dist/store/locks.js.map +1 -0
- package/dist/store/sessions.d.ts +37 -0
- package/dist/store/sessions.d.ts.map +1 -0
- package/dist/store/sessions.js +117 -0
- package/dist/store/sessions.js.map +1 -0
- package/dist/trace/digest.d.ts +77 -0
- package/dist/trace/digest.d.ts.map +1 -0
- package/dist/trace/digest.js +481 -0
- package/dist/trace/digest.js.map +1 -0
- package/package.json +68 -0
- package/skills/agy-ceiling/SKILL.md +72 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { JobRow } from '../contract/types.js';
|
|
2
|
+
import type { Store } from '../store/db.js';
|
|
3
|
+
/**
|
|
4
|
+
* The daemon replacement.
|
|
5
|
+
*
|
|
6
|
+
* Every MCP tool handler calls this first. It has to be cheap, idempotent, and
|
|
7
|
+
* guarded by `BEGIN IMMEDIATE` so two servers racing to finalize the same job
|
|
8
|
+
* write the result exactly once.
|
|
9
|
+
*
|
|
10
|
+
* | observation | action |
|
|
11
|
+
* | ---------------------------------------- | --------------------------------------------- |
|
|
12
|
+
* | `exit_code` exists, lifecycle = running | finalize → verify → outcome/headline → unlock |
|
|
13
|
+
* | pid gone, runner alive (`runner_pid`) | still running — the runner is in `verifying` |
|
|
14
|
+
* | | or its writeback window; leave it alone |
|
|
15
|
+
* | pid gone, runner gone, no `exit_code` | `process_error` (the runner was killed) |
|
|
16
|
+
* | `deadline_at` elapsed, pid alive | `timed_out` after killpg |
|
|
17
|
+
* | `deadline_at` elapsed, runner `verifying`| wait: verify has its own clock (backstop below)|
|
|
18
|
+
* | pid alive but start_time mismatch | pid reused → `orphaned` |
|
|
19
|
+
*
|
|
20
|
+
* `pid` is agy's, not the runner's (`spawn.ts`). Between agy's exit and the
|
|
21
|
+
* runner's final write the runner may be running `verify_command` for
|
|
22
|
+
* minutes (0.2.2 PR1): `state.json.phase === 'verifying'`
|
|
23
|
+
* with a live `state.json.runner_pid` is the only way to tell that apart from
|
|
24
|
+
* a lost runner, and every row above that would otherwise conclude "gone"
|
|
25
|
+
* checks it first.
|
|
26
|
+
*
|
|
27
|
+
* The order above is the order these are checked in, and it matters: `exit_code`
|
|
28
|
+
* existing always wins (even over a passed deadline — the runner beat us to it),
|
|
29
|
+
* and a passed deadline is checked before a pid-reuse mismatch. Among the last
|
|
30
|
+
* two, pid reuse is checked first: it is the only *positive* observation of the
|
|
31
|
+
* three, while "pid gone" covers both an ordinary exit and a lost runner.
|
|
32
|
+
*/
|
|
33
|
+
export interface ReconcileOptions {
|
|
34
|
+
/** Freeze the clock in tests. */
|
|
35
|
+
now?: number;
|
|
36
|
+
/** Skip the (more expensive) verification pass; used by hot read-only paths. */
|
|
37
|
+
skipVerify?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ReconcileReport {
|
|
40
|
+
checked: number;
|
|
41
|
+
finalized: string[];
|
|
42
|
+
timedOut: string[];
|
|
43
|
+
orphaned: string[];
|
|
44
|
+
processErrors: string[];
|
|
45
|
+
locksReleased: number;
|
|
46
|
+
durationMs: number;
|
|
47
|
+
}
|
|
48
|
+
/** Sweep every live job. Safe to call on every tool invocation. */
|
|
49
|
+
export declare function reconcile(store: Store, opts?: ReconcileOptions): Promise<ReconcileReport>;
|
|
50
|
+
/**
|
|
51
|
+
* Reconcile one job and return its current row.
|
|
52
|
+
*
|
|
53
|
+
* A no-op when the job is already `finished`: a second caller arriving after
|
|
54
|
+
* finalization reads the existing `broker-result.json`, not rebuilds it.
|
|
55
|
+
*/
|
|
56
|
+
export declare function reconcileJob(store: Store, job: JobRow, opts?: ReconcileOptions): Promise<JobRow>;
|
|
57
|
+
/**
|
|
58
|
+
* Finalize a job whose `exit_code` file has appeared: run verification, decide
|
|
59
|
+
* the outcome, write `broker-result.json`, set `lifecycle = 'finished'`, release
|
|
60
|
+
* locks. All inside one transaction.
|
|
61
|
+
*
|
|
62
|
+
* The job's *incoming* lifecycle carries one more fact: `'canceling'` means
|
|
63
|
+
* `agy_cancel` had already asked for this exit, so the outcome is `canceled`
|
|
64
|
+
* even though a normal exit code came back. `timedOut` carries the other one,
|
|
65
|
+
* from `state.json`: the runner's watchdog killed the group at the deadline,
|
|
66
|
+
* which is not an ordinary failure however the exit code reads.
|
|
67
|
+
*/
|
|
68
|
+
export declare function finalizeJob(store: Store, job: JobRow, exitCode: number, nowMs: number, timedOut?: boolean): JobRow;
|
|
69
|
+
export type AbnormalKind = 'process_error' | 'orphaned' | 'timed_out';
|
|
70
|
+
/** Opportunistic cleanup of old job directories, triggered from `agy_start`. */
|
|
71
|
+
export declare function cleanupOldJobs(store: Store, maxAgeMs: number): number;
|
|
72
|
+
/** Convenience: reconcile one job by id, fetching its current row first. */
|
|
73
|
+
export declare function reconcileJobById(store: Store, jobId: string, opts?: ReconcileOptions): Promise<JobRow>;
|
|
74
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/broker/reconcile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,MAAM,EAAgB,MAAM,sBAAsB,CAAA;AAMjF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAO3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CACnB;AA0BD,mEAAmE;AACnE,wBAAsB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0C/F;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0GtG;AA0CD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,UAAQ,GACf,MAAM,CAiBR;AAED,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,UAAU,GAAG,WAAW,CAAA;AAmIrE,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoBrE;AA8BD,4EAA4E;AAC5E,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAG5G"}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { jobPaths, readJsonIfExists } from '../contract/paths.js';
|
|
3
|
+
import { readLinesFrom } from '../events/cursor.js';
|
|
4
|
+
import { okEvents, parseEventLines } from '../events/parse.js';
|
|
5
|
+
import { removeGateHook } from '../gate/hooks-file.js';
|
|
6
|
+
import { checkProcessIdentity, isPidAlive, isSameProcess, killProcessGroup } from '../runner/reap.js';
|
|
7
|
+
import { now, transaction } from '../store/db.js';
|
|
8
|
+
import { getJob, listJobs, LIVE_LIFECYCLES, tryGetJob, updateJob } from '../store/jobs.js';
|
|
9
|
+
import { listLocks, releaseJobLocks } from '../store/locks.js';
|
|
10
|
+
import { bindConversationId, getSession } from '../store/sessions.js';
|
|
11
|
+
import { buildBrokerResult, writeBrokerResult } from './result.js';
|
|
12
|
+
/**
|
|
13
|
+
* How long the runner is given to write `exit_code` after its agy child is
|
|
14
|
+
* already gone. The runner only has to `writeFileAtomic` a couple of bytes from
|
|
15
|
+
* an `exit` handler, so this is generous; it only ever costs latency on a run
|
|
16
|
+
* that really did lose its runner.
|
|
17
|
+
*/
|
|
18
|
+
const RUNNER_WRITEBACK_GRACE_MS = 1500;
|
|
19
|
+
/**
|
|
20
|
+
* How long past `deadline_at + verify.timeout_ms` a `verifying` runner is
|
|
21
|
+
* still trusted to finish on its own before reconcile stops deferring to it.
|
|
22
|
+
* The runner enforces `verify_timeout_ms` itself; this only bounds the case
|
|
23
|
+
* where that enforcement somehow never returns.
|
|
24
|
+
*/
|
|
25
|
+
const VERIFY_BACKSTOP_MS = 60_000;
|
|
26
|
+
/** Lifecycles that can have a live process and so are worth reconciling. */
|
|
27
|
+
const RECONCILABLE_LIFECYCLES = [
|
|
28
|
+
'queued',
|
|
29
|
+
'starting',
|
|
30
|
+
'running',
|
|
31
|
+
'canceling',
|
|
32
|
+
];
|
|
33
|
+
/** Sweep every live job. Safe to call on every tool invocation. */
|
|
34
|
+
export async function reconcile(store, opts) {
|
|
35
|
+
const start = Date.now();
|
|
36
|
+
const nowMs = opts?.now ?? now();
|
|
37
|
+
const candidates = listJobs(store, { lifecycle: [...RECONCILABLE_LIFECYCLES], limit: 10000 });
|
|
38
|
+
const locksBefore = listLocks(store).length;
|
|
39
|
+
const report = {
|
|
40
|
+
checked: 0,
|
|
41
|
+
finalized: [],
|
|
42
|
+
timedOut: [],
|
|
43
|
+
orphaned: [],
|
|
44
|
+
processErrors: [],
|
|
45
|
+
locksReleased: 0,
|
|
46
|
+
durationMs: 0,
|
|
47
|
+
};
|
|
48
|
+
for (const job of candidates) {
|
|
49
|
+
report.checked++;
|
|
50
|
+
const before = job.lifecycle;
|
|
51
|
+
const after = await reconcileJob(store, job, { ...opts, now: nowMs, skipVerify: opts?.skipVerify });
|
|
52
|
+
if (before === 'finished' || after.lifecycle !== 'finished')
|
|
53
|
+
continue;
|
|
54
|
+
switch (after.outcome) {
|
|
55
|
+
case 'timed_out':
|
|
56
|
+
report.timedOut.push(after.job_id);
|
|
57
|
+
break;
|
|
58
|
+
case 'orphaned':
|
|
59
|
+
report.orphaned.push(after.job_id);
|
|
60
|
+
break;
|
|
61
|
+
case 'process_error':
|
|
62
|
+
report.processErrors.push(after.job_id);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
report.finalized.push(after.job_id);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const locksAfter = listLocks(store).length;
|
|
69
|
+
report.locksReleased = Math.max(0, locksBefore - locksAfter);
|
|
70
|
+
report.durationMs = Date.now() - start;
|
|
71
|
+
return report;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reconcile one job and return its current row.
|
|
75
|
+
*
|
|
76
|
+
* A no-op when the job is already `finished`: a second caller arriving after
|
|
77
|
+
* finalization reads the existing `broker-result.json`, not rebuilds it.
|
|
78
|
+
*/
|
|
79
|
+
export async function reconcileJob(store, job, opts) {
|
|
80
|
+
if (job.lifecycle === 'finished')
|
|
81
|
+
return job;
|
|
82
|
+
const nowMs = opts?.now ?? now();
|
|
83
|
+
const paths = jobPaths(store.paths, job.job_id);
|
|
84
|
+
// The runner (which must never import `src/server/**`) records pid/pgid/
|
|
85
|
+
// proc_start_time/lifecycle only in `state.json`; reconcile is the sole place
|
|
86
|
+
// that absorbs it into the jobs row, since nothing else reads it back.
|
|
87
|
+
job = ingestRunnerState(store, job, paths.state);
|
|
88
|
+
const exitCode = readExitCode(paths.exitCode);
|
|
89
|
+
if (exitCode !== null) {
|
|
90
|
+
// The runner's own watchdog may have produced this exit code by killing the
|
|
91
|
+
// process group at the deadline. Only `state.json` carries that fact — the
|
|
92
|
+
// exit code alone is indistinguishable from an ordinary failure.
|
|
93
|
+
const runnerTimedOut = readJsonIfExists(paths.state)?.timed_out === true;
|
|
94
|
+
return finalizeJob(store, job, exitCode, nowMs, runnerTimedOut);
|
|
95
|
+
}
|
|
96
|
+
// Row 3 (checked ahead of the pid-null early return): a deadline that has
|
|
97
|
+
// passed must finalize the job even when the runner never published a pid —
|
|
98
|
+
// otherwise a runner that fails to start (or dies before spawning agy) leaves
|
|
99
|
+
// the job, and its locks, live forever.
|
|
100
|
+
const state = readJsonIfExists(paths.state);
|
|
101
|
+
if (job.deadline_at !== null && nowMs >= job.deadline_at) {
|
|
102
|
+
// `verify_command` runs on its own clock, past `deadline_at` by design
|
|
103
|
+
// (`docs/tools.md`, `verify_timeout_ms`). A runner that is verifying is
|
|
104
|
+
// not late — until even the verify clock plus a backstop has run out.
|
|
105
|
+
if (runnerIsVerifying(state)) {
|
|
106
|
+
const config = readJsonIfExists(paths.effectiveConfig);
|
|
107
|
+
const verifyBudget = job.deadline_at + (config?.verify?.timeout_ms ?? 0) + VERIFY_BACKSTOP_MS;
|
|
108
|
+
if (nowMs < verifyBudget)
|
|
109
|
+
return job;
|
|
110
|
+
}
|
|
111
|
+
if (job.pid !== null &&
|
|
112
|
+
job.pgid !== null &&
|
|
113
|
+
isPidAlive(job.pid) &&
|
|
114
|
+
isSameProcess(job.pid, job.proc_start_time)) {
|
|
115
|
+
await killProcessGroup(job.pgid);
|
|
116
|
+
}
|
|
117
|
+
const exitAfterKill = await pollExitCode(paths.exitCode);
|
|
118
|
+
if (exitAfterKill !== null) {
|
|
119
|
+
// We killed it for the deadline, so the exit code it produced is a
|
|
120
|
+
// timeout's exit code, not a failure's.
|
|
121
|
+
return finalizeJob(store, job, exitAfterKill, now(), true);
|
|
122
|
+
}
|
|
123
|
+
return finalizeAbnormal(store, job, 'timed_out', now());
|
|
124
|
+
}
|
|
125
|
+
if (job.pid === null) {
|
|
126
|
+
// Not spawned yet (still `queued`/`starting`) and its deadline (if any)
|
|
127
|
+
// has not passed. Nothing observable to reconcile.
|
|
128
|
+
return job;
|
|
129
|
+
}
|
|
130
|
+
const identity = checkProcessIdentity(job.pid, job.proc_start_time);
|
|
131
|
+
// Row 4: pid alive, but the start-time token belongs to a *different*
|
|
132
|
+
// process. Checked before row 2 because it is the only positive conclusion of
|
|
133
|
+
// the three — `gone` covers both an ordinary exit and a lost runner, and
|
|
134
|
+
// telling those apart is row 2's job.
|
|
135
|
+
if (identity === 'different') {
|
|
136
|
+
return finalizeAbnormal(store, job, 'orphaned', nowMs);
|
|
137
|
+
}
|
|
138
|
+
// Row 2: pid gone, no exit_code file. The runner was killed before it could
|
|
139
|
+
// record a result.
|
|
140
|
+
//
|
|
141
|
+
// `agy` dying is NOT by itself evidence of that: between agy's exit and the
|
|
142
|
+
// runner's `writeExitCode` there is a short window in which the pid is
|
|
143
|
+
// already gone while a perfectly healthy run is about to record exit 0.
|
|
144
|
+
// Reading only `isPidAlive` here misclassifies every normally-finishing job
|
|
145
|
+
// caught in that window as `process_error`, which a caller polling reconcile
|
|
146
|
+
// in a loop hits reliably. So re-poll for the exit_code first, and only
|
|
147
|
+
// conclude the runner is lost once the grace elapses with nothing written.
|
|
148
|
+
if (identity === 'gone') {
|
|
149
|
+
// The runner is still there (verifying, or about to write `exit_code`):
|
|
150
|
+
// nothing is lost, and the next reconcile will find the exit code. Without
|
|
151
|
+
// this check every job whose verify outlived the grace below was judged
|
|
152
|
+
// `process_error` with a real exit 0 landing seconds later (audit `08`).
|
|
153
|
+
if (runnerIsAlive(state))
|
|
154
|
+
return job;
|
|
155
|
+
const exitAfterDeath = await pollExitCode(paths.exitCode, RUNNER_WRITEBACK_GRACE_MS);
|
|
156
|
+
if (exitAfterDeath !== null) {
|
|
157
|
+
return finalizeJob(store, job, exitAfterDeath, now());
|
|
158
|
+
}
|
|
159
|
+
return finalizeAbnormal(store, job, 'process_error', now());
|
|
160
|
+
}
|
|
161
|
+
// Row 5: the gate marked this job `canceling` (on_denial: 'abort'). The gate
|
|
162
|
+
// only marks it — reconcile owns the actual killpg, and does it here rather
|
|
163
|
+
// than waiting for the deadline.
|
|
164
|
+
if (job.lifecycle === 'canceling' && job.pgid !== null) {
|
|
165
|
+
await killProcessGroup(job.pgid);
|
|
166
|
+
const exitAfterKill = await pollExitCode(paths.exitCode);
|
|
167
|
+
if (exitAfterKill !== null) {
|
|
168
|
+
return finalizeJob(store, job, exitAfterKill, now());
|
|
169
|
+
}
|
|
170
|
+
return finalizeAbnormal(store, job, 'timed_out', now());
|
|
171
|
+
}
|
|
172
|
+
// Still genuinely running.
|
|
173
|
+
return job;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Absorb `state.json` (written only by the runner) into the jobs row: pid,
|
|
177
|
+
* pgid, proc_start_time and started_at are filled in once and never
|
|
178
|
+
* contradicted (the runner never rewrites them to null), and lifecycle is only
|
|
179
|
+
* ever moved forward (`queued` → `starting` → `running`) — never used to
|
|
180
|
+
* downgrade a `canceling` mark the gate made, and `finished` is decided
|
|
181
|
+
* exclusively by the presence of `exit_code`, never by `state.json` alone.
|
|
182
|
+
*/
|
|
183
|
+
function ingestRunnerState(store, job, statePath) {
|
|
184
|
+
const state = readJsonIfExists(statePath);
|
|
185
|
+
if (!state)
|
|
186
|
+
return job;
|
|
187
|
+
const patch = {};
|
|
188
|
+
if (state.pid !== null && job.pid === null)
|
|
189
|
+
patch.pid = state.pid;
|
|
190
|
+
if (state.pgid !== null && job.pgid === null)
|
|
191
|
+
patch.pgid = state.pgid;
|
|
192
|
+
if (state.proc_start_time !== null && job.proc_start_time === null) {
|
|
193
|
+
patch.proc_start_time = state.proc_start_time;
|
|
194
|
+
}
|
|
195
|
+
if (state.started_at !== null && job.started_at === null)
|
|
196
|
+
patch.started_at = state.started_at;
|
|
197
|
+
if (state.lifecycle === 'running' && (job.lifecycle === 'queued' || job.lifecycle === 'starting')) {
|
|
198
|
+
patch.lifecycle = 'running';
|
|
199
|
+
}
|
|
200
|
+
else if (state.lifecycle === 'starting' && job.lifecycle === 'queued') {
|
|
201
|
+
patch.lifecycle = 'starting';
|
|
202
|
+
}
|
|
203
|
+
if (Object.keys(patch).length === 0)
|
|
204
|
+
return job;
|
|
205
|
+
return updateJob(store, job.job_id, patch);
|
|
206
|
+
}
|
|
207
|
+
/** `state.json` names a runner pid and that pid is alive. */
|
|
208
|
+
function runnerIsAlive(state) {
|
|
209
|
+
return state?.runner_pid != null && isPidAlive(state.runner_pid);
|
|
210
|
+
}
|
|
211
|
+
/** The runner announced `verify_command` is running, and it is still there to run it. */
|
|
212
|
+
function runnerIsVerifying(state) {
|
|
213
|
+
return state?.phase === 'verifying' && runnerIsAlive(state);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Finalize a job whose `exit_code` file has appeared: run verification, decide
|
|
217
|
+
* the outcome, write `broker-result.json`, set `lifecycle = 'finished'`, release
|
|
218
|
+
* locks. All inside one transaction.
|
|
219
|
+
*
|
|
220
|
+
* The job's *incoming* lifecycle carries one more fact: `'canceling'` means
|
|
221
|
+
* `agy_cancel` had already asked for this exit, so the outcome is `canceled`
|
|
222
|
+
* even though a normal exit code came back. `timedOut` carries the other one,
|
|
223
|
+
* from `state.json`: the runner's watchdog killed the group at the deadline,
|
|
224
|
+
* which is not an ordinary failure however the exit code reads.
|
|
225
|
+
*/
|
|
226
|
+
export function finalizeJob(store, job, exitCode, nowMs, timedOut = false) {
|
|
227
|
+
return transaction(store.db, () => {
|
|
228
|
+
const fresh = tryGetJob(store, job.job_id);
|
|
229
|
+
if (fresh === null)
|
|
230
|
+
return job;
|
|
231
|
+
if (fresh.lifecycle === 'finished')
|
|
232
|
+
return fresh;
|
|
233
|
+
const canceled = fresh.lifecycle === 'canceling';
|
|
234
|
+
return finalizeCore(store, fresh, {
|
|
235
|
+
exitCode,
|
|
236
|
+
// An explicit cancel outranks the deadline: the user asked for this exit.
|
|
237
|
+
timedOut: timedOut && !canceled,
|
|
238
|
+
canceled,
|
|
239
|
+
runnerLost: false,
|
|
240
|
+
pidReused: false,
|
|
241
|
+
nowMs,
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Finalize a job with no real `exit_code` — the runner vanished, the pid was
|
|
247
|
+
* reused, or our own deadline enforcement had to kill it directly.
|
|
248
|
+
*/
|
|
249
|
+
function finalizeAbnormal(store, job, kind, nowMs) {
|
|
250
|
+
return transaction(store.db, () => {
|
|
251
|
+
const fresh = tryGetJob(store, job.job_id);
|
|
252
|
+
if (fresh === null)
|
|
253
|
+
return job;
|
|
254
|
+
if (fresh.lifecycle === 'finished')
|
|
255
|
+
return fresh;
|
|
256
|
+
const canceled = fresh.lifecycle === 'canceling';
|
|
257
|
+
return finalizeCore(store, fresh, {
|
|
258
|
+
exitCode: null,
|
|
259
|
+
timedOut: kind === 'timed_out' && !canceled,
|
|
260
|
+
canceled,
|
|
261
|
+
runnerLost: kind === 'process_error' && !canceled,
|
|
262
|
+
pidReused: kind === 'orphaned' && !canceled,
|
|
263
|
+
nowMs,
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
/** Shared body of {@link finalizeJob} / {@link finalizeAbnormal}. Must run inside a transaction. */
|
|
268
|
+
function finalizeCore(store, job, flags) {
|
|
269
|
+
const paths = jobPaths(store.paths, job.job_id);
|
|
270
|
+
const config = readJsonIfExists(paths.effectiveConfig);
|
|
271
|
+
const expectedArtifacts = config?.expected_artifacts ?? [];
|
|
272
|
+
const jsonSchemaPath = config?.json_schema_path ?? null;
|
|
273
|
+
// Only `state.json` records that the runner's idle watchdog closed stdin; the
|
|
274
|
+
// exit code it produces is an ordinary 0.
|
|
275
|
+
const state = readJsonIfExists(paths.state);
|
|
276
|
+
const idleClosed = state?.idle_closed === true;
|
|
277
|
+
const idleClosedAfterMs = idleClosed ? (config?.idle_timeout_ms ?? null) : null;
|
|
278
|
+
// I4: null on a `state.json` predating this field, or on a job that ended
|
|
279
|
+
// before any tool step ever ran — neither forces `outcome`.
|
|
280
|
+
const gateConfirmed = state?.gate_confirmed ?? null;
|
|
281
|
+
// The runner's own end time, when it recorded one (it writes `state.json`
|
|
282
|
+
// before `exit_code`, so on the normal path it is always there). Abnormal
|
|
283
|
+
// finalization has no runner-recorded end; the discovery time stands in.
|
|
284
|
+
const finishedAt = flags.exitCode !== null && state?.finished_at != null ? state.finished_at : flags.nowMs;
|
|
285
|
+
const read = readLinesFrom(paths.events, 0);
|
|
286
|
+
const parsed = parseEventLines(read.lines, 0);
|
|
287
|
+
const events = okEvents(parsed);
|
|
288
|
+
const malformedLines = parsed.filter((p) => !p.ok).length;
|
|
289
|
+
const result = buildBrokerResult(store, {
|
|
290
|
+
job,
|
|
291
|
+
events,
|
|
292
|
+
malformedLines,
|
|
293
|
+
exitCode: flags.exitCode,
|
|
294
|
+
timedOut: flags.timedOut,
|
|
295
|
+
canceled: flags.canceled,
|
|
296
|
+
runnerLost: flags.runnerLost,
|
|
297
|
+
pidReused: flags.pidReused,
|
|
298
|
+
expectedArtifacts,
|
|
299
|
+
jsonSchemaPath,
|
|
300
|
+
idleClosedAfterMs,
|
|
301
|
+
gateConfirmed,
|
|
302
|
+
finishedAt,
|
|
303
|
+
now: flags.nowMs,
|
|
304
|
+
});
|
|
305
|
+
writeBrokerResult(paths, result);
|
|
306
|
+
releaseJobLocks(store, job.job_id);
|
|
307
|
+
// Write the conversation id back to the session the moment the broker result
|
|
308
|
+
// knows it, independent of whether the gate ever bound this conversation
|
|
309
|
+
// (finding 14) — otherwise a later `agy_start({session_id})` reads
|
|
310
|
+
// `conversation_id: null` and silently starts a brand-new conversation
|
|
311
|
+
// instead of resuming this one.
|
|
312
|
+
if (result.conversation_id && job.session_id) {
|
|
313
|
+
const session = getSession(store, job.session_id);
|
|
314
|
+
if (session && session.conversation_id === null) {
|
|
315
|
+
try {
|
|
316
|
+
bindConversationId(store, job.session_id, result.conversation_id);
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// Best effort — a concurrent bind from the gate already won.
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const finalized = updateJob(store, job.job_id, {
|
|
324
|
+
lifecycle: 'finished',
|
|
325
|
+
outcome: result.broker_summary.outcome,
|
|
326
|
+
headline: result.broker_summary.headline,
|
|
327
|
+
exit_code: result.broker_summary.exit_code,
|
|
328
|
+
agent_status: result.agent_status,
|
|
329
|
+
contract_status: result.contract_status,
|
|
330
|
+
finished_at: finishedAt,
|
|
331
|
+
});
|
|
332
|
+
removeGateHookIfIdle(store, finalized);
|
|
333
|
+
return finalized;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Once a job lands in `finished`, remove only our own `hooks.json` entry
|
|
337
|
+
* (`GATE_HOOK_KEY`), and only when no other job
|
|
338
|
+
* with the same canonical `cwd` is still live — a second live job on that
|
|
339
|
+
* workspace still needs the hook. Runs *after* `updateJob` above so this job's
|
|
340
|
+
* own row already reads `finished` and does not count itself as a live holdout.
|
|
341
|
+
*
|
|
342
|
+
* The runner never opens the store (it must run even when every server process is dead),
|
|
343
|
+
* so reconcile is the only place that
|
|
344
|
+
* can know "is anything else on this cwd still live" and act on it. Best
|
|
345
|
+
* effort: `removeGateHook` swallows its own failures, and a stale entry left
|
|
346
|
+
* behind here is harmless until the next `agy_start` on the same workspace,
|
|
347
|
+
* which overwrites it unconditionally.
|
|
348
|
+
*/
|
|
349
|
+
function removeGateHookIfIdle(store, job) {
|
|
350
|
+
try {
|
|
351
|
+
const stillLive = listJobs(store, { lifecycle: [...LIVE_LIFECYCLES], cwd: job.cwd, limit: 1 });
|
|
352
|
+
if (stillLive.length === 0)
|
|
353
|
+
removeGateHook(job.cwd);
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
// Best effort — hook-file housekeeping must never break reconcile.
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** Opportunistic cleanup of old job directories, triggered from `agy_start`. */
|
|
360
|
+
export function cleanupOldJobs(store, maxAgeMs) {
|
|
361
|
+
const cutoff = now() - maxAgeMs;
|
|
362
|
+
const finished = listJobs(store, { lifecycle: 'finished', limit: 10000 });
|
|
363
|
+
let removed = 0;
|
|
364
|
+
for (const job of finished) {
|
|
365
|
+
const ts = job.finished_at ?? job.created_at;
|
|
366
|
+
if (ts >= cutoff)
|
|
367
|
+
continue;
|
|
368
|
+
const paths = jobPaths(store.paths, job.job_id);
|
|
369
|
+
try {
|
|
370
|
+
rmSync(paths.dir, { recursive: true, force: true });
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// Best effort: an already-gone directory is not a failure.
|
|
374
|
+
}
|
|
375
|
+
store.db.prepare('DELETE FROM jobs WHERE job_id = ?').run(job.job_id);
|
|
376
|
+
removed++;
|
|
377
|
+
}
|
|
378
|
+
return removed;
|
|
379
|
+
}
|
|
380
|
+
/** `jobs/<id>/exit_code` holds a bare integer; missing or unparsable reads as null. */
|
|
381
|
+
function readExitCode(path) {
|
|
382
|
+
try {
|
|
383
|
+
const raw = readFileSync(path, 'utf8').trim();
|
|
384
|
+
if (raw.length === 0)
|
|
385
|
+
return null;
|
|
386
|
+
const n = Number.parseInt(raw, 10);
|
|
387
|
+
return Number.isFinite(n) ? n : null;
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* After a forced `killpg`, give a still-alive runner a short window to observe
|
|
395
|
+
* the child's exit and write `exit_code` itself, so the finalize path reuses the
|
|
396
|
+
* real exit code instead of manufacturing one. Bounded so reconcile stays cheap
|
|
397
|
+
* even when the runner is not there to write anything.
|
|
398
|
+
*/
|
|
399
|
+
async function pollExitCode(path, totalMs = 2000, intervalMs = 200) {
|
|
400
|
+
const deadline = Date.now() + totalMs;
|
|
401
|
+
for (;;) {
|
|
402
|
+
const code = readExitCode(path);
|
|
403
|
+
if (code !== null)
|
|
404
|
+
return code;
|
|
405
|
+
if (Date.now() >= deadline)
|
|
406
|
+
return null;
|
|
407
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/** Convenience: reconcile one job by id, fetching its current row first. */
|
|
411
|
+
export async function reconcileJobById(store, jobId, opts) {
|
|
412
|
+
const job = getJob(store, jobId);
|
|
413
|
+
return reconcileJob(store, job, opts);
|
|
414
|
+
}
|
|
415
|
+
//# sourceMappingURL=reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/broker/reconcile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { AgentReport, AgyEvent, BrokerResult, JobRow, JudgementPacket } from '../contract/types.js';
|
|
2
|
+
import type { JobPaths } from '../contract/paths.js';
|
|
3
|
+
import type { Store } from '../store/db.js';
|
|
4
|
+
/**
|
|
5
|
+
* `broker-result.json` is the single source of truth.
|
|
6
|
+
*
|
|
7
|
+
* `agy_wait` projects a subset of it, `agy_result` pages all of it, and nothing
|
|
8
|
+
* else recomputes `outcome` — if a tool derives its own verdict, two callers can
|
|
9
|
+
* be told different things about the same job.
|
|
10
|
+
*/
|
|
11
|
+
export interface BuildResultInput {
|
|
12
|
+
job: JobRow;
|
|
13
|
+
events: AgyEvent[];
|
|
14
|
+
malformedLines: number;
|
|
15
|
+
exitCode: number | null;
|
|
16
|
+
timedOut: boolean;
|
|
17
|
+
canceled: boolean;
|
|
18
|
+
runnerLost: boolean;
|
|
19
|
+
pidReused: boolean;
|
|
20
|
+
expectedArtifacts: string[];
|
|
21
|
+
jsonSchemaPath: string | null;
|
|
22
|
+
/**
|
|
23
|
+
* `idle_timeout_ms` when the runner's idle watchdog ended the session, null
|
|
24
|
+
* otherwise. A clean exit 0, so nothing here changes the outcome — but the
|
|
25
|
+
* caller cannot otherwise tell an idle close from an ordinary finish, and the
|
|
26
|
+
* two call for different next steps.
|
|
27
|
+
*/
|
|
28
|
+
idleClosedAfterMs: number | null;
|
|
29
|
+
/**
|
|
30
|
+
* `state.json.gate_confirmed`, verbatim (I4). `false` adds a `source:'broker'`
|
|
31
|
+
* blocker and forces `outcome: 'process_error'`; `null` (no tool step ever
|
|
32
|
+
* ran, or an older `state.json`) changes nothing.
|
|
33
|
+
*/
|
|
34
|
+
gateConfirmed: boolean | null;
|
|
35
|
+
/**
|
|
36
|
+
* When the runner actually finished (`state.json.finished_at`), if known.
|
|
37
|
+
* `finished_at` / `duration_ms` come from this; `now` is only when the
|
|
38
|
+
* broker got around to judging it (`finalized_at`). Absent on abnormal
|
|
39
|
+
* finalization (no `exit_code`, so no runner-recorded end) — `now` then.
|
|
40
|
+
* 0.2.2 (PR1): a job nobody polled for four days used to
|
|
41
|
+
* report an 86-hour duration for a 30-minute run.
|
|
42
|
+
*/
|
|
43
|
+
finishedAt?: number | null;
|
|
44
|
+
now: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function buildBrokerResult(store: Store, input: BuildResultInput): BrokerResult;
|
|
47
|
+
/** Extract agy's unverified self-report. Quarantined from `broker_summary` on purpose. */
|
|
48
|
+
export declare function buildAgentReport(events: AgyEvent[]): AgentReport;
|
|
49
|
+
/** Atomic write, so a concurrent reader never sees a half-written result. */
|
|
50
|
+
export declare function writeBrokerResult(paths: JobPaths, result: BrokerResult): void;
|
|
51
|
+
/**
|
|
52
|
+
* Null when the job has not been finalized yet.
|
|
53
|
+
*
|
|
54
|
+
* A `broker-result.json` written by an older server is migrated in memory
|
|
55
|
+
* (see {@link migrateBrokerResult}) rather than crashing a tool call — job
|
|
56
|
+
* directories outlive an upgrade, and `agy_result` on yesterday's job must keep
|
|
57
|
+
* working. A version we do not know how to read is refused loudly instead of
|
|
58
|
+
* being handed on half-typed.
|
|
59
|
+
*
|
|
60
|
+
* @throws {import('../contract/errors.js').ValidationError} on an unknown version.
|
|
61
|
+
*/
|
|
62
|
+
export declare function loadBrokerResult(paths: JobPaths): BrokerResult | null;
|
|
63
|
+
/**
|
|
64
|
+
* Bring a stored result up to {@link BROKER_RESULT_VERSION}.
|
|
65
|
+
*
|
|
66
|
+
* Version 1 kept `verification.permission_denials` and
|
|
67
|
+
* `verification.environment_blocks`; version 2 collapsed those into the single
|
|
68
|
+
* `verification.blockers`; version 3 adds `verification.verify` (PR6,
|
|
69
|
+
* `verify_command`) — always `null` on a migrated file, since no job written
|
|
70
|
+
* before 0.2.0 ever ran a verify command. The version-1 migration runs the
|
|
71
|
+
* same constructors the live path uses, so an old job is judged by exactly
|
|
72
|
+
* today's rules — and nothing in the old records is dropped, since each one is
|
|
73
|
+
* carried in `Blocker.detail`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function migrateBrokerResult(raw: BrokerResult, path: string): BrokerResult;
|
|
76
|
+
export interface ProjectOptions {
|
|
77
|
+
maxLogTailLines?: number;
|
|
78
|
+
maxBytes?: number;
|
|
79
|
+
/** Cursor to report back for the caller's next `agy_logs` / `agy_wait`. */
|
|
80
|
+
cursor?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Project the judgement packet `agy_wait` returns: counts, warnings, headline,
|
|
84
|
+
* and a short log tail. No lists, no raw response text — those live in
|
|
85
|
+
* `agy_result`.
|
|
86
|
+
*/
|
|
87
|
+
export declare function projectJudgementPacket(result: BrokerResult, opts?: ProjectOptions): JudgementPacket;
|
|
88
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/broker/result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EAIZ,MAAM,EACN,eAAe,EAEhB,MAAM,sBAAsB,CAAA;AAG7B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAIpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAY3C;;;;;;GAMG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,QAAQ,EAAE,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,OAAO,CAAA;IAClB,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC;;;;OAIG;IACH,aAAa,EAAE,OAAO,GAAG,IAAI,CAAA;IAC7B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,GAAG,EAAE,MAAM,CAAA;CACZ;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,GAAG,YAAY,CAkFrF;AA+BD,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAchE;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAE7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,YAAY,GAAG,IAAI,CAIrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CA0CjF;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,eAAe,CA6BnG"}
|