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,482 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { ValidationError } from '../../contract/errors.js';
|
|
5
|
+
import { assertContained, binPath, canonicalize, chmodDbFiles, ensureJobDirs, isWithin, jobPaths, newJobId, newSessionId, stateHome, writeJsonAtomic, } from '../../contract/paths.js';
|
|
6
|
+
import { describePolicy } from '../../broker/blockers.js';
|
|
7
|
+
import { cleanupOldJobs, reconcile } from '../../broker/reconcile.js';
|
|
8
|
+
import { ensureGateHook } from '../../gate/hooks-file.js';
|
|
9
|
+
import { ceilingAbsenceHint, loadCeiling } from '../../policy/ceiling.js';
|
|
10
|
+
import { validateWriteRoots } from '../../policy/containment.js';
|
|
11
|
+
import { getProfile, resolvePolicy } from '../../policy/profiles.js';
|
|
12
|
+
import { evaluateCommandPolicy, firstMatchForDenial, parseRulesLenient } from '../../policy/rules.js';
|
|
13
|
+
import { hooksFilePath } from '../../gate/hooks-file.js';
|
|
14
|
+
import { appendUserTurn } from '../../runner/inbox.js';
|
|
15
|
+
import { buildAgyArgv, buildChildEnv, resolveAgyBin } from '../../runner/spawn.js';
|
|
16
|
+
import { acquireJobLocks } from '../../store/locks.js';
|
|
17
|
+
import { createJob, listJobs, LIVE_LIFECYCLES } from '../../store/jobs.js';
|
|
18
|
+
import { createSession, getSession } from '../../store/sessions.js';
|
|
19
|
+
import { errorReply, reply } from '../context.js';
|
|
20
|
+
import { acceptedEfforts } from './capabilities.js';
|
|
21
|
+
/**
|
|
22
|
+
* `agy_start` — begin a new job and return its `job_id` immediately.
|
|
23
|
+
*
|
|
24
|
+
* Never blocks. The job outlives this server process, so any client connected to
|
|
25
|
+
* the same project can pick it up with `agy_wait` / `agy_logs`.
|
|
26
|
+
*
|
|
27
|
+
* `dry_run: true` resolves everything — argv, policy, locks that *would* be
|
|
28
|
+
* taken — and returns the `EffectiveConfig` without spawning. Settling
|
|
29
|
+
* configuration this way costs no agy quota.
|
|
30
|
+
*/
|
|
31
|
+
export const startInput = z.object({
|
|
32
|
+
prompt: z.string().min(1).describe('Task for the agent. Sent as --print=<prompt>.'),
|
|
33
|
+
cwd: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Workspace directory. Must be inside the project root. Defaults to the project root.'),
|
|
37
|
+
profile: z
|
|
38
|
+
.enum(['research_readonly', 'general_worker'])
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('Permission profile ceiling. research_readonly cannot write or run interpreters. Defaults to research_readonly.'),
|
|
41
|
+
model: z
|
|
42
|
+
.string()
|
|
43
|
+
.refine((v) => !v.startsWith('-'), 'must not look like a CLI flag')
|
|
44
|
+
.optional(),
|
|
45
|
+
effort: z.enum(['low', 'medium', 'high']).optional(),
|
|
46
|
+
mode: z
|
|
47
|
+
.string()
|
|
48
|
+
.refine((v) => !v.startsWith('-'), 'must not look like a CLI flag')
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('agy execution mode, e.g. accept-edits or plan.'),
|
|
51
|
+
session_id: z
|
|
52
|
+
.string()
|
|
53
|
+
.optional()
|
|
54
|
+
.describe('Continue an existing agy conversation. Omit to create a new session.'),
|
|
55
|
+
session_mode: z
|
|
56
|
+
.enum(['oneshot', 'session'])
|
|
57
|
+
.optional()
|
|
58
|
+
.describe('oneshot closes stdin after the prompt; session keeps it open for agy_send.'),
|
|
59
|
+
permissions: z
|
|
60
|
+
.object({
|
|
61
|
+
allow: z
|
|
62
|
+
.array(z.string())
|
|
63
|
+
.optional()
|
|
64
|
+
.describe('Narrows within the profile+ceiling allow list; entries the ceiling refuses come back in rejected_allow.'),
|
|
65
|
+
deny: z
|
|
66
|
+
.array(z.string())
|
|
67
|
+
.optional()
|
|
68
|
+
.describe('Always wins: unioned with the profile deny list and HARD_DENY, never narrowed.'),
|
|
69
|
+
sandboxed: z
|
|
70
|
+
.boolean()
|
|
71
|
+
.optional()
|
|
72
|
+
.describe('Forces agy\'s OS sandbox on for this job (0.2.0 behaviour). Parent can only narrow: true forces the sandbox on; false or absent changes nothing.'),
|
|
73
|
+
read_roots: z
|
|
74
|
+
.array(z.string())
|
|
75
|
+
.optional()
|
|
76
|
+
.describe("The project ceiling's read_roots apply by default. List entries here only to use a subset of them; entries outside the ceiling are dropped and reported in rejected_read_roots."),
|
|
77
|
+
})
|
|
78
|
+
.optional()
|
|
79
|
+
.describe('Three-owner model (see docs/permissions.md): code (HARD_DENY, profiles) is fixed; the project ceiling (~/.agy-worker/projects/<hash>/policy.json, human-owned, outside the workspace) sets what can ever be granted; this field only narrows within that ceiling, never widens it.'),
|
|
80
|
+
on_denial: z
|
|
81
|
+
.enum(['abort', 'continue', 'guide'])
|
|
82
|
+
.optional()
|
|
83
|
+
.describe('What to do on the first policy denial. Default continue.'),
|
|
84
|
+
max_denials: z
|
|
85
|
+
.number()
|
|
86
|
+
.int()
|
|
87
|
+
.positive()
|
|
88
|
+
.optional()
|
|
89
|
+
.describe('Abort the job after this many gate denials regardless of on_denial. Null = never. A middle ground between abort (one flaky-tool workaround kills the job) and continue (unbounded denial loops).'),
|
|
90
|
+
timeout_ms: z.number().int().positive().optional(),
|
|
91
|
+
idle_timeout_ms: z
|
|
92
|
+
.number()
|
|
93
|
+
.int()
|
|
94
|
+
.positive()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe('session_mode "session" only. Closes stdin (ending the process) after this many ms of no agy_send following the last completed turn. Does not affect timeout_ms/deadline_at — agy_send never extends those. Ignored for oneshot.'),
|
|
97
|
+
expected_artifacts: z
|
|
98
|
+
.array(z.string())
|
|
99
|
+
.optional()
|
|
100
|
+
.describe('Workspace-relative paths that must exist afterwards. Missing ones block verified_success.'),
|
|
101
|
+
json_schema: z.string().optional().describe('Path to a JSON schema for structured output.'),
|
|
102
|
+
verify_command: z
|
|
103
|
+
.string()
|
|
104
|
+
.min(1)
|
|
105
|
+
.transform((v) => v.trim())
|
|
106
|
+
.refine((v) => v.length > 0, 'must not be blank')
|
|
107
|
+
.optional()
|
|
108
|
+
.describe('A command the runner — not agy — runs once, after agy exits normally, against the final workspace state. Outside the model\'s own decisions: it cannot skip, reorder, or narrow it. Not a sandboxing feature — it runs as the user, unsandboxed, at the same trust level as the parent agent running the command itself. A non-zero exit or a verify_timeout_ms timeout makes outcome "failed", never a Blocker.'),
|
|
109
|
+
verify_timeout_ms: z
|
|
110
|
+
.number()
|
|
111
|
+
.int()
|
|
112
|
+
.positive()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe('Timeout for verify_command alone, independent of timeout_ms/deadline_at — verify_command may run past the job\'s own deadline. Defaults to limits.default_verify_timeout_ms, clamped to limits.max_timeout_ms. Ignored without verify_command.'),
|
|
115
|
+
requested_by: z.string().optional(),
|
|
116
|
+
parent_task_id: z.string().optional(),
|
|
117
|
+
dry_run: z
|
|
118
|
+
.boolean()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe('Resolve configuration and policy without spawning agy. Costs no quota.'),
|
|
121
|
+
expected_commands: z
|
|
122
|
+
.array(z.string())
|
|
123
|
+
.optional()
|
|
124
|
+
.describe('dry_run only: shell commands to evaluate against the effective policy.'),
|
|
125
|
+
});
|
|
126
|
+
function clamp(n, min, max) {
|
|
127
|
+
return Math.min(Math.max(n, min), max);
|
|
128
|
+
}
|
|
129
|
+
/** Opportunistic retention window for finished job directories. */
|
|
130
|
+
const CLEANUP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
131
|
+
export async function handleStart(ctx, input) {
|
|
132
|
+
try {
|
|
133
|
+
await reconcile(ctx.store);
|
|
134
|
+
cleanupOldJobs(ctx.store, CLEANUP_MAX_AGE_MS);
|
|
135
|
+
chmodDbFiles(ctx.store.paths.db);
|
|
136
|
+
if (input.expected_commands && !input.dry_run) {
|
|
137
|
+
throw new ValidationError({
|
|
138
|
+
field: 'expected_commands',
|
|
139
|
+
value: input.expected_commands,
|
|
140
|
+
expected: 'expected_commands is only valid when dry_run is true',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Measured 2026-09-08 (M8): agy rejects an `--effort`
|
|
144
|
+
// that disagrees with a suffixed model name, and any `--effort` on
|
|
145
|
+
// `claude-*`. Both fail in ~5 s with exit 1 after a job was created —
|
|
146
|
+
// the audit (`08`) counted two such wasted jobs. Refuse before spawning.
|
|
147
|
+
if (input.effort && input.model) {
|
|
148
|
+
const accepted = acceptedEfforts(input.model);
|
|
149
|
+
if (accepted !== null && !accepted.includes(input.effort)) {
|
|
150
|
+
throw new ValidationError({
|
|
151
|
+
field: 'effort',
|
|
152
|
+
value: input.effort,
|
|
153
|
+
expected: accepted.length === 0
|
|
154
|
+
? `omit effort for ${input.model}: agy does not accept --effort for this model`
|
|
155
|
+
: `omit effort for ${input.model}, or pass exactly "${accepted.join('" | "')}" (the effort is part of the model name)`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const profileName = input.profile ?? 'research_readonly';
|
|
160
|
+
const profileDef = getProfile(profileName);
|
|
161
|
+
const rawCwd = input.cwd ?? ctx.paths.root;
|
|
162
|
+
const cwd = assertContained(rawCwd, [ctx.paths.root], 'write');
|
|
163
|
+
// Fail before anything else is created: a job whose gate can never load
|
|
164
|
+
// must never reach `acquireJobLocks` / `createJob` — there would be
|
|
165
|
+
// nothing left to unwind a lock or a job row with no runner ever spawned to
|
|
166
|
+
// finalize it. `ensureGateHook` below no longer has a fail-open fallback
|
|
167
|
+
// (the `|| printf` command is gone), so it must never be called with a
|
|
168
|
+
// `gatePath` that does not exist.
|
|
169
|
+
const gatePath = binPath('gate');
|
|
170
|
+
if (!existsSync(gatePath)) {
|
|
171
|
+
throw new ValidationError({
|
|
172
|
+
field: 'gate',
|
|
173
|
+
value: gatePath,
|
|
174
|
+
expected: 'dist/gate.js built',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const canonicalCwd = canonicalize(cwd);
|
|
178
|
+
const canonicalState = canonicalize(stateHome());
|
|
179
|
+
const canonicalGate = canonicalize(gatePath);
|
|
180
|
+
if (canonicalState === canonicalCwd || isWithin(canonicalState, canonicalCwd)) {
|
|
181
|
+
throw new ValidationError({
|
|
182
|
+
field: 'workspace',
|
|
183
|
+
value: cwd,
|
|
184
|
+
expected: `workspace must not contain state home (${canonicalState})`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (canonicalGate === canonicalCwd || isWithin(canonicalGate, canonicalCwd)) {
|
|
188
|
+
throw new ValidationError({
|
|
189
|
+
field: 'gate',
|
|
190
|
+
value: gatePath,
|
|
191
|
+
expected: `gate binary (${gatePath}) must not lie inside the workspace (${cwd})`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
let sessionId;
|
|
195
|
+
let conversationId;
|
|
196
|
+
let sessionExists = false;
|
|
197
|
+
if (input.session_id) {
|
|
198
|
+
const session = getSession(ctx.store, input.session_id);
|
|
199
|
+
if (!session) {
|
|
200
|
+
throw new ValidationError({
|
|
201
|
+
field: 'session_id',
|
|
202
|
+
value: input.session_id,
|
|
203
|
+
expected: `an existing session id in project ${ctx.paths.root}`,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
sessionId = session.session_id;
|
|
207
|
+
conversationId = session.conversation_id;
|
|
208
|
+
sessionExists = true;
|
|
209
|
+
// A still-null conversation_id while a job on this session is genuinely
|
|
210
|
+
// live (queued/starting/running/canceling) just means we are between
|
|
211
|
+
// turns — the natural `LOCK_CONFLICT` from `acquireJobLocks` below is
|
|
212
|
+
// what should surface in that case, not this. Only when *nothing* is
|
|
213
|
+
// live for this session does a null conversation_id mean its one job
|
|
214
|
+
// finished without agy ever reporting a conversation id, so there is
|
|
215
|
+
// nothing to resume — continuing silently would start a brand-new
|
|
216
|
+
// conversation while `agy_start`'s own contract promises this resumes
|
|
217
|
+
// the old one (finding 14): surface it instead of losing context
|
|
218
|
+
// quietly.
|
|
219
|
+
if (conversationId === null) {
|
|
220
|
+
const liveJobs = listJobs(ctx.store, { sessionId, lifecycle: [...LIVE_LIFECYCLES], limit: 1 });
|
|
221
|
+
if (liveJobs.length === 0) {
|
|
222
|
+
throw new ValidationError({
|
|
223
|
+
field: 'session_id',
|
|
224
|
+
value: input.session_id,
|
|
225
|
+
expected: 'a session whose conversation_id has been captured — this session finished its only job without agy ever reporting a conversation id, so there is nothing to resume',
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
sessionId = newSessionId();
|
|
232
|
+
conversationId = null;
|
|
233
|
+
}
|
|
234
|
+
const onDenial = input.on_denial ?? 'continue';
|
|
235
|
+
const sessionMode = input.session_mode ?? 'oneshot';
|
|
236
|
+
const timeoutMs = clamp(input.timeout_ms ?? ctx.limits.default_timeout_ms, 1, ctx.limits.max_timeout_ms);
|
|
237
|
+
// Only meaningful for session_mode:'session' (see
|
|
238
|
+
// EffectiveConfig.idle_timeout_ms). null for oneshot — its one turn is
|
|
239
|
+
// already followed by an immediate stdin close, so there is no idle gap
|
|
240
|
+
// to bound.
|
|
241
|
+
const idleTimeoutMs = sessionMode === 'session'
|
|
242
|
+
? clamp(input.idle_timeout_ms ?? ctx.limits.default_idle_timeout_ms, 1, ctx.limits.max_idle_timeout_ms)
|
|
243
|
+
: null;
|
|
244
|
+
// `--json-schema <path>` hands agy an arbitrary file to read; without
|
|
245
|
+
// containment a client could point it outside the workspace (finding 18).
|
|
246
|
+
const jsonSchemaPath = input.json_schema
|
|
247
|
+
? assertContained(input.json_schema, [ctx.paths.root], 'read')
|
|
248
|
+
: null;
|
|
249
|
+
const expectedArtifacts = input.expected_artifacts ?? [];
|
|
250
|
+
// Loaded once per call, fresh — never cached across calls, so an edit to
|
|
251
|
+
// policy.json between two agy_start calls takes effect immediately.
|
|
252
|
+
// Invalid (`ValidationError`) propagates straight to `errorReply` below,
|
|
253
|
+
// failing this call closed rather than silently falling back to an empty
|
|
254
|
+
// ceiling (see docs/permissions.md).
|
|
255
|
+
const ceiling = loadCeiling(ctx.paths);
|
|
256
|
+
const policy = resolvePolicy({
|
|
257
|
+
profile: profileName,
|
|
258
|
+
workspace: cwd,
|
|
259
|
+
requested: input.permissions,
|
|
260
|
+
onDenial,
|
|
261
|
+
maxDenials: input.max_denials ?? null,
|
|
262
|
+
ceiling,
|
|
263
|
+
});
|
|
264
|
+
validateWriteRoots(policy.write_roots, gatePath);
|
|
265
|
+
// PR6: `verify_command` is checked
|
|
266
|
+
// against the same resolved deny list a `run_command` call inside this job
|
|
267
|
+
// would be checked against — `policy.deny` already unions `HARD_DENY`, the
|
|
268
|
+
// profile's own deny list, the ceiling's `deny`, and the request's
|
|
269
|
+
// own `deny`, `{workspace}`-substituted by `resolvePolicy` above — so
|
|
270
|
+
// reusing it here is both the simplest check and the correct one: nothing
|
|
271
|
+
// verify_command runs unsandboxed after the job finishes should be able to
|
|
272
|
+
// do what an ordinary `run_command` call could not have done inside it.
|
|
273
|
+
let verify = null;
|
|
274
|
+
if (input.verify_command) {
|
|
275
|
+
const denyMatch = firstMatchForDenial(parseRulesLenient(policy.deny), {
|
|
276
|
+
verb: 'command',
|
|
277
|
+
value: input.verify_command,
|
|
278
|
+
});
|
|
279
|
+
if (denyMatch) {
|
|
280
|
+
throw new ValidationError({
|
|
281
|
+
field: 'verify_command',
|
|
282
|
+
value: input.verify_command,
|
|
283
|
+
expected: `a command not matched by a deny rule — matched ${denyMatch.raw}`,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
const verifyTimeoutMs = clamp(input.verify_timeout_ms ?? ctx.limits.default_verify_timeout_ms, 1, ctx.limits.max_timeout_ms);
|
|
287
|
+
verify = { command: input.verify_command, timeout_ms: verifyTimeoutMs };
|
|
288
|
+
}
|
|
289
|
+
const jobId = newJobId();
|
|
290
|
+
const now = Date.now();
|
|
291
|
+
const deadlineAt = now + timeoutMs;
|
|
292
|
+
// Session mode takes its prompts from stdin, so turn 1 is seeded into the
|
|
293
|
+
// inbox below rather than passed on the command line (agy refuses both).
|
|
294
|
+
const streamInput = sessionMode === 'session';
|
|
295
|
+
// The workspace is always addDirs[0] (buildAgyArgv's own contract);
|
|
296
|
+
// policy.add_dirs is `requested.read_roots` filtered against
|
|
297
|
+
// the human ceiling's glob list.
|
|
298
|
+
const addDirs = [cwd, ...policy.add_dirs];
|
|
299
|
+
// M10 (measured on agy 1.1.27): agy loads `.agents/hooks.json` from
|
|
300
|
+
// *every* `--add-dir`, and a deny from such a hook runs before ours and
|
|
301
|
+
// short-circuits the chain — our gate never sees the call, the watchdog
|
|
302
|
+
// (I4) then kills the job as "gate not confirmed", and whether a foreign
|
|
303
|
+
// `overwrite` could clobber ours is unmeasured. Fail closed: a read root
|
|
304
|
+
// that carries its own hook file is refused here, with the path, rather
|
|
305
|
+
// than started and misreported.
|
|
306
|
+
for (const dir of policy.add_dirs) {
|
|
307
|
+
const foreign = hooksFilePath(dir);
|
|
308
|
+
if (existsSync(foreign)) {
|
|
309
|
+
throw new ValidationError({
|
|
310
|
+
field: 'read_roots',
|
|
311
|
+
value: dir,
|
|
312
|
+
expected: `a read root without its own .agents/hooks.json — agy loads hook files from every --add-dir, and ${foreign} would run ahead of this job's gate (M10). Remove it from read_roots or move the hook file`,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const argv = buildAgyArgv({
|
|
317
|
+
prompt: streamInput ? '' : input.prompt,
|
|
318
|
+
addDirs,
|
|
319
|
+
model: input.model ?? null,
|
|
320
|
+
effort: input.effort ?? null,
|
|
321
|
+
mode: input.mode ?? null,
|
|
322
|
+
conversationId,
|
|
323
|
+
inputFormat: streamInput ? 'stream-json' : null,
|
|
324
|
+
outputFormat: 'stream-json',
|
|
325
|
+
printTimeoutMs: timeoutMs,
|
|
326
|
+
jsonSchemaPath,
|
|
327
|
+
});
|
|
328
|
+
const agyBin = resolveAgyBin();
|
|
329
|
+
// Pin the gate's own openStore() to the same project root and state home
|
|
330
|
+
// this server resolved, so a nested repo as `cwd` or an env-override root
|
|
331
|
+
// can never make the gate compute a different, empty database (finding 10).
|
|
332
|
+
const env = buildChildEnv(process.env, { projectRoot: ctx.paths.root, stateHome: stateHome() });
|
|
333
|
+
const effectiveConfig = {
|
|
334
|
+
job_id: jobId,
|
|
335
|
+
session_id: sessionId,
|
|
336
|
+
conversation_id: conversationId,
|
|
337
|
+
cwd,
|
|
338
|
+
profile: profileName,
|
|
339
|
+
model: input.model ?? null,
|
|
340
|
+
effort: input.effort ?? null,
|
|
341
|
+
mode: input.mode ?? null,
|
|
342
|
+
session_mode: sessionMode,
|
|
343
|
+
on_denial: onDenial,
|
|
344
|
+
write_mode: profileDef.write,
|
|
345
|
+
timeout_ms: timeoutMs,
|
|
346
|
+
deadline_at: deadlineAt,
|
|
347
|
+
idle_timeout_ms: idleTimeoutMs,
|
|
348
|
+
expected_artifacts: expectedArtifacts,
|
|
349
|
+
json_schema_path: jsonSchemaPath,
|
|
350
|
+
policy,
|
|
351
|
+
add_dirs: addDirs,
|
|
352
|
+
verify,
|
|
353
|
+
argv,
|
|
354
|
+
agy_bin: agyBin,
|
|
355
|
+
env,
|
|
356
|
+
created_at: now,
|
|
357
|
+
};
|
|
358
|
+
// Same three fields on both replies, from one builder: what the policy
|
|
359
|
+
// ended up as, what the request lost on the way there, and that rendered
|
|
360
|
+
// for a caller who reads prose. A rejected `permissions.allow` collapses
|
|
361
|
+
// the effective allow list to empty — profile defaults included — which is
|
|
362
|
+
// the trap this reports (see policyCeilingBlockers).
|
|
363
|
+
const described = describePolicy(policy);
|
|
364
|
+
const warnings = [...described.warnings];
|
|
365
|
+
if (!policy.ceiling_present)
|
|
366
|
+
warnings.push(ceilingAbsenceHint(policy.ceiling_path));
|
|
367
|
+
const hasVerifiable = (input.expected_artifacts && input.expected_artifacts.length > 0) ||
|
|
368
|
+
Boolean(input.verify_command) ||
|
|
369
|
+
Boolean(input.json_schema);
|
|
370
|
+
if (profileName === 'general_worker' && !hasVerifiable) {
|
|
371
|
+
warnings.push('nothing verifiable requested: the best outcome this job can reach is success_unverified. Pass verify_command (the build or test command), expected_artifacts, or json_schema to make verified_success possible.');
|
|
372
|
+
}
|
|
373
|
+
let preflight;
|
|
374
|
+
if (input.dry_run && input.expected_commands) {
|
|
375
|
+
const allowRules = parseRulesLenient(policy.allow);
|
|
376
|
+
const denyRules = parseRulesLenient(policy.deny);
|
|
377
|
+
const commandResults = [];
|
|
378
|
+
let deniedCount = 0;
|
|
379
|
+
for (const cmd of input.expected_commands) {
|
|
380
|
+
const evalRes = evaluateCommandPolicy(cmd, allowRules, denyRules);
|
|
381
|
+
const decision = evalRes.allowed ? 'allow' : 'deny';
|
|
382
|
+
if (!evalRes.allowed) {
|
|
383
|
+
deniedCount++;
|
|
384
|
+
}
|
|
385
|
+
commandResults.push({
|
|
386
|
+
command: cmd,
|
|
387
|
+
decision,
|
|
388
|
+
stage: evalRes.stage,
|
|
389
|
+
required_rule: evalRes.requiredRule,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
preflight = { commands: commandResults };
|
|
393
|
+
if (deniedCount > 0) {
|
|
394
|
+
warnings.push(`${deniedCount} of ${input.expected_commands.length} expected_commands would be denied; see preflight.commands`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (input.dry_run) {
|
|
398
|
+
return reply({
|
|
399
|
+
dry_run: true,
|
|
400
|
+
job_id: jobId,
|
|
401
|
+
session_id: sessionId,
|
|
402
|
+
policy_summary: described.policy_summary,
|
|
403
|
+
blockers: described.blockers,
|
|
404
|
+
warnings,
|
|
405
|
+
...(preflight ? { preflight } : {}),
|
|
406
|
+
effective_config: effectiveConfig,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
// Locks first: a lost race must leave nothing behind, and
|
|
410
|
+
// no DB row references this job_id yet, so there is nothing to unwind.
|
|
411
|
+
acquireJobLocks(ctx.store, {
|
|
412
|
+
jobId,
|
|
413
|
+
cwd,
|
|
414
|
+
writeMode: profileDef.write,
|
|
415
|
+
sessionId,
|
|
416
|
+
maxRunning: ctx.limits.max_running_jobs,
|
|
417
|
+
});
|
|
418
|
+
if (!sessionExists) {
|
|
419
|
+
createSession(ctx.store, {
|
|
420
|
+
sessionId,
|
|
421
|
+
cwd,
|
|
422
|
+
model: input.model ?? null,
|
|
423
|
+
effort: input.effort ?? null,
|
|
424
|
+
profile: profileName,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
createJob(ctx.store, {
|
|
428
|
+
jobId,
|
|
429
|
+
sessionId,
|
|
430
|
+
cwd,
|
|
431
|
+
profile: profileName,
|
|
432
|
+
writeMode: profileDef.write,
|
|
433
|
+
sessionMode,
|
|
434
|
+
onDenial,
|
|
435
|
+
deadlineAt,
|
|
436
|
+
requestedBy: input.requested_by ?? null,
|
|
437
|
+
parentTaskId: input.parent_task_id ?? null,
|
|
438
|
+
});
|
|
439
|
+
chmodDbFiles(ctx.store.paths.db);
|
|
440
|
+
const paths = jobPaths(ctx.paths, jobId);
|
|
441
|
+
ensureJobDirs(paths);
|
|
442
|
+
const request = { ...input };
|
|
443
|
+
writeJsonAtomic(paths.request, request);
|
|
444
|
+
writeJsonAtomic(paths.effectiveConfig, effectiveConfig);
|
|
445
|
+
writeJsonAtomic(paths.policy, policy);
|
|
446
|
+
// Turn 1 of a session-mode job. The runner's inbox relay writes it to agy's
|
|
447
|
+
// stdin as soon as the process is up; `agy_send` appends later turns to the
|
|
448
|
+
// same file. Written before the spawn so the relay can never miss it.
|
|
449
|
+
if (streamInput && input.prompt !== '') {
|
|
450
|
+
appendUserTurn(paths.inbox, input.prompt);
|
|
451
|
+
}
|
|
452
|
+
ensureGateHook(cwd, gatePath);
|
|
453
|
+
// Detached: the runner outlives this server process entirely.
|
|
454
|
+
// stdio is 'ignore' — the runner redirects agy's own stdout/stderr
|
|
455
|
+
// to job-directory files itself; nothing here needs a pipe.
|
|
456
|
+
const child = spawn(process.execPath, [binPath('runner'), jobId], {
|
|
457
|
+
cwd: ctx.paths.root,
|
|
458
|
+
detached: true,
|
|
459
|
+
stdio: 'ignore',
|
|
460
|
+
env: process.env,
|
|
461
|
+
});
|
|
462
|
+
child.unref();
|
|
463
|
+
return reply({
|
|
464
|
+
job_id: jobId,
|
|
465
|
+
session_id: sessionId,
|
|
466
|
+
lifecycle: 'queued',
|
|
467
|
+
profile: profileName,
|
|
468
|
+
cwd,
|
|
469
|
+
session_mode: sessionMode,
|
|
470
|
+
deadline_at: deadlineAt,
|
|
471
|
+
idle_timeout_ms: idleTimeoutMs,
|
|
472
|
+
policy_summary: described.policy_summary,
|
|
473
|
+
blockers: described.blockers,
|
|
474
|
+
warnings,
|
|
475
|
+
dry_run: false,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
catch (e) {
|
|
479
|
+
return errorReply(e);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
//# sourceMappingURL=start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../../src/server/tools/start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type ToolContext, type ToolReply } from '../context.js';
|
|
3
|
+
/**
|
|
4
|
+
* `agy_wait` — long-poll until the job finishes or `wait_ms` runs out.
|
|
5
|
+
*
|
|
6
|
+
* Implemented as 200 ms polling, not an in-process await: the job belongs to no
|
|
7
|
+
* client, so there is no handle to await on. From the caller's
|
|
8
|
+
* side it is still a single MCP call.
|
|
9
|
+
*
|
|
10
|
+
* `wait_ms: 0` returns an immediate snapshot. On completion the reply is the
|
|
11
|
+
* judgement packet only — headline, outcome, counts, warnings, short log tail.
|
|
12
|
+
* Lists and raw text belong to `agy_result`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const waitInput: z.ZodObject<{
|
|
15
|
+
job_id: z.ZodString;
|
|
16
|
+
wait_ms: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
after_cursor: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
}, "strip", z.ZodTypeAny, {
|
|
19
|
+
job_id: string;
|
|
20
|
+
wait_ms?: number | undefined;
|
|
21
|
+
after_cursor?: number | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
job_id: string;
|
|
24
|
+
wait_ms?: number | undefined;
|
|
25
|
+
after_cursor?: number | undefined;
|
|
26
|
+
}>;
|
|
27
|
+
export type WaitInput = z.infer<typeof waitInput>;
|
|
28
|
+
export declare function handleWait(ctx: ToolContext, input: WaitInput): Promise<ToolReply>;
|
|
29
|
+
//# sourceMappingURL=wait.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../../src/server/tools/wait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAWvB,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAA;AAEnF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;EAgBpB,CAAA;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA;AAQjD,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAsEvF"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { jobPaths, readJsonIfExists } from '../../contract/paths.js';
|
|
3
|
+
import { reconcile } from '../../broker/reconcile.js';
|
|
4
|
+
import { loadBrokerResult, projectJudgementPacket } from '../../broker/result.js';
|
|
5
|
+
import { sleep } from '../../runner/reap.js';
|
|
6
|
+
import { completeLineEnd, readLinesFrom, readTailLines } from '../../events/cursor.js';
|
|
7
|
+
import { parseEventLines } from '../../events/parse.js';
|
|
8
|
+
import { normalizeParsed, tailSummary } from '../../events/normalize.js';
|
|
9
|
+
import { getJob } from '../../store/jobs.js';
|
|
10
|
+
import { errorReply, reply } from '../context.js';
|
|
11
|
+
/**
|
|
12
|
+
* `agy_wait` — long-poll until the job finishes or `wait_ms` runs out.
|
|
13
|
+
*
|
|
14
|
+
* Implemented as 200 ms polling, not an in-process await: the job belongs to no
|
|
15
|
+
* client, so there is no handle to await on. From the caller's
|
|
16
|
+
* side it is still a single MCP call.
|
|
17
|
+
*
|
|
18
|
+
* `wait_ms: 0` returns an immediate snapshot. On completion the reply is the
|
|
19
|
+
* judgement packet only — headline, outcome, counts, warnings, short log tail.
|
|
20
|
+
* Lists and raw text belong to `agy_result`.
|
|
21
|
+
*/
|
|
22
|
+
export const waitInput = z.object({
|
|
23
|
+
job_id: z.string(),
|
|
24
|
+
wait_ms: z
|
|
25
|
+
.number()
|
|
26
|
+
.int()
|
|
27
|
+
.min(0)
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Max time to block. 0 returns the current state immediately.'),
|
|
30
|
+
after_cursor: z
|
|
31
|
+
.number()
|
|
32
|
+
.int()
|
|
33
|
+
.min(0)
|
|
34
|
+
.optional()
|
|
35
|
+
.describe('Byte offset from a previous call, applied to the in-progress log tail. A finished job returns the full judgement packet and its end-of-stream cursor regardless.'),
|
|
36
|
+
});
|
|
37
|
+
const POLL_INTERVAL_MS = 200;
|
|
38
|
+
function clamp(n, min, max) {
|
|
39
|
+
return Math.min(Math.max(n, min), max);
|
|
40
|
+
}
|
|
41
|
+
export async function handleWait(ctx, input) {
|
|
42
|
+
try {
|
|
43
|
+
const budget = clamp(input.wait_ms ?? 0, 0, ctx.limits.max_timeout_ms);
|
|
44
|
+
const startedAt = Date.now();
|
|
45
|
+
const afterCursor = input.after_cursor ?? 0;
|
|
46
|
+
await reconcile(ctx.store);
|
|
47
|
+
let job = getJob(ctx.store, input.job_id);
|
|
48
|
+
for (;;) {
|
|
49
|
+
if (job.lifecycle === 'finished')
|
|
50
|
+
break;
|
|
51
|
+
if (Date.now() - startedAt >= budget)
|
|
52
|
+
break;
|
|
53
|
+
await sleep(POLL_INTERVAL_MS);
|
|
54
|
+
await reconcile(ctx.store);
|
|
55
|
+
job = getJob(ctx.store, input.job_id);
|
|
56
|
+
}
|
|
57
|
+
const paths = jobPaths(ctx.paths, job.job_id);
|
|
58
|
+
if (job.lifecycle === 'finished') {
|
|
59
|
+
const result = loadBrokerResult(paths);
|
|
60
|
+
if (result) {
|
|
61
|
+
const packet = projectJudgementPacket(result, {
|
|
62
|
+
maxLogTailLines: ctx.limits.max_log_tail_lines,
|
|
63
|
+
maxBytes: ctx.limits.max_response_bytes,
|
|
64
|
+
// End of stream, not 0. The caller feeds this to `agy_logs`, and a
|
|
65
|
+
// finished job has nothing left to hand out.
|
|
66
|
+
cursor: completeLineEnd(paths.events),
|
|
67
|
+
});
|
|
68
|
+
return reply(packet);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Not finished (or finished but not finalized yet) — a lightweight
|
|
72
|
+
// snapshot built directly from events.ndjson. No lists, no raw response.
|
|
73
|
+
let readLines;
|
|
74
|
+
let nextCursor;
|
|
75
|
+
if (afterCursor === 0) {
|
|
76
|
+
readLines = readTailLines(paths.events, ctx.limits.max_response_bytes);
|
|
77
|
+
nextCursor = completeLineEnd(paths.events);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const read = readLinesFrom(paths.events, afterCursor, { maxBytes: ctx.limits.max_response_bytes });
|
|
81
|
+
readLines = read.lines;
|
|
82
|
+
nextCursor = read.nextCursor;
|
|
83
|
+
}
|
|
84
|
+
const normalized = normalizeParsed(parseEventLines(readLines, afterCursor));
|
|
85
|
+
const tail = tailSummary(normalized, ctx.limits.max_log_tail_lines, ctx.limits.max_response_bytes);
|
|
86
|
+
const packet = {
|
|
87
|
+
job_id: job.job_id,
|
|
88
|
+
lifecycle: job.lifecycle,
|
|
89
|
+
outcome: job.outcome,
|
|
90
|
+
headline: job.headline ?? describeLive(job.lifecycle, paths.state),
|
|
91
|
+
exit_code: job.exit_code,
|
|
92
|
+
duration_ms: job.started_at !== null ? Date.now() - job.started_at : null,
|
|
93
|
+
agent_status: job.agent_status,
|
|
94
|
+
contract_status: job.contract_status,
|
|
95
|
+
// Nothing is judged until the job finalizes; zeros here mean "not
|
|
96
|
+
// computed yet", which `lifecycle !== 'finished'` already says.
|
|
97
|
+
counts: { blockers: 0, actionable: 0, tool_errors: 0, turns: 0 },
|
|
98
|
+
warnings: [],
|
|
99
|
+
log_tail: tail,
|
|
100
|
+
cursor: nextCursor,
|
|
101
|
+
};
|
|
102
|
+
return reply(packet);
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
return errorReply(e);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Headline for a job that has not finalized. Names the runner's phase when
|
|
110
|
+
* `state.json` carries one, so a caller polling through a long
|
|
111
|
+
* `verify_command` sees "running, verifying" rather than an unexplained wait
|
|
112
|
+
* (0.2.2 PR1).
|
|
113
|
+
*/
|
|
114
|
+
function describeLive(lifecycle, statePath) {
|
|
115
|
+
const phase = readJsonIfExists(statePath)?.phase;
|
|
116
|
+
if (lifecycle === 'running' && phase === 'verifying')
|
|
117
|
+
return 'job is running: agy exited, verify_command in progress';
|
|
118
|
+
return `job is ${lifecycle}`;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=wait.js.map
|