@rallycry/conveyor-agent 11.0.20 → 11.0.22

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.
@@ -31,2349 +31,93 @@ import {
31
31
  turnOptionsFrom
32
32
  } from "./chunk-W5INK3NE.js";
33
33
  import {
34
+ AGENT_STATUS_REASON_USER_QUESTION,
34
35
  AgentConnection,
36
+ CARD_DESCRIPTION_FIELD_HINT,
37
+ CONTEXT_LINK_LOCATOR_MAX,
38
+ CRITICAL_AUTOMATED_SOURCES,
35
39
  CodespacePortVisibility,
40
+ DEFAULT_CI_WAIT_TIMEOUT_MINUTES,
41
+ DEFAULT_CODEX_CODING_MODEL,
36
42
  DEFAULT_LIFECYCLE_CONFIG,
43
+ DEFAULT_SONNET_MODEL,
44
+ EXTERNAL_AGENT_MESSAGE_SOURCE,
45
+ FABLE_MODEL,
46
+ HUMAN_PROSE_WRITING_STYLE,
37
47
  Lifecycle,
48
+ MAX_CI_WAIT_TIMEOUT_MINUTES,
49
+ MAX_FILE_SIZE_BYTES,
50
+ PM_CHAT_HISTORY_LIMIT,
51
+ PRE_BUILD_TASK_STATUSES,
52
+ PTY_STREAM_PORT_ATTEMPTS,
53
+ PTY_STREAM_PORT_BASE,
38
54
  PortDiscovery,
55
+ PtyStreamFrameReader,
56
+ ReviewGuideContentSchema,
57
+ SEVERITY_ENUM,
58
+ TAG_DESCRIPTION_MAX,
59
+ TAG_OVERVIEW_MAX,
60
+ TAG_REASON_MAX,
61
+ TASK_CHAT_HISTORY_LIMIT,
62
+ TUI_KINDS,
39
63
  awaitGitReady,
40
64
  clearForceFreshCooldown,
41
65
  createServiceLogger,
66
+ encodePtyStreamFrame,
42
67
  ensureOnTaskBranch,
43
68
  flushAllPendingWork,
44
69
  flushPendingChanges,
45
70
  forceFreshCooldownNotice,
46
71
  forceFreshMintBlocked,
47
72
  getCurrentBranch,
48
- git,
49
- hasUncommittedChanges,
50
- hasUnpushedCommits,
51
- pushToOrigin,
52
- readWorkspaceBytes,
53
- readWorkspaceDir,
54
- readWorkspaceFile,
55
- recordForceFreshFailure,
56
- remoteMatchesLocalHead,
57
- restoreWipSnapshot,
58
- stageAndCommit,
59
- statWorkspacePath,
60
- updateRemoteToken,
61
- verifyGitCredential
62
- } from "./chunk-JBGPARLG.js";
63
- import {
64
- registerBootMilestoneSocketFallback,
65
- reportBootMilestone
66
- } from "./chunk-Q4FQOJ7D.js";
67
- import {
68
- describeTokenFile,
69
- ghHostsExternallyOwned,
70
- githubTokenFilePath,
71
- sleep
72
- } from "./chunk-W4LZ7R6Z.js";
73
- import {
74
- isHeavyGateActive,
75
- listAbandonedGateReceipts,
76
- listGateExitSentinels,
77
- refreshGenericGateStatus
78
- } from "./chunk-372R6E4C.js";
79
- import {
80
- LoopLagMonitor,
81
- loopStatusForRunnerStatus
82
- } from "./chunk-IA45XHOA.js";
83
- import {
84
- getWorkbenchClient
85
- } from "./chunk-SQM2BQ7H.js";
86
- import {
87
- workbenchEnabled
88
- } from "./chunk-KMB3BU4S.js";
89
-
90
- // ../shared/dist/chunk-OKJPFFQI.js
91
- var CARD_DESCRIPTION_MAX = 255;
92
- var CARD_DESCRIPTION_LIMIT_MESSAGE = `Card descriptions are capped at ${CARD_DESCRIPTION_MAX} characters \u2014 write 1-2 plain sentences a non-engineer can read; put technical detail in the plan or card chat.`;
93
- var CARD_DESCRIPTION_FIELD_HINT = `max ${CARD_DESCRIPTION_MAX} chars, 1-2 plain sentences a non-engineer can read \u2014 put technical detail in the plan`;
94
- var SEVERITY_ENUM = [
95
- "DEBUG",
96
- "INFO",
97
- "NOTICE",
98
- "WARNING",
99
- "ERROR",
100
- "CRITICAL",
101
- "ALERT",
102
- "EMERGENCY"
103
- ];
104
- var MAX_LINE_CHARS = 400;
105
- var DEFAULT_SINCE_MINUTES = 60;
106
- function truncateLine(text) {
107
- const oneLine = text.replace(/\s*\n\s*/g, " \u23CE ");
108
- if (oneLine.length <= MAX_LINE_CHARS) return oneLine;
109
- const overflow = oneLine.length - MAX_LINE_CHARS;
110
- return `${oneLine.slice(0, MAX_LINE_CHARS)}\u2026[+${overflow}c]`;
111
- }
112
- function entrySource(entry) {
113
- return entry.resource.service_name ?? entry.resource.pod_name ?? entry.resource.database_id ?? entry.resourceType ?? "-";
114
- }
115
- var PAYLOAD_SKIP_KEYS = /* @__PURE__ */ new Set(["message", "severity", "timestamp", "level", "stack"]);
116
- var PAYLOAD_PRIORITY = [
117
- "error",
118
- "outcome",
119
- "serviceName",
120
- "methodName",
121
- "userId",
122
- "taskId",
123
- "sessionId",
124
- "workspaceId",
125
- "projectId",
126
- "durationMs"
127
- ];
128
- var PAYLOAD_VALUE_MAX_CHARS = 160;
129
- function compactPayloadValue(value) {
130
- const raw = typeof value === "string" ? value : JSON.stringify(value);
131
- const flat = (raw ?? "undefined").replace(/\s+/g, " ");
132
- const quoted = typeof value === "string" && /[\s"]/.test(flat) ? JSON.stringify(flat) : flat;
133
- return quoted.length > PAYLOAD_VALUE_MAX_CHARS ? `${quoted.slice(0, PAYLOAD_VALUE_MAX_CHARS)}\u2026` : quoted;
134
- }
135
- function formatPayloadSuffix(payloadJson) {
136
- if (!payloadJson) return "";
137
- let parsed;
138
- try {
139
- parsed = JSON.parse(payloadJson);
140
- } catch {
141
- return "";
142
- }
143
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return "";
144
- const obj = parsed;
145
- const rank = (key) => {
146
- const i = PAYLOAD_PRIORITY.indexOf(key);
147
- return i === -1 ? PAYLOAD_PRIORITY.length : i;
148
- };
149
- const parts = Object.keys(obj).filter((k) => !PAYLOAD_SKIP_KEYS.has(k) && obj[k] !== void 0 && obj[k] !== null).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b)).map((k) => `${k}=${compactPayloadValue(obj[k])}`);
150
- return parts.length > 0 ? ` | ${parts.join(" ")}` : "";
151
- }
152
- function formatLogEntryLine(entry) {
153
- const httpPrefix = entry.httpRequest?.status ? `http ${entry.httpRequest.status} ${entry.httpRequest.method ?? ""} ${entry.httpRequest.url ?? ""}`.trim() + " \u2014 " : "";
154
- return `${entry.timestamp} ${entry.severity.padEnd(7)} [${entrySource(entry)}] ${truncateLine(
155
- `${httpPrefix}${entry.message}${formatPayloadSuffix(entry.payload)}`
156
- )}`;
157
- }
158
- async function runQueryGcpLogs(port, params, now = Date.now) {
159
- const startTime = params.startTime ?? new Date(now() - (params.sinceMinutes ?? DEFAULT_SINCE_MINUTES) * 6e4).toISOString();
160
- const result = await port.queryGcpLogs({
161
- projectId: params.projectId,
162
- env: params.env,
163
- severity: params.severity,
164
- services: params.services,
165
- sqlInstances: params.sqlInstances,
166
- allServices: params.allServices,
167
- search: params.search,
168
- filter: params.filter,
169
- startTime,
170
- endTime: params.endTime,
171
- limit: params.limit,
172
- pageToken: params.pageToken
173
- });
174
- if (result.error) return result.error;
175
- const header = [
176
- `env=${params.env ?? "prod"}`,
177
- `window=${startTime}\u2192${params.endTime ?? "now"}`,
178
- ...params.severity ? [`minSeverity=${params.severity}`] : [],
179
- `scope=${result.scopedServices ? `[${result.scopedServices.join(", ")}]` : "all"}`,
180
- `entries=${result.entries.length}`
181
- ].join(" ");
182
- const lines = result.entries.map(formatLogEntryLine);
183
- const footer = result.nextPageToken ? [`-- more available: pass pageToken="${result.nextPageToken}" to continue`] : [];
184
- if (lines.length === 0) {
185
- return [
186
- header,
187
- "(no matching log entries \u2014 widen the window, lower minSeverity, or drop filters)"
188
- ].join("\n");
189
- }
190
- return [header, ...lines, ...footer].join("\n");
191
- }
192
- async function runQueryGrafanaLogs(port, params, now = Date.now) {
193
- const startTime = params.startTime ?? new Date(now() - (params.sinceMinutes ?? DEFAULT_SINCE_MINUTES) * 6e4).toISOString();
194
- const result = await port.queryGrafanaLogs({
195
- projectId: params.projectId,
196
- env: params.env,
197
- level: params.level,
198
- services: params.services,
199
- search: params.search,
200
- logql: params.logql,
201
- startTime,
202
- endTime: params.endTime,
203
- limit: params.limit
204
- });
205
- if (result.error) return result.error;
206
- const header = [
207
- `env=${params.env ?? "prod"}`,
208
- `window=${startTime}\u2192${params.endTime ?? "now"}`,
209
- ...params.level ? [`minLevel=${params.level}`] : [],
210
- ...result.logql ? [`logql=${truncateLine(result.logql)}`] : [],
211
- `entries=${result.entries.length}`
212
- ].join(" ");
213
- const lines = result.entries.map(formatLogEntryLine);
214
- const oldest = result.entries.map((e) => e.timestamp).sort()[0];
215
- const footer = result.hasMore ? [`-- hit the limit: older lines exist \u2014 pass endTime="${oldest}" to page further back`] : [];
216
- if (lines.length === 0) {
217
- return [
218
- header,
219
- "(no matching log entries \u2014 widen the window, lower minLevel, or drop filters)"
220
- ].join("\n");
221
- }
222
- return [header, ...lines, ...footer].join("\n");
223
- }
224
-
225
- // ../shared/dist/index.js
226
- import { z } from "zod";
227
- import { z as z2 } from "zod";
228
- import { z as z3 } from "zod";
229
- import { z as z4 } from "zod";
230
- import { z as z5 } from "zod";
231
- import { z as z6 } from "zod";
232
- import { z as z7 } from "zod";
233
- import { z as z8 } from "zod";
234
- import { z as z9 } from "zod";
235
- import { z as z10 } from "zod";
236
- var EXTERNAL_AGENT_MESSAGE_SOURCE = "external_agent";
237
- var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
238
- var DEFAULT_OPUS_MODEL = "claude-opus-5";
239
- var DEFAULT_HAIKU_MODEL = "claude-haiku-4-5-20251001";
240
- var FABLE_MODEL = "claude-fable-5-1";
241
- var PREVIOUS_SONNET_MODEL = "claude-sonnet-4-6";
242
- var PREVIOUS_OPUS_MODEL = "claude-opus-4-8";
243
- var PTY_STREAM_PORT_BASE = 7420;
244
- var PTY_STREAM_PORT_ATTEMPTS = 8;
245
- function encodePtyStreamFrame(frame) {
246
- return `${JSON.stringify(frame)}
247
- `;
248
- }
249
- function isRecord(value) {
250
- return typeof value === "object" && value !== null;
251
- }
252
- function parsePtyStreamFrame(line) {
253
- if (!line) return null;
254
- let parsed;
255
- try {
256
- parsed = JSON.parse(line);
257
- } catch {
258
- return null;
259
- }
260
- if (!isRecord(parsed) || typeof parsed.t !== "string") return null;
261
- switch (parsed.t) {
262
- case "hello":
263
- case "data":
264
- case "ended":
265
- case "input":
266
- case "resize":
267
- return parsed;
268
- default:
269
- return null;
270
- }
271
- }
272
- var PtyStreamFrameReader = class {
273
- constructor(onFrame) {
274
- this.onFrame = onFrame;
275
- }
276
- onFrame;
277
- buffer = "";
278
- push(chunk) {
279
- this.buffer += chunk;
280
- let index = this.buffer.indexOf("\n");
281
- while (index >= 0) {
282
- const line = this.buffer.slice(0, index);
283
- this.buffer = this.buffer.slice(index + 1);
284
- const frame = parsePtyStreamFrame(line);
285
- if (frame) this.onFrame(frame);
286
- index = this.buffer.indexOf("\n");
287
- }
288
- }
289
- };
290
- var PREVIEW_PORT_DENY_LIST = [
291
- 5432,
292
- 6379,
293
- 9200,
294
- ...Array.from({ length: PTY_STREAM_PORT_ATTEMPTS }, (_, i) => PTY_STREAM_PORT_BASE + i)
295
- ];
296
- function normalizeCheckpointPath(value) {
297
- let normalized = value.trim().replace(/\/{2,}/g, "/");
298
- normalized = normalized.split("/").filter((segment) => segment !== ".").join("/");
299
- while (normalized.endsWith("/")) normalized = normalized.slice(0, -1);
300
- return normalized;
301
- }
302
- var checkpointPathSchema = z.string().transform(normalizeCheckpointPath).pipe(
303
- z.string().min(1).refine((value) => value !== ".", "Checkpoint paths must name a repository entry").refine((value) => !value.startsWith("/"), "Checkpoint paths must be repository-relative").refine(
304
- (value) => !/^[A-Za-z]:[\\/]/.test(value) && !value.includes("\\"),
305
- "Checkpoint paths must use repository-relative POSIX syntax"
306
- ).refine(
307
- (value) => !value.split("/").includes(".."),
308
- "Checkpoint paths must not traverse a parent directory"
309
- )
310
- );
311
- var secretNameSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
312
- var checkpointKeySchema = z.string().regex(/^[0-9a-f]{64}$/);
313
- var checkpointDigestRefSchema = z.string().regex(/^[^\s@]+@sha256:[0-9a-f]{64}$/);
314
- var ACTIONS_PREBAKE_REGISTRY_PATTERN = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::(?:[1-9][0-9]{0,4}))?(?:\/[a-z0-9]+(?:[._-][a-z0-9]+)*)*$/;
315
- var actionsPrebakeRegistrySchema = z.string().trim().min(1).regex(
316
- ACTIONS_PREBAKE_REGISTRY_PATTERN,
317
- "Actions prebake registry must be a lowercase host[:port] with an optional path prefix"
318
- ).refine((value) => {
319
- const port = /:([0-9]+)(?:\/|$)/.exec(value)?.[1];
320
- return !port || Number(port) <= 65535;
321
- }, "Actions prebake registry port must be between 1 and 65535");
322
- function uniqueSortedArray(item, minimum = 0) {
323
- return z.array(item).min(minimum).superRefine((values, ctx) => {
324
- if (new Set(values).size !== values.length) {
325
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Duplicate values are not allowed" });
326
- }
327
- }).transform((values) => [...values].sort());
328
- }
329
- var projectCheckpointSettingsSchema = z.object({
330
- enabled: z.literal(true),
331
- cacheCommand: z.string().trim().min(1),
332
- cacheInputPaths: uniqueSortedArray(checkpointPathSchema, 1),
333
- reusableArtifactPaths: uniqueSortedArray(checkpointPathSchema, 1),
334
- finalizeCommand: z.string().trim().min(1),
335
- credentialEpoch: z.string().trim().min(1),
336
- requiredSecretNames: uniqueSortedArray(secretNameSchema).optional(),
337
- optionalSecretNames: uniqueSortedArray(secretNameSchema).optional(),
338
- bakeWebAppBuild: z.boolean().optional()
339
- }).superRefine((checkpoint, ctx) => {
340
- const required = new Set(checkpoint.requiredSecretNames ?? []);
341
- for (const name of checkpoint.optionalSecretNames ?? []) {
342
- if (required.has(name)) {
343
- ctx.addIssue({
344
- code: z.ZodIssueCode.custom,
345
- path: ["optionalSecretNames"],
346
- message: "A secret cannot be both required and optional"
347
- });
348
- }
349
- }
350
- });
351
- var TUI_KINDS = ["claude-code", "opencode", "codex"];
352
- var ACHIEVEMENT_RARITIES = [
353
- {
354
- key: "common",
355
- name: "Common",
356
- color: "#22c55e",
357
- iconPath: "/storypoints/square-solid-full.svg"
358
- },
359
- {
360
- key: "magic",
361
- name: "Magic",
362
- color: "#3b82f6",
363
- iconPath: "/storypoints/diamond-solid-full.svg"
364
- },
365
- { key: "rare", name: "Rare", color: "#eab308", iconPath: "/storypoints/gem-solid-full.svg" },
366
- {
367
- key: "unique",
368
- name: "Unique",
369
- color: "#f97316",
370
- iconPath: "/storypoints/scroll-sharp-solid-full.svg"
371
- },
372
- { key: "pack", name: "Pack", color: "#9c27b0", iconPath: "/storypoints/pack.svg" }
373
- ];
374
- var RISK_LEVELS = ["critical", "high", "medium", "low"];
375
- var riskLevelSchema = z2.enum(RISK_LEVELS);
376
- var DEFAULT_RISK_LEVELS = [
377
- {
378
- level: "critical",
379
- value: 4,
380
- label: "Critical",
381
- description: "Touches critical surface area; give it the closest review.",
382
- color: "#dc2626",
383
- ordinal: 0
384
- },
385
- {
386
- level: "high",
387
- value: 3,
388
- label: "Elevated",
389
- description: "Touches important surface area; review carefully.",
390
- color: "#ea580c",
391
- ordinal: 1
392
- },
393
- {
394
- level: "medium",
395
- value: 2,
396
- label: "Moderate",
397
- description: "Moderate surface area; normal review.",
398
- color: "#d97706",
399
- ordinal: 2
400
- },
401
- {
402
- level: "low",
403
- value: 1,
404
- label: "Minimal",
405
- description: "Small or isolated surface area.",
406
- color: "#64748b",
407
- ordinal: 3
408
- }
409
- ];
410
- var LEVEL_BY_VALUE = new Map(
411
- DEFAULT_RISK_LEVELS.map((m) => [m.value, m.level])
412
- );
413
- var ACTIVE_WORK_STATUSES = [
414
- "InProgress",
415
- "ReviewPR",
416
- "ReviewDev",
417
- "ReviewLive",
418
- "Complete"
419
- ];
420
- var IDENTIFIED_WORK_STATUSES = ["Open", ...ACTIVE_WORK_STATUSES];
421
- var DEFAULT_TASK_STATUS_COLOR = "#a8a29e";
422
- var DEFAULT_TASK_STATUS_COLOR_INT = Number.parseInt(DEFAULT_TASK_STATUS_COLOR.slice(1), 16);
423
- var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
424
- var MAX_FILE_TAGS = 5;
425
- var MAX_FILE_TAG_LENGTH = 100;
426
- var EMBED_THRESHOLD_IMAGES = 5 * 1024 * 1024;
427
- var EMBED_THRESHOLD_TEXT = 2 * 1024 * 1024;
428
- var IDLE_HEARTBEAT_MS = 90 * 1e3;
429
- var RUNNER_MODES = [
430
- "task",
431
- "plan",
432
- "pm",
433
- "code-review",
434
- "adhoc",
435
- "pack",
436
- "shell",
437
- "serving"
438
- ];
439
- var TurnEndToolCallSchema = z3.object({
440
- tool: z3.string(),
441
- input: z3.string().optional(),
442
- output: z3.string().optional(),
443
- timestamp: z3.string().optional()
444
- }).passthrough();
445
- var KnownAgentEventSchema = z3.discriminatedUnion("type", [
446
- // ── Lifecycle / connection ────────────────────────────────────────────
447
- z3.object({
448
- type: z3.literal("connected"),
449
- sessionId: z3.string(),
450
- projectId: z3.string().optional()
451
- }).passthrough(),
452
- // Open-ended context snapshot spread from buildInitializationContext().
453
- z3.object({ type: z3.literal("session_manifest") }).passthrough(),
454
- z3.object({
455
- type: z3.literal("agent_runner_status"),
456
- reason: z3.string(),
457
- attempt: z3.number().optional(),
458
- attempts: z3.number().optional()
459
- }).passthrough(),
460
- z3.object({ type: z3.literal("shutdown"), reason: z3.string().optional() }).passthrough(),
461
- z3.object({ type: z3.literal("mode_changed"), agentMode: z3.string() }).passthrough(),
462
- z3.object({ type: z3.literal("mode_transition"), from: z3.string(), to: z3.string() }).passthrough(),
463
- // ── Turn stream ───────────────────────────────────────────────────────
464
- z3.object({ type: z3.literal("message"), content: z3.string() }).passthrough(),
465
- z3.object({ type: z3.literal("thinking"), message: z3.string() }).passthrough(),
466
- z3.object({
467
- type: z3.literal("tool_use"),
468
- tool: z3.string(),
469
- // Producers send JSON.stringify(input); consumers defend against
470
- // object inputs from older agents, so the wire stays permissive here.
471
- input: z3.unknown().optional()
472
- }).passthrough(),
473
- z3.object({
474
- type: z3.literal("tool_result"),
475
- tool: z3.string(),
476
- output: z3.unknown().optional(),
477
- isError: z3.boolean().optional(),
478
- redactedCount: z3.number().optional()
479
- }).passthrough(),
480
- z3.object({ type: z3.literal("turn_end"), toolCalls: z3.array(TurnEndToolCallSchema) }).passthrough(),
481
- z3.object({
482
- type: z3.literal("completed"),
483
- summary: z3.string().optional(),
484
- durationMs: z3.number().optional()
485
- }).passthrough(),
486
- z3.object({ type: z3.literal("error"), message: z3.string() }).passthrough(),
487
- z3.object({ type: z3.literal("agent_typing_start") }).passthrough(),
488
- z3.object({ type: z3.literal("agent_typing_stop") }).passthrough(),
489
- // ── Telemetry ─────────────────────────────────────────────────────────
490
- // heartbeat/typing: legacy telemetry the server still classifies as
491
- // transient (TRANSIENT_EVENT_TYPES) — kept in the vocabulary.
492
- z3.object({ type: z3.literal("heartbeat") }).passthrough(),
493
- z3.object({ type: z3.literal("typing") }).passthrough(),
494
- z3.object({
495
- type: z3.literal("context_update"),
496
- contextTokens: z3.number(),
497
- contextWindow: z3.number(),
498
- inputTokens: z3.number().optional(),
499
- cacheReadInputTokens: z3.number().optional(),
500
- cacheCreationInputTokens: z3.number().optional(),
501
- totalTokensUsed: z3.number().optional()
502
- }).passthrough(),
503
- // Four producer shapes share this type: {rateLimitType, utilization, status}
504
- // (SDK rate_limit_event), {resetsAt} (agent-connection resume notice), the
505
- // usage-sampler ({rateLimitType, utilization, status, resetsAt, gauges}
506
- // — resetsAt matches rateLimitType; gauges survives via .passthrough()), and
507
- // {unmeasurable, reason} (the sampler reporting it cannot read this key).
508
- z3.object({
509
- type: z3.literal("rate_limit_update"),
510
- rateLimitType: z3.string().optional(),
511
- utilization: z3.number().optional(),
512
- status: z3.string().optional(),
513
- resetsAt: z3.string().optional(),
514
- unmeasurable: z3.boolean().optional(),
515
- reason: z3.string().optional()
516
- }).passthrough(),
517
- z3.object({
518
- type: z3.literal("context_compacted"),
519
- trigger: z3.string().optional(),
520
- preTokens: z3.number().optional()
521
- }).passthrough(),
522
- z3.object({
523
- type: z3.literal("tool_progress"),
524
- toolName: z3.string().optional(),
525
- elapsedSeconds: z3.number().optional()
526
- }).passthrough(),
527
- z3.object({
528
- type: z3.literal("subagent_started"),
529
- sdkTaskId: z3.string().optional(),
530
- description: z3.string().optional()
531
- }).passthrough(),
532
- z3.object({
533
- type: z3.literal("subagent_progress"),
534
- sdkTaskId: z3.string().optional(),
535
- description: z3.string().optional(),
536
- toolUses: z3.number().optional(),
537
- durationMs: z3.number().optional()
538
- }).passthrough(),
539
- // ── Work products ─────────────────────────────────────────────────────
540
- z3.object({ type: z3.literal("pr_created"), url: z3.string(), number: z3.number() }).passthrough(),
541
- z3.object({
542
- type: z3.literal("code_review_complete"),
543
- result: z3.enum(["approved", "changes_requested"]),
544
- summary: z3.string().optional(),
545
- issues: z3.array(
546
- z3.object({
547
- file: z3.string(),
548
- line: z3.number().optional(),
549
- severity: z3.string().optional(),
550
- description: z3.string().optional()
551
- }).passthrough()
552
- ).optional()
553
- }).passthrough(),
554
- // ── Environment setup / start command ─────────────────────────────────
555
- z3.object({ type: z3.literal("setup_output"), stream: z3.string(), data: z3.string() }).passthrough(),
556
- z3.object({
557
- type: z3.literal("setup_complete"),
558
- startCommandRunning: z3.boolean().optional(),
559
- startCommandConfigured: z3.boolean().optional(),
560
- // Sanitized server-side by sanitizeSessionPreviewPorts — stays unknown.
561
- previewPorts: z3.unknown().optional()
562
- }).passthrough(),
563
- z3.object({ type: z3.literal("setup_error"), message: z3.string() }).passthrough(),
564
- z3.object({ type: z3.literal("start_command_started") }).passthrough(),
565
- z3.object({ type: z3.literal("start_command_output"), stream: z3.string(), data: z3.string() }).passthrough(),
566
- z3.object({
567
- type: z3.literal("start_command_exited"),
568
- code: z3.number().nullable().optional(),
569
- signal: z3.string().nullable().optional(),
570
- message: z3.string().optional()
571
- }).passthrough(),
572
- z3.object({ type: z3.literal("start_command_error"), message: z3.string() }).passthrough()
573
- ]);
574
- var AgentEventSchema = z3.union([
575
- KnownAgentEventSchema,
576
- z3.object({ type: z3.string().min(1) }).catchall(z3.unknown())
577
- ]);
578
- var cardDescription = z4.string().max(CARD_DESCRIPTION_MAX, CARD_DESCRIPTION_LIMIT_MESSAGE).optional();
579
- var AgentHeartbeatSchema = z4.object({
580
- sessionId: z4.string().optional(),
581
- timestamp: z4.string(),
582
- status: z4.enum(["active", "idle", "building"]),
583
- currentAction: z4.string().optional(),
584
- /** Sender-observed main event-loop lag (ms) — see AgentHeartbeat.loopLagMs. */
585
- loopLagMs: z4.number().nonnegative().optional()
586
- });
587
- var CreatePRInputSchema = z4.object({
588
- title: z4.string().min(1),
589
- body: z4.string(),
590
- head: z4.string().optional(),
591
- base: z4.string().optional()
592
- });
593
- var PostToChatInputSchema = z4.object({
594
- message: z4.string().min(1),
595
- type: z4.enum(["message", "question", "update"]).optional().default("message"),
596
- milestone: z4.enum(["plan_ready", "implementation_complete", "blocked"]).optional()
597
- });
598
- var GetTaskContextRequestSchema = z4.object({
599
- sessionId: z4.string(),
600
- includeHistory: z4.boolean().optional().default(false),
601
- /**
602
- * Read the plan-revised marker WITHOUT consuming it. Bookkeeping fetches
603
- * (the session-identity check, the branch refresh) pass true so they cannot
604
- * race the boot fetch and swallow the notice before it reaches the prompt.
605
- * Defaults to false — consuming — so a pod running an older agent build still
606
- * clears the marker instead of showing the notice on every boot forever.
607
- */
608
- peekPlanRevision: z4.boolean().optional().default(false)
609
- });
610
- var GetChatMessagesRequestSchema = z4.object({
611
- sessionId: z4.string(),
612
- limit: z4.number().int().positive().optional().default(50),
613
- offset: z4.number().int().nonnegative().optional().default(0),
614
- /** Task id or slug to read chat from. Omit for the session's own task. Only
615
- * the session's own task or one of its children resolves — anything else is
616
- * an error, never a silent fallback to the caller's own chat. */
617
- taskId: z4.string().optional()
618
- });
619
- var GetTaskFilesRequestSchema = z4.object({
620
- sessionId: z4.string()
621
- });
622
- var GetTaskFileRequestSchema = z4.object({
623
- sessionId: z4.string(),
624
- fileId: z4.string()
625
- });
626
- var GetTaskRequestSchema = z4.object({
627
- sessionId: z4.string(),
628
- taskSlugOrId: z4.string()
629
- });
630
- var GetCliHistoryRequestSchema = z4.object({
631
- sessionId: z4.string(),
632
- limit: z4.number().int().positive().optional().default(100),
633
- source: z4.enum(["agent", "application"]).optional(),
634
- /** Task id or slug to read logs from. Omit for the session's own task. Only
635
- * the session's own task or one of its children resolves — anything else is
636
- * an error, never a silent fallback to the caller's own logs. */
637
- taskId: z4.string().optional()
638
- });
639
- var ListSubtasksRequestSchema = z4.object({
640
- sessionId: z4.string(),
641
- /** "compact" returns the slim orchestration view (ListSubtasksCompactResponse)
642
- * with the pack build-slot picture; "full" (default — wire-compat with older
643
- * agents) returns the verbose SubtaskSummaryDTO[] including description/plan. */
644
- view: z4.enum(["compact", "full"]).optional()
645
- });
646
- var GetDependenciesRequestSchema = z4.object({
647
- sessionId: z4.string()
648
- });
649
- var GetSuggestionsRequestSchema = z4.object({
650
- sessionId: z4.string(),
651
- status: z4.string().optional(),
652
- limit: z4.number().int().min(1).max(100).optional()
653
- });
654
- var ListManualTestsRequestSchema = z4.object({
655
- sessionId: z4.string()
656
- });
657
- var QueryManualTestsRequestSchema = z4.object({
658
- sessionId: z4.string(),
659
- cardStatuses: z4.array(z4.string()).optional(),
660
- testStatuses: z4.array(z4.enum(["open", "approved", "rejected"])).optional()
661
- });
662
- var CreatePullRequestRequestSchema = CreatePRInputSchema.extend({ sessionId: z4.string() });
663
- var RequestFileUploadRequestSchema = z4.object({
664
- sessionId: z4.string(),
665
- fileName: z4.string().min(1).max(255),
666
- mimeType: z4.string().min(1).max(128),
667
- fileSize: z4.number().int().positive().max(MAX_FILE_SIZE_BYTES)
668
- });
669
- var ConfirmFileUploadRequestSchema = z4.object({
670
- sessionId: z4.string(),
671
- fileId: z4.string(),
672
- title: z4.string().max(500).optional(),
673
- /** Glossary tag names (or ids) this file is an example of. */
674
- tags: z4.array(z4.string().min(1).max(MAX_FILE_TAG_LENGTH)).max(MAX_FILE_TAGS).optional()
675
- });
676
- var UpdateTaskStatusRequestSchema = z4.object({
677
- sessionId: z4.string(),
678
- status: z4.string(),
679
- force: z4.boolean().optional().default(false)
680
- });
681
- var StoreSessionIdRequestSchema = z4.object({
682
- sessionId: z4.string(),
683
- sdkSessionId: z4.string()
684
- });
685
- var SetManualTestsRequestSchema = z4.object({
686
- sessionId: z4.string(),
687
- items: z4.array(z4.object({ title: z4.string().min(1) })).min(1)
688
- });
689
- var EditManualTestRequestSchema = z4.object({
690
- sessionId: z4.string(),
691
- title: z4.string().min(1),
692
- newTitle: z4.string().min(1)
693
- });
694
- var RemoveManualTestRequestSchema = z4.object({
695
- sessionId: z4.string(),
696
- title: z4.string().min(1)
697
- });
698
- var ApproveManualTestRequestSchema = z4.object({
699
- sessionId: z4.string(),
700
- title: z4.string().min(1)
701
- });
702
- var RejectManualTestRequestSchema = z4.object({
703
- sessionId: z4.string(),
704
- title: z4.string().min(1),
705
- reason: z4.string().min(1).max(2e3)
706
- });
707
- var SessionStartRequestSchema = z4.object({
708
- sessionId: z4.string(),
709
- agentVersion: z4.string(),
710
- capabilities: z4.array(z4.string())
711
- });
712
- var SessionStopRequestSchema = z4.object({
713
- sessionId: z4.string(),
714
- reason: z4.string().optional()
715
- });
716
- var EndReviewSessionRequestSchema = z4.object({
717
- sessionId: z4.string(),
718
- reason: z4.enum(["approved", "changes_requested", "finished"]).optional()
719
- });
720
- var ConnectAgentRequestSchema = z4.object({
721
- sessionId: z4.string()
722
- });
723
- var ReportAgentStatusRequestSchema = z4.object({
724
- sessionId: z4.string(),
725
- status: z4.string(),
726
- /** Why the agent reports this status (e.g. "user_question" while an AskUserQuestion questionnaire is pending in the TUI). */
727
- reason: z4.string().optional(),
728
- /**
729
- * The pending question text, sent only alongside `reason: "user_question"`
730
- * so the server can surface it in the user-question notification body (and
731
- * thus the Attention feed) instead of a generic string. Optional: older
732
- * agents omit it and the server falls back to the generic wording.
733
- */
734
- questionText: z4.string().optional()
735
- });
736
- var NotifyAgentVersionRequestSchema = z4.object({
737
- sessionId: z4.string(),
738
- agentVersion: z4.string()
739
- });
740
- var DiscoveredPortSchema = z4.object({
741
- port: z4.number().int().min(1).max(65535),
742
- label: z4.string().min(1).max(64).optional(),
743
- protocol: z4.enum(["http", "tcp"]).optional(),
744
- detectedAt: z4.string()
745
- });
746
- var ReportDiscoveredPortsRequestSchema = z4.object({
747
- sessionId: z4.string(),
748
- ports: z4.array(DiscoveredPortSchema).max(64)
749
- });
750
- var ReportBootMilestoneRequestSchema = z4.object({
751
- sessionId: z4.string(),
752
- key: z4.string().max(64)
753
- });
754
- var CreateSubtaskRequestSchema = z4.object({
755
- sessionId: z4.string(),
756
- title: z4.string().min(1),
757
- description: cardDescription,
758
- plan: z4.string().optional(),
759
- storyPointValue: z4.number().int().positive().optional(),
760
- ordinal: z4.number().int().nonnegative().optional(),
761
- followParentStatus: z4.boolean().optional(),
762
- /** Sibling subtask ids or slugs this subtask blocks on (explicit dependency
763
- * metadata — preferred over encoding order in plan text / ordinal). */
764
- dependsOn: z4.array(z4.string().min(1)).max(32).optional(),
765
- /** Glossary tag names to assign to the child. Unmatched names come back in
766
- * the response rather than failing the create. */
767
- tags: z4.array(z4.string().min(1)).max(10).optional()
768
- });
769
- var UpdateSubtaskRequestSchema = z4.object({
770
- sessionId: z4.string(),
771
- subtaskId: z4.string(),
772
- title: z4.string().min(1).optional(),
773
- description: cardDescription,
774
- plan: z4.string().optional(),
775
- /** Orchestration statuses only ("Planning" | "Open") — the pack parent's
776
- * sanctioned promotion path. Execution statuses stay with the build
777
- * pipeline / force_update_task_status. Enforced server-side. */
778
- status: z4.string().optional(),
779
- /** Assign a project agent to the child — accepts the agent's id or exact
780
- * name; resolved against the parent task's project server-side. */
781
- agentIdOrName: z4.string().min(1).optional(),
782
- storyPointValue: z4.number().int().positive().optional(),
783
- followParentStatus: z4.boolean().optional(),
784
- /** Replace this subtask's dependency edges with these sibling ids/slugs.
785
- * Empty array clears all. Omit to leave dependencies unchanged. */
786
- dependsOn: z4.array(z4.string().min(1)).max(32).optional()
787
- });
788
- var DeleteSubtaskRequestSchema = z4.object({
789
- sessionId: z4.string(),
790
- subtaskId: z4.string()
791
- });
792
- var SetSubtaskParentRequestSchema = z4.object({
793
- sessionId: z4.string(),
794
- taskId: z4.string().min(1),
795
- detach: z4.boolean().optional(),
796
- ordinal: z4.number().int().nonnegative().optional(),
797
- followParentStatus: z4.boolean().optional()
798
- });
799
- var GetTaskPropertiesRequestSchema = z4.object({
800
- sessionId: z4.string()
801
- });
802
- var UpdateTaskFieldsRequestSchema = z4.object({
803
- sessionId: z4.string(),
804
- plan: z4.string().optional(),
805
- description: cardDescription
806
- });
807
- var UpdateTaskPropertiesRequestSchema = z4.object({
808
- sessionId: z4.string(),
809
- title: z4.string().optional(),
810
- storyPointValue: z4.number().int().positive().optional(),
811
- tagIds: z4.array(z4.string()).optional(),
812
- tagNames: z4.array(z4.string()).optional(),
813
- githubPRUrl: z4.string().url().optional(),
814
- githubBranch: z4.string().optional(),
815
- // Canonical risk level, or null to clear — same semantics as the headless
816
- // update_task boundary (resolved to the project's Risk row in the handler).
817
- risk: riskLevelSchema.nullable().optional()
818
- });
819
- var ListIconsRequestSchema = z4.object({
820
- sessionId: z4.string()
821
- });
822
- var GenerateTaskIconRequestSchema = z4.object({
823
- sessionId: z4.string(),
824
- prompt: z4.string().min(1),
825
- aspectRatio: z4.string().optional()
826
- });
827
- var SearchFaIconsRequestSchema = z4.object({
828
- sessionId: z4.string(),
829
- query: z4.string().min(1),
830
- first: z4.number().int().positive().optional()
831
- });
832
- var PickFaIconRequestSchema = z4.object({
833
- sessionId: z4.string(),
834
- fontAwesomeId: z4.string().min(1),
835
- fontAwesomeStyle: z4.string().optional()
836
- });
837
- var CreateFollowUpTaskRequestSchema = z4.object({
838
- sessionId: z4.string(),
839
- title: z4.string().min(1),
840
- description: cardDescription,
841
- plan: z4.string().optional(),
842
- storyPointValue: z4.number().int().positive().optional()
843
- });
844
- var AddDependencyRequestSchema = z4.object({
845
- sessionId: z4.string(),
846
- dependsOnSlugOrId: z4.string()
847
- });
848
- var RemoveDependencyRequestSchema = z4.object({
849
- sessionId: z4.string(),
850
- dependsOnSlugOrId: z4.string()
851
- });
852
- var CreateSuggestionRequestSchema = z4.object({
853
- sessionId: z4.string(),
854
- title: z4.string().min(1),
855
- description: cardDescription,
856
- tagNames: z4.array(z4.string()).optional()
857
- });
858
- var VoteSuggestionRequestSchema = z4.object({
859
- sessionId: z4.string(),
860
- suggestionId: z4.string(),
861
- value: z4.union([z4.literal(1), z4.literal(-1)])
862
- });
863
- var TriggerIdentificationRequestSchema = z4.object({
864
- sessionId: z4.string()
865
- });
866
- var HandoffToImplementerRequestSchema = z4.object({
867
- sessionId: z4.string(),
868
- // Optional difficulty sizing — sets the task's story points before resolving
869
- // the matched implementer agent. Omit to hand off using the task's current
870
- // story points (or the project's default task agent when unsized).
871
- storyPoints: z4.number().int().positive().optional(),
872
- // Optional kickoff note posted to the task chat alongside the handoff notice.
873
- message: z4.string().optional()
874
- });
875
- var SubmitCodeReviewResultRequestSchema = z4.object({
876
- sessionId: z4.string(),
877
- approved: z4.boolean(),
878
- content: z4.string(),
879
- // Canonical risk level the reviewer assigned to this change. Required on every
880
- // verdict — the reviewer must judge it. Applied authoritatively server-side
881
- // (may raise OR lower an already-set value; the reviewer has that authority).
882
- risk: riskLevelSchema,
883
- // The commit SHA the reviewer actually reviewed. When present, the verdict is
884
- // rejected unless the task is still at this SHA (guards against a late
885
- // old-SHA verdict overwriting a newer review cycle).
886
- reviewedSha: z4.string().optional()
887
- });
888
- var CycleCodingAgentKeyRequestSchema = z4.object({
889
- sessionId: z4.string(),
890
- rateLimitType: z4.string(),
891
- resetsAt: z4.string().optional()
892
- });
893
- var StartChildCloudBuildRequestSchema = z4.object({
894
- sessionId: z4.string(),
895
- childTaskId: z4.string()
896
- });
897
- var StopChildBuildRequestSchema = z4.object({
898
- sessionId: z4.string(),
899
- childTaskId: z4.string()
900
- });
901
- var ApproveAndMergePRRequestSchema = z4.object({
902
- sessionId: z4.string(),
903
- childTaskId: z4.string()
904
- });
905
- var PostChildChatMessageRequestSchema = z4.object({
906
- sessionId: z4.string(),
907
- childTaskId: z4.string(),
908
- message: z4.string().min(1)
909
- });
910
- var UpdateChildStatusRequestSchema = z4.object({
911
- sessionId: z4.string(),
912
- childTaskId: z4.string(),
913
- status: z4.string()
914
- });
915
- var GetAgentStatusRequestSchema = z4.object({
916
- taskId: z4.string()
917
- });
918
- var GetUiCliHistoryRequestSchema = z4.object({
919
- taskId: z4.string()
920
- });
921
- var GetActivePtySessionRequestSchema = z4.object({
922
- taskId: z4.string()
923
- });
924
- var ListActivePtySessionsRequestSchema = z4.object({
925
- taskId: z4.string()
926
- });
927
- var SendSoftStopRequestSchema = z4.object({
928
- taskId: z4.string()
929
- });
930
- var StopTaskSessionRequestSchema = z4.object({
931
- taskId: z4.string(),
932
- sessionId: z4.string()
933
- });
934
- var FlushTaskQueueRequestSchema = z4.object({
935
- taskId: z4.string(),
936
- softStop: z4.boolean().optional()
937
- });
938
- var CancelTaskQueuedMessageRequestSchema = z4.object({
939
- taskId: z4.string(),
940
- messageId: z4.string()
941
- });
942
- var FlushSingleQueuedMessageRequestSchema = z4.object({
943
- taskId: z4.string(),
944
- messageId: z4.string(),
945
- softStop: z4.boolean().optional()
946
- });
947
- var AnswerAgentQuestionRequestSchema = z4.object({
948
- taskId: z4.string(),
949
- requestId: z4.string(),
950
- answers: z4.record(z4.string(), z4.string())
951
- });
952
- var ClearAgentTodosRequestSchema = z4.object({
953
- taskId: z4.string()
954
- });
955
- var AgentQuestionOptionSchema = z4.object({
956
- label: z4.string(),
957
- description: z4.string(),
958
- preview: z4.string().optional()
959
- });
960
- var AgentQuestionSchema = z4.object({
961
- question: z4.string(),
962
- header: z4.string(),
963
- options: z4.array(AgentQuestionOptionSchema),
964
- multiSelect: z4.boolean().optional()
965
- });
966
- var AskUserQuestionRequestSchema = z4.object({
967
- sessionId: z4.string(),
968
- question: z4.string().min(1),
969
- requestId: z4.string().min(1),
970
- questions: z4.array(AgentQuestionSchema).min(1)
971
- });
972
- var PostAgentMessageRequestSchema = z4.object({
973
- sessionId: z4.string().min(1),
974
- content: z4.string(),
975
- milestone: z4.enum(["plan_ready", "implementation_complete", "blocked"]).optional()
976
- });
977
- var EmitAgentEventRequestSchema = z4.object({
978
- sessionId: z4.string(),
979
- events: z4.array(AgentEventSchema).max(500)
980
- });
981
- var RefreshGithubTokenRequestSchema = z4.object({
982
- sessionId: z4.string(),
983
- forceFresh: z4.boolean().optional()
984
- });
985
- var ReportCredentialFailureRequestSchema = z4.object({
986
- sessionId: z4.string(),
987
- error: z4.string().max(2e3).optional(),
988
- tokenShape: z4.string().max(500).optional(),
989
- healed: z4.boolean().optional()
990
- });
991
- var ReportReviewSpawnFailureRequestSchema = z4.object({
992
- sessionId: z4.string(),
993
- reviewSessionId: z4.string(),
994
- error: z4.string().max(2e3).optional()
995
- });
996
- var ReportBuilderSpawnFailureRequestSchema = ReportReviewSpawnFailureRequestSchema.omit({
997
- reviewSessionId: true
998
- }).extend({ buildSessionId: z4.string() });
999
- var SpawnTaskSessionRequestSchema = z4.object({
1000
- taskId: z4.string(),
1001
- kind: z4.enum(["tui", "shell"])
1002
- });
1003
- var StartCodeReviewRequestSchema = z4.object({
1004
- taskId: z4.string(),
1005
- force: z4.boolean().optional()
1006
- });
1007
- var StopCodeReviewRequestSchema = z4.object({
1008
- taskId: z4.string()
1009
- });
1010
- var ReportSessionSpawnFailureRequestSchema = z4.object({
1011
- sessionId: z4.string(),
1012
- spawnedSessionId: z4.string(),
1013
- error: z4.string().max(2e3).optional()
1014
- });
1015
- var RefreshGithubTokenResponseSchema = z4.object({
1016
- token: z4.string()
1017
- });
1018
- var PTY_FRAME_MAX_CHARS = 256 * 1024;
1019
- var PTY_MAX_DIMENSION = 1e3;
1020
- var PtyOutputRequestSchema = z4.object({
1021
- sessionId: z4.string(),
1022
- data: z4.string().max(PTY_FRAME_MAX_CHARS),
1023
- cols: z4.number().int().positive().max(PTY_MAX_DIMENSION).optional(),
1024
- rows: z4.number().int().positive().max(PTY_MAX_DIMENSION).optional()
1025
- });
1026
- var PtyEndedRequestSchema = z4.object({
1027
- sessionId: z4.string()
1028
- });
1029
- var PtyInputRequestSchema = z4.object({
1030
- sessionId: z4.string(),
1031
- data: z4.string().max(PTY_FRAME_MAX_CHARS)
1032
- });
1033
- var PtyResizeRequestSchema = z4.object({
1034
- sessionId: z4.string(),
1035
- cols: z4.number().int().positive().max(PTY_MAX_DIMENSION),
1036
- rows: z4.number().int().positive().max(PTY_MAX_DIMENSION)
1037
- });
1038
- var PtyAttachRequestSchema = z4.object({
1039
- sessionId: z4.string()
1040
- });
1041
- var ReportPtyStreamRequestSchema = z4.object({
1042
- sessionId: z4.string(),
1043
- port: z4.number().int().positive().max(65535).nullable()
1044
- });
1045
- var GetPtyStreamEndpointRequestSchema = z4.object({
1046
- sessionId: z4.string()
1047
- });
1048
- var PtyChatEventPayloadSchema = z4.discriminatedUnion("kind", [
1049
- z4.object({
1050
- kind: z4.literal("init"),
1051
- model: z4.string().max(200),
1052
- claudeSessionId: z4.string().max(100).optional()
1053
- }),
1054
- z4.object({
1055
- kind: z4.literal("user_text"),
1056
- text: z4.string().max(16384),
1057
- // Set by the SERVER (never the agent) when this prompt was injected by
1058
- // Conveyor rather than typed by a human — the routed message's `source`
1059
- // (`ci_success`, `review_trigger`, `automated_feedback`, …). The agent
1060
- // pastes automated messages into the TUI exactly like human prompts, so the
1061
- // CLI records both as plain transcript `user` records; without this the
1062
- // builder chat renders "All CI checks passed on your PR." as the human's
1063
- // own bubble. Absent ⇒ a genuine human prompt.
1064
- source: z4.string().max(60).optional()
1065
- }),
1066
- z4.object({ kind: z4.literal("assistant_text"), text: z4.string().max(16384) }),
1067
- z4.object({
1068
- kind: z4.literal("tool_use"),
1069
- name: z4.string().max(200),
1070
- // Compact preview: JSON.stringify(input) truncated agent-side. The cap
1071
- // matches the text events because AskUserQuestion payloads ride this field
1072
- // and the web lifts them into an interactive card — a tight cap forced
1073
- // option descriptions down to 80 chars, making them unreadable. Every
1074
- // other tool keeps a far smaller agent-side budget (`TOOL_INPUT_MAX` in
1075
- // `chat-record-mapper.ts`), so the ring does not grow for normal calls.
1076
- input: z4.string().max(16384),
1077
- // Transcript tool_use block id — lets the client pair the tool_result.
1078
- id: z4.string().max(100).optional()
1079
- }),
1080
- z4.object({
1081
- kind: z4.literal("tool_result"),
1082
- // tool_use block id this result answers (absent on malformed records).
1083
- toolUseId: z4.string().max(100).optional(),
1084
- // Compact output preview, truncated agent-side.
1085
- output: z4.string().max(2e3),
1086
- isError: z4.boolean().optional()
1087
- }),
1088
- z4.object({ kind: z4.literal("turn_end") })
1089
- ]);
1090
- var PtyChatEventRequestSchema = z4.object({
1091
- sessionId: z4.string(),
1092
- event: PtyChatEventPayloadSchema
1093
- });
1094
- var PtyChatAttachRequestSchema = z4.object({
1095
- sessionId: z4.string()
1096
- });
1097
- var CreatePRResponseSchema = z4.object({
1098
- prNumber: z4.number().int().positive(),
1099
- prUrl: z4.string().url(),
1100
- /** Advisory glossary-upkeep note derived from the PR's changed files matched
1101
- * against tag contextPaths — rendered into the tool result, never stored. */
1102
- glossaryNote: z4.string().optional()
1103
- });
1104
- var PostToChatResponseSchema = z4.object({
1105
- messageId: z4.string()
1106
- });
1107
- var UpdateTaskStatusResponseSchema = z4.object({
1108
- taskId: z4.string(),
1109
- status: z4.string()
1110
- });
1111
- var StoreSessionIdResponseSchema = z4.object({
1112
- success: z4.boolean()
1113
- });
1114
- var HeartbeatResponseSchema = z4.object({
1115
- acknowledged: z4.boolean()
1116
- });
1117
- var SessionStartResponseSchema = z4.object({
1118
- sessionId: z4.string(),
1119
- startedAt: z4.string()
1120
- });
1121
- var SessionStopResponseSchema = z4.object({
1122
- sessionId: z4.string(),
1123
- stoppedAt: z4.string()
1124
- });
1125
- var DeleteSubtaskResponseSchema = z4.object({
1126
- deleted: z4.boolean()
1127
- });
1128
- var GIT_BRANCH_NAME_MAX = 255;
1129
- var GIT_BRANCH_NAME_MESSAGE = "Invalid git branch name \u2014 use only letters, numbers, '.', '_', '/' and '-', starting with a letter or number, with no '..', '@{' or '//', and no trailing '/', '-', '.' or '.lock'";
1130
- var ALLOWED_REF = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
1131
- function isValidGitBranchName(name) {
1132
- if (typeof name !== "string") return false;
1133
- if (name.length === 0 || name.length > GIT_BRANCH_NAME_MAX) return false;
1134
- if (!ALLOWED_REF.test(name)) return false;
1135
- if (name.includes("..") || name.includes("@{") || name.includes("//")) return false;
1136
- if (name.endsWith("/") || name.endsWith("-")) return false;
1137
- if (name.endsWith(".") || name.endsWith(".lock")) return false;
1138
- return true;
1139
- }
1140
- var cardDescription2 = z5.string().max(CARD_DESCRIPTION_MAX, CARD_DESCRIPTION_LIMIT_MESSAGE).optional();
1141
- var ListAccessibleProjectsRequestSchema = z5.object({
1142
- pageSize: z5.number().int().positive().max(100).optional().default(100)
1143
- });
1144
- var ListProjectTasksRequestSchema = z5.object({
1145
- projectId: z5.string(),
1146
- status: z5.string().optional(),
1147
- // Card types to include. Omitted/empty → defaults to ["task"] in the handler
1148
- // (mirrors searchProjectTasks) so listing doesn't surface incidents/suggestions
1149
- // unless asked. Enum validation lives at the MCP tool layer.
1150
- typeFilters: z5.array(z5.string()).optional(),
1151
- assigneeId: z5.string().optional(),
1152
- unassigned: z5.boolean().optional(),
1153
- // Scope to a sub-project board when provided. Unlike the board layer's `?? null`
1154
- // semantics, agents default to seeing the whole project when omitted.
1155
- subProjectId: z5.string().nullable().optional(),
1156
- limit: z5.number().int().positive().optional().default(50)
1157
- }).refine((p) => !(p.unassigned && p.assigneeId), {
1158
- message: "Pass either assigneeId or unassigned, not both"
1159
- });
1160
- var GetProjectTaskRequestSchema = z5.object({
1161
- projectId: z5.string(),
1162
- taskId: z5.string()
1163
- });
1164
- var SearchProjectTasksRequestSchema = z5.object({
1165
- projectId: z5.string(),
1166
- // Tag names, matched case-insensitively against the project glossary.
1167
- tagNames: z5.array(z5.string()).optional(),
1168
- // How to combine tagNames: "any" (default) = carries at least one,
1169
- // "all" = carries every one.
1170
- tagMatch: z5.enum(["any", "all"]).optional(),
1171
- // Expand each named tag to its descendants in the tag DAG before matching,
1172
- // so a parent tag sweeps its whole area. Default false.
1173
- includeChildTags: z5.boolean().optional(),
1174
- searchQuery: z5.string().optional(),
1175
- statusFilters: z5.array(z5.string()).optional(),
1176
- // Card types to include. Omitted/empty → defaults to ["task"] in the handler so
1177
- // search doesn't surface incidents/suggestions unless asked. Enum validation lives
1178
- // at the MCP tool layer (mirrors statusFilters).
1179
- typeFilters: z5.array(z5.string()).optional(),
1180
- assigneeId: z5.string().optional(),
1181
- unassigned: z5.boolean().optional(),
1182
- // Scope to a sub-project board when provided. Unlike the board layer's `?? null`
1183
- // semantics, agents default to seeing the whole project when omitted.
1184
- subProjectId: z5.string().nullable().optional(),
1185
- limit: z5.number().int().positive().optional().default(20)
1186
- }).refine((p) => !(p.unassigned && p.assigneeId), {
1187
- message: "Pass either assigneeId or unassigned, not both"
1188
- });
1189
- var ListProjectTagsRequestSchema = z5.object({
1190
- projectId: z5.string()
1191
- });
1192
- var GetProjectTagRequestSchema = z5.object({
1193
- projectId: z5.string(),
1194
- /** Tag id or exact (case-insensitive) tag name. */
1195
- tag: z5.string().min(1).max(100)
1196
- });
1197
- var ListProjectTagAttachmentsRequestSchema = z5.object({
1198
- projectId: z5.string(),
1199
- /** Tag id or exact (case-insensitive) tag name. */
1200
- tag: z5.string().min(1).max(100),
1201
- limit: z5.number().int().min(1).max(60).optional(),
1202
- offset: z5.number().int().min(0).optional()
1203
- });
1204
- var SetProjectFileTagsRequestSchema = z5.object({
1205
- projectId: z5.string(),
1206
- taskId: z5.string(),
1207
- fileId: z5.string(),
1208
- tags: z5.array(z5.string().min(1).max(MAX_FILE_TAG_LENGTH)).max(MAX_FILE_TAGS),
1209
- requestingUserId: z5.string().optional()
1210
- });
1211
- var GetProjectSummaryRequestSchema = z5.object({
1212
- projectId: z5.string()
1213
- });
1214
- var GetProjectOnboardingStatusRequestSchema = z5.object({
1215
- projectId: z5.string()
1216
- });
1217
- var GetProjectOnboardingStepRequestSchema = z5.object({
1218
- projectId: z5.string()
1219
- });
1220
- var GetProjectConnectUrlsRequestSchema = z5.object({
1221
- projectId: z5.string()
1222
- });
1223
- var conveyorCapabilitySchema = z5.enum([
1224
- "read",
1225
- "create",
1226
- "update",
1227
- "chat",
1228
- "files",
1229
- "build"
1230
- ]);
1231
- var GetConnectionContextRequestSchema = z5.object({
1232
- projectId: z5.string(),
1233
- // Optional board scope (CONVEYOR_SUBPROJECT_ID). Validated to belong to the
1234
- // project in the handler; an invalid/foreign id is reported, not silently
1235
- // dropped, so a mis-scoped connection is never presented as board-specific.
1236
- subProjectId: z5.string().nullable().optional()
1237
- });
1238
- var VerifyConnectionRequestSchema = z5.object({
1239
- projectId: z5.string(),
1240
- subProjectId: z5.string().nullable().optional(),
1241
- intendedActions: z5.array(conveyorCapabilitySchema).optional()
1242
- });
1243
- var ListAccessibleSubprojectsRequestSchema = z5.object({
1244
- projectId: z5.string()
1245
- });
1246
- var CreateProjectTaskRequestSchema = z5.object({
1247
- projectId: z5.string(),
1248
- title: z5.string().min(1),
1249
- description: cardDescription2,
1250
- plan: z5.string().optional(),
1251
- status: z5.string().optional(),
1252
- // Assign to a sub-project board. Validated to belong to `projectId` in the handler.
1253
- subProjectId: z5.string().nullable().optional(),
1254
- requestingUserId: z5.string().optional()
1255
- });
1256
- var SetProjectTaskParentRequestSchema = z5.object({
1257
- projectId: z5.string(),
1258
- /** Card to move — id or slug. */
1259
- taskId: z5.string().min(1),
1260
- /** New parent (id or slug), or null to detach. */
1261
- parentTaskId: z5.string().min(1).nullable(),
1262
- ordinal: z5.number().int().nonnegative().optional(),
1263
- followParentStatus: z5.boolean().optional(),
1264
- requestingUserId: z5.string().optional()
1265
- }).strict();
1266
- var UpdateProjectTaskRequestSchema = z5.object({
1267
- projectId: z5.string(),
1268
- taskId: z5.string(),
1269
- title: z5.string().optional(),
1270
- description: cardDescription2,
1271
- plan: z5.string().optional(),
1272
- // Enum validation lives at the MCP tool layer (mirrors createProjectTask);
1273
- // the handler routes through the shared updateStatus core (InProgress
1274
- // dependency check + cleanup/board/Slack side effects), not the stricter
1275
- // card-type-validating path the Socket.IO updateTaskStatus mutation uses.
1276
- status: z5.string().optional(),
1277
- // Canonical risk level, or null to clear. Resolved to the project's
1278
- // configured Risk row (by rank) in the handler.
1279
- risk: riskLevelSchema.nullable().optional(),
1280
- // Story-point value, or null to clear. Resolved to the project's configured
1281
- // StoryPoint row in the handler, which rejects an unconfigured value.
1282
- storyPointValue: z5.number().int().positive().nullable().optional(),
1283
- assignedUserId: z5.string().nullish(),
1284
- // Move to a different sub-project board, or null to move to the parent board.
1285
- // Validated to belong to `projectId` in the handler.
1286
- subProjectId: z5.string().nullable().optional(),
1287
- // Record the task's ACTUAL working branch (e.g. a locally-driven pack's
1288
- // branch, so identification never mints a competing name and pack-child
1289
- // merge handling matches reality), or null to detach. Guarded in the
1290
- // handler: the ref must exist on origin and no live workspace may be bound
1291
- // to a different branch.
1292
- githubBranch: z5.string().min(1).max(GIT_BRANCH_NAME_MAX).refine(isValidGitBranchName, { message: GIT_BRANCH_NAME_MESSAGE }).nullable().optional(),
1293
- requestingUserId: z5.string().optional()
1294
- }).strict().refine(
1295
- (v) => v.title !== void 0 || v.description !== void 0 || v.plan !== void 0 || v.status !== void 0 || v.risk !== void 0 || v.storyPointValue !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0 || v.githubBranch !== void 0,
1296
- {
1297
- message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, subProjectId, or githubBranch)"
1298
- }
1299
- );
1300
- var TransitionProjectTaskStatusRequestSchema = z5.object({
1301
- projectId: z5.string(),
1302
- taskId: z5.string(),
1303
- toStatus: z5.string(),
1304
- expectedFromStatus: z5.string().optional(),
1305
- // Optional raise-only risk to attempt alongside the transition
1306
- // (approve → low, request_changes → medium by default).
1307
- risk: riskLevelSchema.optional(),
1308
- requestingUserId: z5.string().optional()
1309
- });
1310
- var MoveProjectCardRequestSchema = z5.object({
1311
- projectId: z5.string(),
1312
- taskId: z5.string(),
1313
- destinationProjectId: z5.string(),
1314
- requestingUserId: z5.string().optional()
1315
- });
1316
- var PostToProjectTaskChatRequestSchema = z5.object({
1317
- projectId: z5.string(),
1318
- taskId: z5.string(),
1319
- content: z5.string(),
1320
- requestingUserId: z5.string().optional()
1321
- });
1322
- var GetProjectTaskCliRequestSchema = z5.object({
1323
- projectId: z5.string(),
1324
- taskId: z5.string(),
1325
- limit: z5.number().int().positive().optional().default(50),
1326
- source: z5.string().optional()
1327
- });
1328
- var GetProjectTaskSessionsRequestSchema = z5.object({
1329
- projectId: z5.string(),
1330
- taskId: z5.string()
1331
- });
1332
- var QueryProjectGcpLogsRequestSchema = z5.object({
1333
- projectId: z5.string(),
1334
- env: z5.enum(["prod", "dev", "claudespace"]).optional(),
1335
- severity: z5.enum(["DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY"]).optional(),
1336
- services: z5.array(z5.string().min(1).max(200)).max(25).optional(),
1337
- sqlInstances: z5.array(z5.string().min(1).max(200)).max(25).optional(),
1338
- allServices: z5.boolean().optional(),
1339
- search: z5.string().max(256).optional(),
1340
- filter: z5.string().max(1e3).optional(),
1341
- startTime: z5.string().optional(),
1342
- endTime: z5.string().optional(),
1343
- limit: z5.number().int().min(1).max(200).optional().default(50),
1344
- pageToken: z5.string().max(4096).optional()
1345
- });
1346
- var QueryProjectGrafanaLogsRequestSchema = z5.object({
1347
- projectId: z5.string(),
1348
- env: z5.enum(["prod", "dev"]).optional(),
1349
- services: z5.array(z5.string().min(1).max(200)).max(25).optional(),
1350
- level: z5.enum(["debug", "info", "warn", "error", "fatal"]).optional(),
1351
- search: z5.string().max(256).optional(),
1352
- logql: z5.string().max(2e3).optional(),
1353
- startTime: z5.string().optional(),
1354
- endTime: z5.string().optional(),
1355
- limit: z5.number().int().min(1).max(200).optional().default(50)
1356
- });
1357
- var driveFileNameSchema = z5.string().min(1).max(255).regex(/^[^/\\\r\n]+$/, "File names cannot contain slashes or line breaks");
1358
- var DRIVE_MAX_CONTENT_CHARS = 1e6;
1359
- var ListProjectDriveFilesRequestSchema = z5.object({
1360
- projectId: z5.string(),
1361
- folderId: z5.string().max(200).optional(),
1362
- search: z5.string().max(200).optional(),
1363
- limit: z5.number().int().min(1).max(200).optional()
1364
- });
1365
- var ReadProjectDriveFileRequestSchema = z5.object({
1366
- projectId: z5.string(),
1367
- fileId: z5.string().min(1).max(200)
1368
- });
1369
- var CreateProjectDriveFileRequestSchema = z5.object({
1370
- projectId: z5.string(),
1371
- name: driveFileNameSchema,
1372
- content: z5.string().max(DRIVE_MAX_CONTENT_CHARS),
1373
- mimeType: z5.string().max(200).optional(),
1374
- folderId: z5.string().max(200).optional()
1375
- });
1376
- var UpdateProjectDriveFileRequestSchema = z5.object({
1377
- projectId: z5.string(),
1378
- fileId: z5.string().min(1).max(200),
1379
- content: z5.string().max(DRIVE_MAX_CONTENT_CHARS),
1380
- mimeType: z5.string().max(200).optional()
1381
- });
1382
- var DeleteProjectDriveFileRequestSchema = z5.object({
1383
- projectId: z5.string(),
1384
- fileId: z5.string().min(1).max(200)
1385
- });
1386
- var CreateProjectDriveFolderRequestSchema = z5.object({
1387
- projectId: z5.string(),
1388
- name: driveFileNameSchema,
1389
- folderId: z5.string().max(200).optional()
1390
- });
1391
- var StartProjectBuildRequestSchema = z5.object({
1392
- projectId: z5.string(),
1393
- taskId: z5.string(),
1394
- requestingUserId: z5.string().optional()
1395
- });
1396
- var StopProjectBuildRequestSchema = z5.object({
1397
- projectId: z5.string(),
1398
- taskId: z5.string(),
1399
- requestingUserId: z5.string().optional()
1400
- });
1401
- var StartProjectWorkspaceRequestSchema = z5.object({
1402
- projectId: z5.string(),
1403
- requestingUserId: z5.string().optional()
1404
- });
1405
- var StopProjectWorkspaceRequestSchema = z5.object({
1406
- projectId: z5.string(),
1407
- destroy: z5.boolean().optional(),
1408
- requestingUserId: z5.string().optional()
1409
- });
1410
- var ListMyLiveSessionsRequestSchema = z5.object({
1411
- projectId: z5.string(),
1412
- /** Admin-only: list another member's sessions instead of the caller's. */
1413
- targetUserId: z5.string().optional()
1414
- });
1415
- var ListProjectSessionGroupsRequestSchema = z5.object({
1416
- projectId: z5.string()
1417
- });
1418
- var ListMyLiveSessionsAcrossProjectsRequestSchema = z5.object({});
1419
- var ListSessionGroupsAcrossProjectsRequestSchema = z5.object({});
1420
- var GetProjectAvailableTuisRequestSchema = z5.object({
1421
- projectId: z5.string()
1422
- });
1423
- var StartAdhocSessionRequestSchema = z5.object({
1424
- projectId: z5.string(),
1425
- label: z5.string().max(200).optional(),
1426
- /** Coding-agent key to launch under — validated pick-time (ownership + TUI availability) in the handler. */
1427
- codingAgentKeyId: z5.string().optional(),
1428
- /** Model override (Claude model id) — overrides the launch key's own model. */
1429
- model: z5.string().max(200).optional(),
1430
- /**
1431
- * Session role. Constrained: other task-less modes fall through to the pm
1432
- * runner in the pod entrypoint, and "review" would crash without a task.
1433
- */
1434
- mode: z5.enum(["adhoc", "pm"]).optional(),
1435
- /** Base branch to check out (defaults to the project's dev branch). */
1436
- branch: z5.string().max(300).optional(),
1437
- /**
1438
- * Server-assembled instructions the pod's TUI auto-submits once on first boot
1439
- * (headless kickoff). Used by the onboarding "Set it up for me" flow to seed a
1440
- * setup-driver prompt; the session stays watchable/interactive in the Sessions
1441
- * view. `ensureAdhocWorkspace` persists it and clears it after first submit.
1442
- */
1443
- initialPrompt: z5.string().max(2e4).optional(),
1444
- requestingUserId: z5.string().optional()
1445
- });
1446
- var StopAdhocSessionRequestSchema = z5.object({
1447
- projectId: z5.string(),
1448
- workspaceId: z5.string(),
1449
- destroy: z5.boolean().optional(),
1450
- requestingUserId: z5.string().optional()
1451
- });
1452
- var ResumeAdhocSessionRequestSchema = z5.object({
1453
- projectId: z5.string(),
1454
- workspaceId: z5.string(),
1455
- requestingUserId: z5.string().optional()
1456
- });
1457
- var RefreshCodingAgentKeyUsageRequestSchema = z5.object({
1458
- projectId: z5.string(),
1459
- keyId: z5.string().optional(),
1460
- requestingUserId: z5.string().optional()
1461
- });
1462
- var ListKeysToProbeRequestSchema = z5.object({
1463
- sessionId: z5.string()
1464
- });
1465
- var CreateProjectReleaseRequestSchema = z5.object({
1466
- projectId: z5.string(),
1467
- taskIds: z5.array(z5.string()).optional(),
1468
- requestingUserId: z5.string().optional()
1469
- });
1470
- var AddTasksToProjectReleaseRequestSchema = z5.object({
1471
- projectId: z5.string(),
1472
- taskIds: z5.array(z5.string()).min(1),
1473
- requestingUserId: z5.string().optional()
1474
- });
1475
- var ApproveProjectMergePRRequestSchema = z5.object({
1476
- projectId: z5.string(),
1477
- childTaskId: z5.string(),
1478
- requestingUserId: z5.string().optional()
1479
- });
1480
- var ListProjectSubtasksRequestSchema = z5.object({
1481
- projectId: z5.string(),
1482
- taskId: z5.string()
1483
- });
1484
- var CreateProjectSubtaskRequestSchema = z5.object({
1485
- projectId: z5.string(),
1486
- parentTaskId: z5.string(),
1487
- title: z5.string().min(1),
1488
- description: cardDescription2,
1489
- plan: z5.string().optional(),
1490
- ordinal: z5.number().int().nonnegative().optional(),
1491
- storyPointValue: z5.number().int().positive().optional(),
1492
- followParentStatus: z5.boolean().optional(),
1493
- /** Sibling subtask ids or slugs this subtask blocks on (explicit dependency
1494
- * metadata — preferred over encoding order in plan text / ordinal). */
1495
- dependsOn: z5.array(z5.string().min(1)).max(32).optional(),
1496
- requestingUserId: z5.string().optional()
1497
- });
1498
- var UpdateProjectSubtaskRequestSchema = z5.object({
1499
- projectId: z5.string(),
1500
- subtaskId: z5.string(),
1501
- title: z5.string().optional(),
1502
- description: cardDescription2,
1503
- plan: z5.string().optional(),
1504
- status: z5.string().optional(),
1505
- ordinal: z5.number().int().nonnegative().optional(),
1506
- storyPointValue: z5.number().int().positive().optional(),
1507
- followParentStatus: z5.boolean().optional(),
1508
- /** Replace-set of sibling subtask ids/slugs this subtask blocks on ([] clears).
1509
- * Mirrors the in-pod updateSubtask semantics. */
1510
- dependsOn: z5.array(z5.string().min(1)).max(32).optional(),
1511
- requestingUserId: z5.string().optional()
1512
- });
1513
- var DeleteProjectSubtaskRequestSchema = z5.object({
1514
- projectId: z5.string(),
1515
- subtaskId: z5.string(),
1516
- requestingUserId: z5.string().optional()
1517
- });
1518
- var GetProjectTaskChatRequestSchema = z5.object({
1519
- projectId: z5.string(),
1520
- taskId: z5.string(),
1521
- limit: z5.number().int().positive().optional().default(20)
1522
- });
1523
- var AddProjectTaskDependencyRequestSchema = z5.object({
1524
- projectId: z5.string(),
1525
- taskId: z5.string(),
1526
- dependsOnSlugOrId: z5.string(),
1527
- requestingUserId: z5.string().optional()
1528
- });
1529
- var RemoveProjectTaskDependencyRequestSchema = z5.object({
1530
- projectId: z5.string(),
1531
- taskId: z5.string(),
1532
- dependsOnSlugOrId: z5.string(),
1533
- requestingUserId: z5.string().optional()
1534
- });
1535
- var VoteProjectSuggestionRequestSchema = z5.object({
1536
- projectId: z5.string(),
1537
- suggestionId: z5.string(),
1538
- value: z5.union([z5.literal(1), z5.literal(-1)]),
1539
- requestingUserId: z5.string().optional()
1540
- });
1541
- var GetProjectTaskDependenciesRequestSchema = z5.object({
1542
- projectId: z5.string(),
1543
- taskId: z5.string()
1544
- });
1545
- var ListProjectTaskFilesRequestSchema = z5.object({
1546
- projectId: z5.string(),
1547
- taskId: z5.string()
1548
- });
1549
- var GetProjectAttachmentRequestSchema = z5.object({
1550
- projectId: z5.string(),
1551
- taskId: z5.string(),
1552
- fileId: z5.string(),
1553
- /** Byte offset into text content (paging large logs/JSON). Default 0. */
1554
- offset: z5.number().int().nonnegative().optional(),
1555
- /** Max bytes of text content to return from `offset`. Server default applies. */
1556
- maxBytes: z5.number().int().positive().optional()
1557
- });
1558
- var RequestProjectFileUploadRequestSchema = z5.object({
1559
- projectId: z5.string(),
1560
- taskId: z5.string(),
1561
- fileName: z5.string().min(1).max(255),
1562
- mimeType: z5.string().min(1).max(128),
1563
- fileSize: z5.number().int().positive().max(MAX_FILE_SIZE_BYTES),
1564
- requestingUserId: z5.string().optional()
1565
- });
1566
- var ConfirmProjectFileUploadRequestSchema = z5.object({
1567
- projectId: z5.string(),
1568
- taskId: z5.string(),
1569
- fileId: z5.string(),
1570
- /** When set, the attachment is also posted to the task chat with this text. */
1571
- comment: z5.string().max(2e3).optional(),
1572
- /** Glossary tag names (or ids) this file is an example of. */
1573
- tags: z5.array(z5.string().min(1).max(MAX_FILE_TAG_LENGTH)).max(MAX_FILE_TAGS).optional(),
1574
- requestingUserId: z5.string().optional()
1575
- });
1576
- var CreateProjectPullRequestRequestSchema = z5.object({
1577
- projectId: z5.string(),
1578
- taskId: z5.string(),
1579
- title: z5.string().min(1),
1580
- body: z5.string(),
1581
- head: z5.string().optional(),
1582
- base: z5.string().optional(),
1583
- requestingUserId: z5.string().optional()
1584
- });
1585
- var ListProjectMembersRequestSchema = z5.object({
1586
- projectId: z5.string()
1587
- });
1588
- var AddProjectTaskReviewerRequestSchema = z5.object({
1589
- projectId: z5.string(),
1590
- taskId: z5.string(),
1591
- userId: z5.string(),
1592
- requestingUserId: z5.string().optional()
1593
- });
1594
- var RemoveProjectTaskReviewerRequestSchema = z5.object({
1595
- projectId: z5.string(),
1596
- taskId: z5.string(),
1597
- userId: z5.string(),
1598
- requestingUserId: z5.string().optional()
1599
- });
1600
- var ListProjectManualTestsRequestSchema = z5.object({
1601
- projectId: z5.string(),
1602
- taskId: z5.string()
1603
- });
1604
- var QueryProjectManualTestsRequestSchema = z5.object({
1605
- projectId: z5.string(),
1606
- cardStatuses: z5.array(z5.string()).optional(),
1607
- testStatuses: z5.array(z5.enum(["open", "approved", "rejected"])).optional()
1608
- });
1609
- var SetProjectManualTestsRequestSchema = z5.object({
1610
- projectId: z5.string(),
1611
- taskId: z5.string(),
1612
- items: z5.array(z5.object({ title: z5.string().min(1) })).min(1),
1613
- requestingUserId: z5.string().optional()
1614
- });
1615
- var EditProjectManualTestRequestSchema = z5.object({
1616
- projectId: z5.string(),
1617
- taskId: z5.string(),
1618
- title: z5.string().min(1),
1619
- newTitle: z5.string().min(1),
1620
- requestingUserId: z5.string().optional()
1621
- });
1622
- var RemoveProjectManualTestRequestSchema = z5.object({
1623
- projectId: z5.string(),
1624
- taskId: z5.string(),
1625
- title: z5.string().min(1),
1626
- requestingUserId: z5.string().optional()
1627
- });
1628
- var ApproveProjectManualTestRequestSchema = z5.object({
1629
- projectId: z5.string(),
1630
- taskId: z5.string(),
1631
- title: z5.string().min(1),
1632
- requestingUserId: z5.string().optional()
1633
- });
1634
- var RejectProjectManualTestRequestSchema = z5.object({
1635
- projectId: z5.string(),
1636
- taskId: z5.string(),
1637
- title: z5.string().min(1),
1638
- reason: z5.string().min(1).max(2e3),
1639
- requestingUserId: z5.string().optional()
1640
- });
1641
- var CreateProjectSuggestionRequestSchema = z5.object({
1642
- projectId: z5.string(),
1643
- title: z5.string().min(1),
1644
- description: cardDescription2,
1645
- tagNames: z5.array(z5.string()).optional(),
1646
- requestingUserId: z5.string().optional()
1647
- });
1648
- var ListProjectChannelsRequestSchema = z6.object({
1649
- projectId: z6.string()
1650
- });
1651
- var READ_CHANNEL_MESSAGES_MAX_LIMIT = 50;
1652
- var ReadChannelMessagesRequestSchema = z6.object({
1653
- projectId: z6.string(),
1654
- channelId: z6.string().min(1).max(200),
1655
- limit: z6.number().int().min(1).max(READ_CHANNEL_MESSAGES_MAX_LIMIT).optional(),
1656
- /** Provider-native cursor: return messages OLDER than this one. */
1657
- before: z6.string().max(100).optional(),
1658
- /** Provider-native cursor: return messages NEWER than this one. */
1659
- after: z6.string().max(100).optional(),
1660
- /**
1661
- * Read one thread instead of the channel surface. Slack calls this a
1662
- * `thread_ts`; on Discord it is the thread channel's id. One field for both,
1663
- * because a caller holding a `threadTs` from a previous read should not have
1664
- * to know which provider produced it.
1665
- */
1666
- threadTs: z6.string().max(100).optional()
1667
- });
1668
- var POST_CHANNEL_MESSAGE_MAX_CHARS = 1800;
1669
- var PostChannelMessageRequestSchema = z6.object({
1670
- projectId: z6.string(),
1671
- channelId: z6.string().min(1).max(200),
1672
- text: z6.string().min(1).max(POST_CHANNEL_MESSAGE_MAX_CHARS),
1673
- /** Reply inside a thread rather than to the channel. */
1674
- threadTs: z6.string().max(100).optional()
1675
- });
1676
- var GET_ANALYTICS_SUMMARY_MAX_RANGE_DAYS = 90;
1677
- var GetProjectAnalyticsSummaryRequestSchema = z6.object({
1678
- projectId: z6.string(),
1679
- rangeDays: z6.number().int().min(1).max(GET_ANALYTICS_SUMMARY_MAX_RANGE_DAYS).optional(),
1680
- campaign: z6.string().max(200).optional()
1681
- });
1682
- var RequestWorkspaceRecycleRequestSchema = z7.object({
1683
- sessionId: z7.string(),
1684
- reason: z7.string().max(2e3)
1685
- });
1686
- var ReportApiOutageRequestSchema = z7.object({
1687
- sessionId: z7.string(),
1688
- detail: z7.string().max(2e3),
1689
- attempts: z7.number().int().min(0).max(100)
1690
- });
1691
- var SHA_PATTERN = /^[0-9a-f]{40}$/i;
1692
- var ReviewGuideFileReferenceSchema = z8.object({
1693
- path: z8.string().min(1).max(500),
1694
- startLine: z8.number().int().positive().max(1e6).optional(),
1695
- endLine: z8.number().int().positive().max(1e6).optional(),
1696
- hunkHeader: z8.string().min(1).max(300).optional()
1697
- }).strict().superRefine((value, ctx) => {
1698
- if (value.endLine !== void 0 && value.startLine === void 0) {
1699
- ctx.addIssue({
1700
- code: "custom",
1701
- path: ["startLine"],
1702
- message: "startLine is required when endLine is set"
1703
- });
1704
- }
1705
- if (value.startLine !== void 0 && value.endLine !== void 0 && value.endLine < value.startLine) {
1706
- ctx.addIssue({
1707
- code: "custom",
1708
- path: ["endLine"],
1709
- message: "endLine must be greater than or equal to startLine"
1710
- });
1711
- }
1712
- });
1713
- var ReviewGuideSectionSchema = z8.object({
1714
- title: z8.string().min(1).max(160),
1715
- explanation: z8.string().min(1).max(2e3),
1716
- classification: z8.enum(["core", "supporting"]).optional(),
1717
- files: z8.array(ReviewGuideFileReferenceSchema).min(1).max(20)
1718
- }).strict();
1719
- var ReviewGuideContentSchema = z8.object({
1720
- overview: z8.string().min(1).max(3e3),
1721
- sections: z8.array(ReviewGuideSectionSchema).min(1).max(12)
1722
- }).strict();
1723
- var PublishReviewGuideRequestSchema = ReviewGuideContentSchema.extend({
1724
- sessionId: z8.string().min(1),
1725
- reviewedSha: z8.string().regex(SHA_PATTERN, "reviewedSha must be a 40-character commit SHA")
1726
- }).strict();
1727
- var CONTEXT_LINK_LOCATOR_MAX = 300;
1728
- var TEST_TITLE = /\b(?:it|test|describe)(?:\.\w+)?\s*\(\s*(['"`])((?:(?!\1)[\s\S])*)\1/g;
1729
- function extractTestTitles(content) {
1730
- return [...content.matchAll(TEST_TITLE)].map((m) => m[2]);
1731
- }
1732
- function isPlaceholderLocator(locator) {
1733
- return locator.includes("<") || locator.includes(">");
1734
- }
1735
- function locatorMatchesContent(content, locatorType, locator) {
1736
- if (locatorType === "code") return content.includes(locator);
1737
- return extractTestTitles(content).some((title) => title.includes(locator));
1738
- }
1739
- var TAG_DESCRIPTION_MAX = CARD_DESCRIPTION_MAX;
1740
- var TAG_OVERVIEW_MAX = 32e3;
1741
- var TAG_REASON_MAX = 500;
1742
- var ProjectTagContextPathSchema = z9.object({
1743
- type: z9.enum(["rule", "doc", "file", "folder"]),
1744
- path: z9.string().min(1).max(500),
1745
- label: z9.string().max(100).optional(),
1746
- /** Verified-link tether — text that must keep existing in the file. */
1747
- locator: z9.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional(),
1748
- /** test = must appear in a real test/describe title; code = any substring. */
1749
- locatorType: z9.enum(["test", "code"]).optional()
1750
- }).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
1751
- message: "locator and locatorType must be provided together"
1752
- }).refine((link) => link.locator === void 0 || link.type !== "folder", {
1753
- message: "folder links cannot carry a locator"
1754
- });
1755
- var hexColor = z9.string().regex(/^#[0-9a-fA-F]{6}$/, "Expected #RRGGBB hex color");
1756
- var overviewPathSchema = z9.string().min(1).max(500).regex(/^[^\r\n]*$/, "Overview path cannot contain line breaks");
1757
- var CreateProjectTagRequestSchema = z9.object({
1758
- projectId: z9.string(),
1759
- name: z9.string().min(1).max(50),
1760
- color: hexColor.optional(),
1761
- description: z9.string().max(TAG_DESCRIPTION_MAX).optional(),
1762
- overview: z9.string().max(TAG_OVERVIEW_MAX).optional(),
1763
- /** Source the overview from this repo file (stored overview stays as the pending fallback). */
1764
- overviewPath: overviewPathSchema.optional(),
1765
- contextPaths: z9.array(ProjectTagContextPathSchema).max(20).optional(),
1766
- /** Parents to link at create time (multi-parent DAG). */
1767
- parentTagIds: z9.array(z9.string()).max(25).optional(),
1768
- requestingUserId: z9.string().optional()
1769
- });
1770
- var UpdateProjectTagRequestSchema = z9.object({
1771
- projectId: z9.string(),
1772
- tagId: z9.string(),
1773
- name: z9.string().min(1).max(50).optional(),
1774
- color: hexColor.optional(),
1775
- description: z9.string().max(TAG_DESCRIPTION_MAX).optional(),
1776
- /** Full markdown glossary body; null clears it. Rejected while overviewPath is set. */
1777
- overview: z9.string().max(TAG_OVERVIEW_MAX).nullable().optional(),
1778
- /** Repo file to source the overview from; null clears back to the stored overview. */
1779
- overviewPath: overviewPathSchema.nullable().optional(),
1780
- /** Full replacement of the tag's context links when provided. */
1781
- contextPaths: z9.array(ProjectTagContextPathSchema).max(20).optional(),
1782
- /** Full-set replacement of the tag's parent tags (multi-parent DAG). */
1783
- parentTagIds: z9.array(z9.string()).max(25).optional(),
1784
- /** One-line revision provenance, recorded in the tag's history. */
1785
- reason: z9.string().max(TAG_REASON_MAX).optional(),
1786
- /** Card the caller was working in — stamped into the revision history. */
1787
- taskId: z9.string().optional(),
1788
- requestingUserId: z9.string().optional()
1789
- });
1790
- var PostToProjectChatRequestSchema = z9.object({
1791
- projectId: z9.string(),
1792
- content: z9.string().min(1).max(2e4),
1793
- requestingUserId: z9.string().optional(),
1794
- /** Marks the post so the server can persist it beyond chat (tag-audit summaries land in tag history). */
1795
- kind: z9.enum(["tag_audit_summary"]).optional()
1796
- });
1797
- var StartTagAuditRequestSchema = z9.object({
1798
- projectId: z9.string(),
1799
- requestingUserId: z9.string().optional()
1800
- });
1801
- var StartTaskAuditRequestSchema = z9.object({
1802
- projectId: z9.string(),
1803
- taskIds: z9.array(z9.string()).min(1).max(20),
1804
- requestingUserId: z9.string().optional()
1805
- });
1806
- var GetActiveAuditSessionsRequestSchema = z9.object({
1807
- projectId: z9.string()
1808
- });
1809
- var ReportTaskAuditResultRequestSchema = z9.object({
1810
- projectId: z9.string(),
1811
- taskId: z9.string(),
1812
- summary: z9.string(),
1813
- turnGrades: z9.array(
1814
- z9.object({
1815
- turnIndex: z9.number(),
1816
- phase: z9.enum(["planning", "building", "human"]),
1817
- grade: z9.enum(["correct", "neutral", "blunder"]),
1818
- reasoning: z9.string(),
1819
- eventType: z9.string(),
1820
- eventSummary: z9.string()
1821
- })
1822
- ),
1823
- planningAccuracy: z9.number().nullable(),
1824
- buildingAccuracy: z9.number().nullable(),
1825
- humanAccuracy: z9.number().nullable(),
1826
- planningCorrect: z9.number(),
1827
- planningNeutral: z9.number(),
1828
- planningBlunder: z9.number(),
1829
- buildingCorrect: z9.number(),
1830
- buildingNeutral: z9.number(),
1831
- buildingBlunder: z9.number(),
1832
- humanCorrect: z9.number(),
1833
- humanNeutral: z9.number(),
1834
- humanBlunder: z9.number(),
1835
- humanEvaluations: z9.array(
1836
- z9.object({
1837
- messageIndex: z9.number(),
1838
- rating: z9.union([z9.literal(-1), z9.literal(0), z9.literal(1)]),
1839
- reasoning: z9.string()
1840
- })
1841
- ).optional(),
1842
- suggestionIds: z9.array(z9.string()),
1843
- auditCostUsd: z9.number().nullable(),
1844
- model: z9.string().nullable(),
1845
- /** When set, the audit is marked failed with this message instead. */
1846
- error: z9.string().optional()
1847
- });
1848
- var GetTaskAuditsRequestSchema = z9.object({
1849
- projectId: z9.string(),
1850
- limit: z9.number().int().positive().max(200).optional().default(50)
1851
- });
1852
- var GetTaskAuditRequestSchema = z9.object({
1853
- projectId: z9.string(),
1854
- auditId: z9.string()
1855
- });
1856
- var GetTaskAuditAggregatesRequestSchema = z9.object({
1857
- projectId: z9.string()
1858
- });
1859
- var DeleteTaskAuditRequestSchema = z9.object({
1860
- projectId: z9.string(),
1861
- auditId: z9.string(),
1862
- requestingUserId: z9.string().optional()
1863
- });
1864
- var MarkInitialPromptSubmittedRequestSchema = z9.object({
1865
- sessionId: z9.string()
1866
- });
1867
- var CRITICAL_AUTOMATED_SOURCES = /* @__PURE__ */ new Set([
1868
- "ci_failure",
1869
- "review_trigger",
1870
- "merge_conflict",
1871
- "merge_failed",
1872
- "pull_branch",
1873
- // Child-task events for pack parents: the orchestrator must act (merge the
1874
- // child's PR, start unblocked siblings, finish the pack) even after it
1875
- // reported completed for a prior turn. Only ever sent to parent tasks.
1876
- "parent"
1877
- ]);
1878
- var PACK_EXECUTIONS = ["single-pod", "fan-out"];
1879
- var DEFAULT_PACK_EXECUTION = "single-pod";
1880
- function parsePackExecution(value) {
1881
- return PACK_EXECUTIONS.includes(value ?? "") ? value : DEFAULT_PACK_EXECUTION;
1882
- }
1883
- var MEETING_CHECKLIST_TITLE_MAX = 300;
1884
- var MEETING_TRANSCRIPT_MAX_CHARS = 2e6;
1885
- var MEETING_TITLE_MAX = 200;
1886
- var MEETING_OCCURRED_AT_MIN_YEAR = 2e3;
1887
- var MEETING_OCCURRED_AT_MAX_FUTURE_MS = 48 * 60 * 60 * 1e3;
1888
- var OCCURRED_AT_RANGE_MESSAGE = `occurredAt must be a real date: no earlier than ${MEETING_OCCURRED_AT_MIN_YEAR}, and no more than 48 hours in the future.`;
1889
- var MeetingOccurredAtSchema = z10.string().datetime().refine((value) => {
1890
- const ms = Date.parse(value);
1891
- if (Number.isNaN(ms)) return false;
1892
- if (ms > Date.now() + MEETING_OCCURRED_AT_MAX_FUTURE_MS) return false;
1893
- return new Date(ms).getUTCFullYear() >= MEETING_OCCURRED_AT_MIN_YEAR;
1894
- }, OCCURRED_AT_RANGE_MESSAGE);
1895
- var CreateMeetingFromTranscriptRequestSchema = z10.object({
1896
- projectId: z10.string().cuid(),
1897
- rawText: z10.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
1898
- title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
1899
- /** ISO 8601. Defaults to now when the source carries no date. */
1900
- occurredAt: MeetingOccurredAtSchema.optional(),
1901
- /** Override auto-detection. Rarely needed; detection handles the three formats. */
1902
- format: z10.enum(["text", "vtt", "srt"]).optional(),
1903
- source: z10.enum(["manual", "slack"]).optional()
1904
- });
1905
- var GetMeetingRequestSchema = z10.object({
1906
- projectId: z10.string().cuid(),
1907
- meetingId: z10.string().cuid()
1908
- });
1909
- var UpdateMeetingRequestSchema = z10.object({
1910
- projectId: z10.string().cuid(),
1911
- meetingId: z10.string().cuid(),
1912
- title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
1913
- occurredAt: MeetingOccurredAtSchema.optional()
1914
- });
1915
- var RegenerateMeetingSummaryRequestSchema = z10.object({
1916
- projectId: z10.string().cuid(),
1917
- meetingId: z10.string().cuid()
1918
- });
1919
- var DeleteMeetingRequestSchema = z10.object({
1920
- projectId: z10.string().cuid(),
1921
- meetingId: z10.string().cuid()
1922
- });
1923
- var checklistTitle = z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX);
1924
- var ListMeetingChecklistRequestSchema = z10.object({
1925
- projectId: z10.string().cuid(),
1926
- meetingId: z10.string().cuid()
1927
- });
1928
- var AddMeetingChecklistItemsRequestSchema = z10.object({
1929
- projectId: z10.string().cuid(),
1930
- meetingId: z10.string().cuid(),
1931
- items: z10.array(z10.object({ title: checklistTitle })).min(1).max(50)
1932
- });
1933
- var UpdateMeetingChecklistItemRequestSchema = z10.object({
1934
- projectId: z10.string().cuid(),
1935
- meetingId: z10.string().cuid(),
1936
- itemId: z10.string().cuid(),
1937
- title: checklistTitle.optional(),
1938
- ordinal: z10.number().int().min(0).optional(),
1939
- /** Explicit null clears the link; undefined leaves it alone. */
1940
- linkedTaskId: z10.string().cuid().nullable().optional()
1941
- }).refine(
1942
- (v) => v.title !== void 0 || v.ordinal !== void 0 || v.linkedTaskId !== void 0,
1943
- "Pass at least one of title, ordinal, or linkedTaskId."
1944
- );
1945
- var DeleteMeetingChecklistItemRequestSchema = z10.object({
1946
- projectId: z10.string().cuid(),
1947
- meetingId: z10.string().cuid(),
1948
- itemId: z10.string().cuid()
1949
- });
1950
- var SetMeetingChecklistItemCheckedRequestSchema = z10.object({
1951
- projectId: z10.string().cuid(),
1952
- meetingId: z10.string().cuid(),
1953
- itemId: z10.string().cuid(),
1954
- checked: z10.boolean(),
1955
- /** Attach the card in the same call that ticks the item. */
1956
- linkedTaskId: z10.string().cuid().nullable().optional()
1957
- });
1958
- var ListMeetingsRequestSchema = z10.object({
1959
- projectId: z10.string().cuid(),
1960
- limit: z10.number().int().min(1).max(50).optional(),
1961
- search: z10.string().max(200).optional()
1962
- });
1963
- var ReadMeetingTranscriptRequestSchema = z10.object({
1964
- projectId: z10.string().cuid(),
1965
- meetingId: z10.string().cuid(),
1966
- offset: z10.number().int().min(0).optional(),
1967
- limit: z10.number().int().min(1).max(500).optional()
1968
- });
1969
- var MEETING_SUMMARY_MAX_CHARS = 5e4;
1970
- var AddProjectMeetingChecklistItemsRequestSchema = z10.object({
1971
- projectId: z10.string().cuid(),
1972
- meetingId: z10.string().cuid(),
1973
- items: z10.array(z10.object({ title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX) })).min(1).max(50),
1974
- requestingUserId: z10.string().optional()
1975
- });
1976
- var CheckProjectMeetingChecklistItemRequestSchema = z10.object({
1977
- projectId: z10.string().cuid(),
1978
- meetingId: z10.string().cuid(),
1979
- title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1980
- checked: z10.boolean(),
1981
- /** Card id or slug. Resolved server-side and required to be in the project. */
1982
- linkedTask: z10.string().min(1).optional(),
1983
- requestingUserId: z10.string().optional()
1984
- });
1985
- var EditProjectMeetingChecklistItemRequestSchema = z10.object({
1986
- projectId: z10.string().cuid(),
1987
- meetingId: z10.string().cuid(),
1988
- title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1989
- newTitle: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1990
- requestingUserId: z10.string().optional()
1991
- });
1992
- var RemoveProjectMeetingChecklistItemRequestSchema = z10.object({
1993
- projectId: z10.string().cuid(),
1994
- meetingId: z10.string().cuid(),
1995
- title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1996
- requestingUserId: z10.string().optional()
1997
- });
1998
- var CreateProjectMeetingRequestSchema = z10.object({
1999
- projectId: z10.string().cuid(),
2000
- rawText: z10.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
2001
- title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
2002
- occurredAt: MeetingOccurredAtSchema.optional(),
2003
- requestingUserId: z10.string().optional()
2004
- });
2005
- var UpdateProjectMeetingRequestSchema = z10.object({
2006
- projectId: z10.string().cuid(),
2007
- meetingId: z10.string().cuid(),
2008
- title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
2009
- occurredAt: MeetingOccurredAtSchema.optional(),
2010
- summary: z10.string().min(1).max(MEETING_SUMMARY_MAX_CHARS).optional(),
2011
- requestingUserId: z10.string().optional()
2012
- }).refine(
2013
- (v) => v.title !== void 0 || v.occurredAt !== void 0 || v.summary !== void 0,
2014
- "Pass at least one of title, occurredAt, or summary."
2015
- );
2016
- var AGENT_STATUS_REASON_USER_QUESTION = "user_question";
2017
- var TASK_CHAT_HISTORY_LIMIT = 20;
2018
- var PM_CHAT_HISTORY_LIMIT = 40;
2019
- var AGENT_CHAT_HISTORY_FETCH_LIMIT = Math.max(TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT) + 10;
2020
- function formatModelId(provider, model) {
2021
- return `${provider}/${model}`;
2022
- }
2023
- function anthropicEntry(model, label, inputPerMillion, outputPerMillion, opts = {}) {
2024
- return {
2025
- provider: "anthropic",
2026
- model,
2027
- id: formatModelId("anthropic", model),
2028
- label,
2029
- format: "anthropic-messages",
2030
- inputPrice: inputPerMillion / 1e6,
2031
- outputPrice: outputPerMillion / 1e6,
2032
- supportsTools: true,
2033
- supportsEffort: opts.supportsEffort ?? true,
2034
- ...opts.experimental ? { experimental: true } : {}
2035
- };
2036
- }
2037
- var ANTHROPIC_CATALOG = [
2038
- anthropicEntry(DEFAULT_OPUS_MODEL, "Opus 5 Latest", 5, 25),
2039
- anthropicEntry(PREVIOUS_OPUS_MODEL, "Opus 4.8", 5, 25),
2040
- anthropicEntry(DEFAULT_SONNET_MODEL, "Sonnet 5 Latest", 3, 15),
2041
- anthropicEntry(PREVIOUS_SONNET_MODEL, "Sonnet 4.6", 3, 15),
2042
- // The Haiku line (4.5 and older) predates the tuning surface and 400s on it.
2043
- anthropicEntry(DEFAULT_HAIKU_MODEL, "Haiku 4.5", 1, 5, { supportsEffort: false }),
2044
- anthropicEntry(FABLE_MODEL, "Fable 5.1", 10, 50)
2045
- ];
2046
- var CODEX_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
2047
- var DEFAULT_CODEX_CODING_MODEL = "gpt-5.6-terra";
2048
- function isCodexReasoningEffort(value) {
2049
- return typeof value === "string" && CODEX_REASONING_EFFORTS.includes(value);
2050
- }
2051
- var HUMAN_PROSE_WRITING_STYLE = `## Writing style for humans
2052
- When you write prose a person will read \u2014 chat messages, plan updates, PR titles and bodies, PR review guides (the \`publish_review_guide\` overview and section explanations), review comments \u2014 follow these rules (based on ASD-STE100 Simplified Technical English):
2053
- - Use active voice. Say who does what ("The API rejects the request", not "the request is rejected").
2054
- - Use simple tenses ("we received", not "we have received").
2055
- - One instruction or fact per sentence. Keep sentences under ~20 words.
2056
- - Pick one word for one thing and reuse it. Do not rotate synonyms (check/verify/confirm) for the same action.
2057
- - Prefer the plain, common word ("use", not "utilize"; "start", not "initiate").
2058
- - Do not stack more than 3 nouns in a row ("task queue handler" is the limit).
2059
- - Use a numbered or bulleted list for 3+ steps or conditions instead of burying them in one sentence.
2060
- - Define a technical term on first use when a non-engineer will read the message.
2061
- - Never drop a condition, number, or scope qualifier to shorten a sentence. Precision beats brevity.
2062
- These rules do NOT apply to code, code comments, commit messages, or quoted output.`;
2063
- var CLAUDESPACE_WORKLOAD_LABEL = "rc-workload";
2064
- var CLAUDESPACE_WORKLOAD_VALUE = "claudespace";
2065
- var CONVEYOR_POD_SELECTOR = `${CLAUDESPACE_WORKLOAD_LABEL}=${CLAUDESPACE_WORKLOAD_VALUE}`;
2066
- var MS_PER_DAY = 24 * 60 * 60 * 1e3;
2067
- var MENTION_TOKEN_REGEX = /@\[(\w+):([^\]]+)\]/g;
2068
- function parseMentions(content) {
2069
- const tokens = [];
2070
- for (const match of content.matchAll(MENTION_TOKEN_REGEX)) {
2071
- tokens.push({ type: match[1], id: match[2] });
2072
- }
2073
- return tokens;
2074
- }
2075
- var POSTGRES_ENV = {
2076
- POSTGRES_HOST_AUTH_METHOD: "trust",
2077
- POSTGRES_DB: "conveyor"
2078
- };
2079
- var FIREBASE_EMULATOR_COMMAND = [
2080
- "sh",
2081
- "-c",
2082
- `set -e; mkdir -p /home/node && cd /home/node && cat > firebase.json <<'EOF'
2083
- {"emulators":{"auth":{"host":"0.0.0.0","port":9099},"hub":{"host":"0.0.0.0","port":4400},"ui":{"enabled":false}}}
2084
- EOF
2085
- exec firebase emulators:start --only=auth --project=rally-cry-dev`
2086
- ];
2087
- var FIREBASE_EMULATOR_ENV = {
2088
- METADATA_SERVER_DETECTION: "none",
2089
- GOOGLE_APPLICATION_CREDENTIALS: "/dev/null"
2090
- };
2091
- var CATALOG = {
2092
- postgresql: {
2093
- name: "postgresql",
2094
- image: "postgres:16-alpine",
2095
- command: [
2096
- "sh",
2097
- "-c",
2098
- // Start postgres, wait for readiness, then create the test database.
2099
- // Durability flags: service state is ephemeral by design (overlayfs, no
2100
- // PVC; a crash re-seeds from pod-data), so commits must never wait on a
2101
- // WAL flush. fsync=off + synchronous_commit=off + full_page_writes=off
2102
- // remove all blocking storage I/O — without them, per-commit flushes on
2103
- // the node's network boot disk dominated int-test wall time (~100ms/commit).
2104
- "if [ ! -s /var/lib/postgresql/data/PG_VERSION ] && [ -d /var/lib/postgresql/pod-data ]; then cp -a /var/lib/postgresql/pod-data/. /var/lib/postgresql/data/; fi; chown -R postgres:postgres /var/lib/postgresql/data; docker-entrypoint.sh postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off & PID=$!; for i in $(seq 1 30); do pg_isready -U postgres && break; sleep 1; done; createdb -U postgres conveyor_test 2>/dev/null || true; wait $PID"
2105
- ],
2106
- ports: [5432],
2107
- livenessProbe: {
2108
- exec: ["pg_isready", "-U", "postgres"],
2109
- periodSeconds: 30,
2110
- failureThreshold: 3,
2111
- timeoutSeconds: 5,
2112
- initialDelaySeconds: 60
2113
- },
2114
- env: { ...POSTGRES_ENV },
2115
- resources: {
2116
- // postgres runs from the baked seed at /var/lib/postgresql/pod-data via
2117
- // overlayfs CoW (no emptyDir — see k8s-pod-spec.ts), so WAL + catalog +
2118
- // re-seed churn charges the container's ephemeral-storage. It must exceed
2119
- // the ~1Gi ceiling where pods evicted, but is bounded by TWO Autopilot rules:
2120
- // 1) limit == request — Autopilot caps the limit DOWN to the request at
2121
- // admission, so headroom must live on the REQUEST, not just the limit;
2122
- // 2) the SUM of all container ephemeral requests in a pod must be ≤ 10Gi
2123
- // (an emptyDir doesn't escape this: its usage still evicts against the
2124
- // container limit, and raising the limit re-hits rule 1 → the cap).
2125
- // The agent is trimmed to 4Gi (resource-tiers.ts) to free room: with
2126
- // agent 4 + gcsfuse 1 + lgtm 1 + es/redis/firebase 0.75, postgres gets 3Gi
2127
- // and the heaviest (URC) pod sits at 9.75Gi + 10Mi for GCS Fuse metadata
2128
- // prefetch — under the 10Gi cap — while giving postgres 3x the ~1Gi
2129
- // ceiling that evicted. Keep request == limit;
2130
- // see the per-pod ephemeral budget guard test.
2131
- // CPU: the request is the CFS floor; the old 50m starved postgres to 5ms
2132
- // of CPU per 100ms period — every query burst hit throttle stalls, which
2133
- // showed up as ~100ms floors on trivial statements and dominated the API
2134
- // int suite even after the fsync flags above. 500m is paid for out of the
2135
- // agent's derived share (resource-tiers.ts). The limit bursts to 2 for
2136
- // spiky work — the first-boot pod-data seed copy and int-suite query
2137
- // storms — borrowing idle node CPU without moving the request.
2138
- requests: { cpuMillicores: 500, memoryMi: 512, ephemeralMi: 3 * 1024 },
2139
- limits: { cpuMillicores: 2e3, memoryMi: 512, ephemeralMi: 3 * 1024 }
2140
- },
2141
- connectionEnv: {
2142
- DATABASE_URL: "postgresql://postgres@postgresql:5432/conveyor",
2143
- TEST_DATABASE_URL: "postgresql://postgres@postgresql:5432/conveyor_test"
2144
- },
2145
- statefulBake: true,
2146
- bake: {
2147
- // Start postgres, wait for readiness, then create the test database so
2148
- // it's baked into the committed image. No pod-data restore (nothing is
2149
- // seeded yet) and no durability flags (the bake's writes must land).
2150
- // NOTE: docker-compose interpolates `$VAR`/`$(...)`, so every `$` that
2151
- // must reach the shell is doubled.
2152
- command: [
2153
- "sh",
2154
- "-c",
2155
- "docker-entrypoint.sh postgres & PID=$$!; for i in $$(seq 1 30); do pg_isready -U postgres && break; sleep 1; done; createdb -U postgres conveyor_test 2>/dev/null || true; wait $$PID"
2156
- ],
2157
- environment: { ...POSTGRES_ENV },
2158
- healthcheck: {
2159
- test: ["CMD-SHELL", "pg_isready -U postgres"],
2160
- intervalSec: 2,
2161
- timeoutSec: 5,
2162
- retries: 15
2163
- }
2164
- }
2165
- },
2166
- redis: {
2167
- name: "redis",
2168
- image: "redis:7-alpine",
2169
- command: ["redis-server", "--appendonly", "yes", "--dir", "/data"],
2170
- ports: [6379],
2171
- env: {},
2172
- resources: {
2173
- requests: { cpuMillicores: 25, memoryMi: 32, ephemeralMi: 256 },
2174
- limits: { cpuMillicores: 50, memoryMi: 64, ephemeralMi: 256 }
2175
- },
2176
- connectionEnv: {
2177
- REDIS_URL: "redis://redis:6379",
2178
- AUTH_REDIS_URL: "redis://redis:6379"
2179
- },
2180
- statefulBake: false,
2181
- // Redis holds no baked state, so the bake runs the stock image CMD.
2182
- bake: {},
2183
- mirror: { src: "redis:7-alpine", dest: "mirror-redis:7-alpine" }
2184
- },
2185
- elasticsearch: {
2186
- name: "elasticsearch",
2187
- // 9.4.0 matches universal-rally-cry's local compose + its v9 ES client —
2188
- // the v9 client sends Accept: compatible-with=9, which an 8.x server
2189
- // rejects (media_type_header_exception), breaking search/audit indexing
2190
- // in pods. 512m heap matches the project compose sizing.
2191
- image: "docker.elastic.co/elasticsearch/elasticsearch:9.4.0",
2192
- mirror: {
2193
- src: "docker.elastic.co/elasticsearch/elasticsearch:9.4.0",
2194
- dest: "mirror-elasticsearch:9.4.0"
2195
- },
2196
- ports: [9200],
2197
- // Baked service images are `docker commit`s of a recently-running ES, so
2198
- // they carry a stale data-dir node.lock; ES 9 hard-fails on it at boot
2199
- // ("Underlying file changed by an external force" → AlreadyClosedException).
2200
- // Clear it before handing off to the stock entrypoint.
2201
- command: [
2202
- "sh",
2203
- "-c",
2204
- // ALL Lucene lock files, not just node.lock — the baked image also
2205
- // carries per-index write.lock + snapshot_cache/write.lock, and ES 9
2206
- // fail-fasts on any of them ("changed by an external force").
2207
- "find /usr/share/elasticsearch/data -name '*.lock' -type f -delete 2>/dev/null; exec /usr/local/bin/docker-entrypoint.sh eswrapper"
2208
- ],
2209
- env: {
2210
- "discovery.type": "single-node",
2211
- "xpack.security.enabled": "false",
2212
- "xpack.ml.enabled": "false",
2213
- "xpack.watcher.enabled": "false",
2214
- "xpack.profiling.enabled": "false",
2215
- "ingest.geoip.downloader.enabled": "false",
2216
- ES_JAVA_OPTS: "-Xms512m -Xmx512m"
2217
- },
2218
- resources: {
2219
- // CPU limit 4x the request: ES cold-start is a CPU-bound JVM boot
2220
- // (class loading + JIT + recovery of the docker-commit'ed data dir),
2221
- // and the 500m hard cap put it at ~135s to yellow — past the sidecar
2222
- // wait script's original 90s budget. Bursting to 2 cut it to ~40s on
2223
- // the real cluster (A/B on identical nodes, 2 rounds). The burst only
2224
- // borrows idle node CPU at boot; under contention CFS still floors ES
2225
- // at its 500m request.
2226
- requests: { cpuMillicores: 500, memoryMi: 1024, ephemeralMi: 256 },
2227
- limits: { cpuMillicores: 2e3, memoryMi: 1024, ephemeralMi: 256 }
2228
- },
2229
- connectionEnv: {
2230
- ELASTICSEARCH_URL: "http://elasticsearch:9200"
2231
- },
2232
- statefulBake: true,
2233
- bake: {
2234
- // No lock-clearing command at bake time: the bake starts from the stock
2235
- // image, which has no committed data dir to unlock yet.
2236
- environment: {
2237
- "discovery.type": "single-node",
2238
- "xpack.security.enabled": "false",
2239
- ES_JAVA_OPTS: "-Xms512m -Xmx512m"
2240
- }
2241
- }
2242
- },
2243
- // All-in-one image bundling Grafana + Loki + Tempo + Mimir + an OTEL Collector.
2244
- // Pinned tag (not :latest) so the image-builder content hash stays
2245
- // deterministic across upstream releases — see image-builder.ts content-hash
2246
- // dedup keyed on `deps.sorted()`.
2247
- lgtm: {
2248
- name: "lgtm",
2249
- image: "grafana/otel-lgtm:0.11.6",
2250
- // The otel-lgtm image's own CMD is ["/otel-lgtm/run-all.sh"] (WORKDIR
2251
- // /otel-lgtm). Declaring it explicitly makes lgtm a command-based service so
2252
- // the lazy start-file gate wraps it like the others — otherwise it launches
2253
- // via image CMD at boot and can't be parked (which is why warm-booting it at
2254
- // a minimal request OOMKilled it). getSidecarSpecs strips this command for a
2255
- // baked lgtm image, so it only applies to the stock image whose launcher is
2256
- // exactly this path.
2257
- command: ["/otel-lgtm/run-all.sh"],
2258
- ports: [
2259
- // OTLP gRPC + HTTP — agents and user code emit telemetry here.
2260
- 4317,
2261
- 4318,
2262
- // Grafana UI — reachable through the existing preview proxy on
2263
- // https://3000-{sessionId}.preview.<PREVIEW_DOMAIN>/.
2264
- 3e3
2265
- ],
2266
- env: {
2267
- // The pod is per-task; the preview proxy authenticates the session
2268
- // upstream, so anonymous in-pod admin is acceptable here.
2269
- GF_AUTH_ANONYMOUS_ENABLED: "true",
2270
- GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin",
2271
- GF_SECURITY_ALLOW_EMBEDDING: "true",
2272
- ENABLE_LOGS_GRAFANA: "true",
2273
- ENABLE_LOGS_OTELCOL: "true"
2274
- },
2275
- resources: {
2276
- // Autopilot caps the ephemeral-storage limit to the request (see the
2277
- // postgresql note), so the 1Gi headroom must be on the request too.
2278
- requests: { cpuMillicores: 250, memoryMi: 1024, ephemeralMi: 1024 },
2279
- limits: { cpuMillicores: 1e3, memoryMi: 2048, ephemeralMi: 1024 }
2280
- },
2281
- statefulBake: false,
2282
- // No `bake` block: a BAKE produces traces nobody reads, so starting the
2283
- // (heavy) collector image would only add a pull + boot to every build.
2284
- // lgtm still runs for live pods, and is never committed regardless.
2285
- mirror: { src: "grafana/otel-lgtm:0.11.6", dest: "mirror-otel-lgtm:0.11.6" }
2286
- },
2287
- "firebase-auth-emulator": {
2288
- name: "firebase-auth-emulator",
2289
- image: "andreysenov/firebase-tools:latest",
2290
- command: FIREBASE_EMULATOR_COMMAND,
2291
- ports: [9099, 4400],
2292
- env: {
2293
- // The emulator container inherits the pod's Workload Identity, so
2294
- // firebase-tools finds GCP credentials and its `emulators:start` does an
2295
- // online "auto auth" + project validation that stalls ~47s on the pod's
2296
- // locked-down egress (the dominant service boot cost — measured live).
2297
- // The emulator needs NO real credentials, so cut off credential discovery
2298
- // for this container only: google-auth-library skips metadata detection
2299
- // and finds no key file, firebase-tools logs "not authenticated" and
2300
- // starts the emulator immediately. The agent container keeps its WI.
2301
- ...FIREBASE_EMULATOR_ENV
2302
- },
2303
- resources: {
2304
- requests: { cpuMillicores: 100, memoryMi: 256, ephemeralMi: 256 },
2305
- limits: { cpuMillicores: 500, memoryMi: 512, ephemeralMi: 256 }
2306
- },
2307
- connectionEnv: {
2308
- // In docker-compose, services reach each other by service name — the
2309
- // runtime pod equivalents use `localhost` because co-located services
2310
- // share the pod's network namespace.
2311
- FIREBASE_AUTH_EMULATOR_HOST: "firebase-auth-emulator:9099",
2312
- NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_HOST: "firebase-auth-emulator:9099"
2313
- },
2314
- statefulBake: false,
2315
- mirror: {
2316
- src: "andreysenov/firebase-tools:latest",
2317
- dest: "mirror-firebase-tools:latest"
2318
- },
2319
- bake: {
2320
- // The emulator writes its config at startup, so the bake needs the same
2321
- // inline command the runtime uses.
2322
- command: FIREBASE_EMULATOR_COMMAND,
2323
- environment: FIREBASE_EMULATOR_ENV,
2324
- healthcheck: {
2325
- // Probe with node, the one runtime this image guarantees. The image
2326
- // (`andreysenov/firebase-tools`) is a slim Node image that ships
2327
- // NEITHER `wget` NOR `curl`, so the `wget -qO- …` probe this replaced
2328
- // exited 127 on every attempt: a healthy emulator burned all 20 retries
2329
- // and every bake reported it unhealthy. See the catalog invariant in
2330
- // `service-definitions.test.ts`, which also executes this probe.
2331
- test: [
2332
- "CMD-SHELL",
2333
- `node -e "fetch('http://localhost:9099/').then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))"`
2334
- ],
2335
- intervalSec: 3,
2336
- timeoutSec: 5,
2337
- retries: 20
2338
- }
2339
- }
2340
- }
2341
- };
2342
- var SERVICE_DEFINITIONS = CATALOG;
2343
- var SUPPORTED_CLAUDESPACE_DEPS = Object.keys(CATALOG);
2344
- var STATEFUL_BAKE_DEPS = new Set(
2345
- SUPPORTED_CLAUDESPACE_DEPS.filter((dep) => SERVICE_DEFINITIONS[dep].statefulBake)
2346
- );
2347
- var MIRRORABLE_SIDECARS = Object.fromEntries(
2348
- SUPPORTED_CLAUDESPACE_DEPS.flatMap((dep) => {
2349
- const mirror = SERVICE_DEFINITIONS[dep].mirror;
2350
- return mirror ? [[dep, mirror]] : [];
2351
- })
2352
- );
2353
- var LEVELS_PER_BAND = 100;
2354
- var PRESTIGE_TIERS = ACHIEVEMENT_RARITIES.map((rarity, index) => ({
2355
- prestige: index + 1,
2356
- name: rarity.name,
2357
- color: rarity.color,
2358
- iconPath: rarity.iconPath,
2359
- minLevel: (index + 1) * LEVELS_PER_BAND
2360
- }));
2361
- var TOP_PRESTIGE_BAND = PRESTIGE_TIERS.length;
2362
- var PRE_BUILD_TASK_STATUSES = /* @__PURE__ */ new Set(["Planning", "Open"]);
2363
- function hasTaskPlan(plan) {
2364
- return !!plan?.trim();
2365
- }
2366
- var CARD_TYPE_SURFACE = {
2367
- task: "board",
2368
- chat: "board",
2369
- incident: "report",
2370
- suggestion: "report"
2371
- };
2372
- var surfaceTypes = (surface) => Object.keys(CARD_TYPE_SURFACE).filter(
2373
- (type) => CARD_TYPE_SURFACE[type] === surface
2374
- );
2375
- var BOARD_CARD_TYPES = surfaceTypes("board");
2376
- var REPORT_CARD_TYPES = surfaceTypes("report");
73
+ git,
74
+ hasTaskPlan,
75
+ hasUncommittedChanges,
76
+ hasUnpushedCommits,
77
+ isCodexReasoningEffort,
78
+ isPlaceholderLocator,
79
+ locatorMatchesContent,
80
+ parseMentions,
81
+ pushToOrigin,
82
+ readWorkspaceBytes,
83
+ readWorkspaceDir,
84
+ readWorkspaceFile,
85
+ recordForceFreshFailure,
86
+ remoteMatchesLocalHead,
87
+ restoreWipSnapshot,
88
+ runQueryGcpLogs,
89
+ runQueryGrafanaLogs,
90
+ stageAndCommit,
91
+ statWorkspacePath,
92
+ updateRemoteToken,
93
+ verifyGitCredential
94
+ } from "./chunk-OMD77OIJ.js";
95
+ import {
96
+ registerBootMilestoneSocketFallback,
97
+ reportBootMilestone
98
+ } from "./chunk-Q4FQOJ7D.js";
99
+ import {
100
+ describeTokenFile,
101
+ ghHostsExternallyOwned,
102
+ githubTokenFilePath,
103
+ sleep
104
+ } from "./chunk-W4LZ7R6Z.js";
105
+ import {
106
+ isHeavyGateActive,
107
+ listAbandonedGateReceipts,
108
+ listGateExitSentinels,
109
+ refreshGenericGateStatus
110
+ } from "./chunk-372R6E4C.js";
111
+ import {
112
+ LoopLagMonitor,
113
+ loopStatusForRunnerStatus
114
+ } from "./chunk-IA45XHOA.js";
115
+ import {
116
+ getWorkbenchClient
117
+ } from "./chunk-SQM2BQ7H.js";
118
+ import {
119
+ workbenchEnabled
120
+ } from "./chunk-KMB3BU4S.js";
2377
121
 
2378
122
  // src/connection/auth-errors.ts
2379
123
  function isPermissionDeniedError(err) {
@@ -2608,7 +352,7 @@ function defineTool(name, description, schema, handler, options) {
2608
352
 
2609
353
  // src/harness/claude-code/index.ts
2610
354
  import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
2611
- import { z as z11 } from "zod";
355
+ import { z } from "zod";
2612
356
  var ClaudeCodeHarness = class {
2613
357
  /** The SDK stream is itself the structured-event source. */
2614
358
  emitsStructuredEvents = true;
@@ -2641,7 +385,7 @@ var ClaudeCodeHarness = class {
2641
385
  }
2642
386
  );
2643
387
  if (t.strict) {
2644
- sdkTool.inputSchema = z11.strictObject(t.schema);
388
+ sdkTool.inputSchema = z.strictObject(t.schema);
2645
389
  }
2646
390
  return sdkTool;
2647
391
  });
@@ -3038,7 +782,7 @@ function redact(input) {
3038
782
  import { open } from "fs/promises";
3039
783
 
3040
784
  // src/harness/pty/record-mapper.ts
3041
- function isRecord2(value) {
785
+ function isRecord(value) {
3042
786
  return typeof value === "object" && value !== null;
3043
787
  }
3044
788
  function isUnknownArray(value) {
@@ -3084,7 +828,7 @@ function mapSystem(record) {
3084
828
  }
3085
829
  function mapUsage(message) {
3086
830
  const usage = message.usage;
3087
- if (!isRecord2(usage)) return void 0;
831
+ if (!isRecord(usage)) return void 0;
3088
832
  const result = {};
3089
833
  const input = numberField(usage, "input_tokens");
3090
834
  const cacheRead = numberField(usage, "cache_read_input_tokens");
@@ -3095,7 +839,7 @@ function mapUsage(message) {
3095
839
  return result;
3096
840
  }
3097
841
  function mapContentBlock(raw) {
3098
- if (!isRecord2(raw)) return null;
842
+ if (!isRecord(raw)) return null;
3099
843
  const type = stringField(raw, "type");
3100
844
  if (type === void 0) return null;
3101
845
  const block = { type };
@@ -3110,7 +854,7 @@ function mapContentBlock(raw) {
3110
854
  }
3111
855
  function mapAssistant(record) {
3112
856
  const message = record.message;
3113
- if (!isRecord2(message)) return null;
857
+ if (!isRecord(message)) return null;
3114
858
  const rawContent = isUnknownArray(message.content) ? message.content : [];
3115
859
  const content = [];
3116
860
  for (const item of rawContent) {
@@ -3134,7 +878,7 @@ function mapResultSuccess(record) {
3134
878
  total_cost_usd: numberField(record, "total_cost_usd", "totalCostUsd") ?? 0
3135
879
  };
3136
880
  const modelUsage = record.modelUsage;
3137
- if (isRecord2(modelUsage)) event.modelUsage = modelUsage;
881
+ if (isRecord(modelUsage)) event.modelUsage = modelUsage;
3138
882
  const sessionId = stringField(record, "session_id", "sessionId");
3139
883
  if (sessionId !== void 0) event.sessionId = sessionId;
3140
884
  return event;
@@ -3153,7 +897,7 @@ function mapResult(record) {
3153
897
  return null;
3154
898
  }
3155
899
  function mapTranscriptRecord(raw) {
3156
- if (!isRecord2(raw)) return null;
900
+ if (!isRecord(raw)) return null;
3157
901
  switch (raw.type) {
3158
902
  case "system":
3159
903
  return mapSystem(raw);
@@ -3670,7 +1414,7 @@ var TEXT_MAX = 16e3;
3670
1414
  var TOOL_INPUT_MAX = 1900;
3671
1415
  var TOOL_OUTPUT_MAX = 1900;
3672
1416
  var QUESTION_INPUT_MAX = 16e3;
3673
- function isRecord3(value) {
1417
+ function isRecord2(value) {
3674
1418
  return typeof value === "object" && value !== null;
3675
1419
  }
3676
1420
  function isUnknownArray2(value) {
@@ -3701,7 +1445,7 @@ function compactQuestionsJson(questions) {
3701
1445
  return serialize(withDescriptions(0)).slice(0, QUESTION_INPUT_MAX);
3702
1446
  }
3703
1447
  function compactToolInput(name, input) {
3704
- if (name === "AskUserQuestion" && isRecord3(input)) {
1448
+ if (name === "AskUserQuestion" && isRecord2(input)) {
3705
1449
  const questions = parseUserQuestions(input);
3706
1450
  if (questions.length > 0) return compactQuestionsJson(questions);
3707
1451
  }
@@ -3714,15 +1458,15 @@ function isNonConversationText(text) {
3714
1458
  }
3715
1459
  function userRecordText(record) {
3716
1460
  const message = record.message;
3717
- if (!isRecord3(message)) return void 0;
1461
+ if (!isRecord2(message)) return void 0;
3718
1462
  const content = message.content;
3719
1463
  if (typeof content === "string") return content;
3720
1464
  if (!isUnknownArray2(content)) return void 0;
3721
- if (content.some((b) => isRecord3(b) && b.type === "tool_result")) return void 0;
3722
- return content.filter((b) => isRecord3(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
1465
+ if (content.some((b) => isRecord2(b) && b.type === "tool_result")) return void 0;
1466
+ return content.filter((b) => isRecord2(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
3723
1467
  }
3724
1468
  function isBackgroundTaskNotificationRecord(raw) {
3725
- if (!isRecord3(raw) || raw.type !== "user") return false;
1469
+ if (!isRecord2(raw) || raw.type !== "user") return false;
3726
1470
  if (raw.isSidechain === true || raw.isMeta === true) return false;
3727
1471
  const text = userRecordText(raw);
3728
1472
  return text !== void 0 && text.trimStart().startsWith(TASK_NOTIFICATION_PREFIX);
@@ -3742,11 +1486,11 @@ function mapSystem2(record) {
3742
1486
  }
3743
1487
  function mapAssistant2(record) {
3744
1488
  const message = record.message;
3745
- if (!isRecord3(message)) return [];
1489
+ if (!isRecord2(message)) return [];
3746
1490
  const content = isUnknownArray2(message.content) ? message.content : [];
3747
1491
  const events = [];
3748
1492
  for (const raw of content) {
3749
- if (!isRecord3(raw)) continue;
1493
+ if (!isRecord2(raw)) continue;
3750
1494
  if (raw.type === "text") {
3751
1495
  const text = stringField2(raw, "text");
3752
1496
  if (text && text.length > 0) {
@@ -3773,14 +1517,14 @@ function toolResultText(block) {
3773
1517
  const content = block.content;
3774
1518
  if (typeof content === "string") return content;
3775
1519
  if (isUnknownArray2(content)) {
3776
- return content.filter((b) => isRecord3(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
1520
+ return content.filter((b) => isRecord2(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
3777
1521
  }
3778
1522
  return "";
3779
1523
  }
3780
1524
  function mapToolResults(content) {
3781
1525
  const events = [];
3782
1526
  for (const raw of content) {
3783
- if (!isRecord3(raw) || raw.type !== "tool_result") continue;
1527
+ if (!isRecord2(raw) || raw.type !== "tool_result") continue;
3784
1528
  const event = {
3785
1529
  kind: "tool_result",
3786
1530
  output: truncate2(toolResultText(raw), TOOL_OUTPUT_MAX),
@@ -3794,9 +1538,9 @@ function mapToolResults(content) {
3794
1538
  }
3795
1539
  function mapUser(record) {
3796
1540
  const message = record.message;
3797
- if (!isRecord3(message)) return [];
1541
+ if (!isRecord2(message)) return [];
3798
1542
  const content = message.content;
3799
- if (isUnknownArray2(content) && content.some((b) => isRecord3(b) && b.type === "tool_result")) {
1543
+ if (isUnknownArray2(content) && content.some((b) => isRecord2(b) && b.type === "tool_result")) {
3800
1544
  return mapToolResults(content);
3801
1545
  }
3802
1546
  const text = userRecordText(record);
@@ -3806,7 +1550,7 @@ function mapUser(record) {
3806
1550
  return [{ kind: "user_text", text: truncate2(trimmed, TEXT_MAX) }];
3807
1551
  }
3808
1552
  function mapChatRecords(raw) {
3809
- if (!isRecord3(raw)) return [];
1553
+ if (!isRecord2(raw)) return [];
3810
1554
  if (raw.isSidechain === true || raw.isMeta === true) return [];
3811
1555
  switch (raw.type) {
3812
1556
  case "system":
@@ -4134,7 +1878,7 @@ var PtyOutputCoalescer = class {
4134
1878
 
4135
1879
  // src/harness/pty/tool-server.ts
4136
1880
  import { createServer as createServer2 } from "http";
4137
- import { z as z12 } from "zod";
1881
+ import { z as z2 } from "zod";
4138
1882
  import { writeFile as writeFile4 } from "fs/promises";
4139
1883
  import { join as join4 } from "path";
4140
1884
  import { randomBytes } from "crypto";
@@ -4191,7 +1935,7 @@ var PtyToolServer = class {
4191
1935
  const mcp = new McpServer({ name: this.name, version: "1.0.0" });
4192
1936
  const register = mcp.registerTool.bind(mcp);
4193
1937
  for (const tool2 of this.tools) {
4194
- const inputSchema = tool2.strict ? z12.strictObject(tool2.schema) : tool2.schema;
1938
+ const inputSchema = tool2.strict ? z2.strictObject(tool2.schema) : tool2.schema;
4195
1939
  register(
4196
1940
  tool2.name,
4197
1941
  {
@@ -7524,383 +5268,122 @@ var DirectStreamController = class {
7524
5268
  const off = this.inner.onResize((cols, rows) => {
7525
5269
  this.relayDims = { cols, rows };
7526
5270
  this.applyDims();
7527
- });
7528
- return () => {
7529
- if (this.resizeHandler === handler) this.resizeHandler = null;
7530
- off();
7531
- };
7532
- }
7533
- async dispose() {
7534
- if (this.disposed) return;
7535
- this.disposed = true;
7536
- this.relayCoalescer.dispose();
7537
- this.reporter.reportPtyStream(null);
7538
- const current = this.server;
7539
- this.server = null;
7540
- if (current) await current.close();
7541
- }
7542
- };
7543
- function wrapBridgeWithDirectStream(inner, reporter, options = {}) {
7544
- const controller = new DirectStreamController(inner, reporter, options);
7545
- return {
7546
- bridge: {
7547
- ...inner,
7548
- sendOutput: (data, dims) => controller.sendOutput(data, dims),
7549
- sendEnded: () => controller.sendEnded(),
7550
- onInput: (handler) => controller.onInput(handler),
7551
- onResize: (handler) => controller.onResize(handler)
7552
- },
7553
- dispose: () => controller.dispose()
7554
- };
7555
- }
7556
-
7557
- // src/execution/query-executor.ts
7558
- import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
7559
- import { existsSync, readFileSync as readFileSync3, renameSync as renameSync2, truncateSync } from "fs";
7560
-
7561
- // src/execution/chat-instructions.ts
7562
- function buildChatInstructions(context, scenario, newMessages) {
7563
- const userMessages = newMessages.filter((m) => m.role === "user");
7564
- const parts = [];
7565
- if (scenario === "fresh") {
7566
- parts.push(
7567
- `The user's message is above. Do what it asks and reply with post_to_chat \u2014 your turn output is NOT shown in chat, so post_to_chat is the only way the user sees your answer.`
7568
- );
7569
- } else if (userMessages.length > 0) {
7570
- parts.push(
7571
- `You have new messages on this card.`,
7572
- `
7573
- New messages since your last run:`,
7574
- ...userMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`),
7575
- `
7576
- Respond to them with post_to_chat (your turn output is NOT shown in chat).`
7577
- );
7578
- } else {
7579
- parts.push(
7580
- `You were relaunched but no new messages have arrived since your last run.`,
7581
- `Post a brief status update with post_to_chat if you still owe the team a response, then wait for them.`
7582
- );
7583
- }
7584
- if (context.githubPRUrl) {
7585
- parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
7586
- }
7587
- return parts;
7588
- }
7589
-
7590
- // src/execution/relaunch-hold.ts
7591
- function findLastAgentMessageIndex(history) {
7592
- for (let i = history.length - 1; i >= 0; i--) {
7593
- if (history[i].role === "assistant") return i;
7594
- }
7595
- return -1;
7596
- }
7597
- function messagesAfterCursor(history, lastSeenMessageId) {
7598
- if (!lastSeenMessageId) return history;
7599
- const idx = history.findIndex((m) => m.id === lastSeenMessageId);
7600
- return idx === -1 ? history : history.slice(idx + 1);
7601
- }
7602
- function relaunchMessageBatch(context) {
7603
- if (context.lastSeenMessageId) {
7604
- return messagesAfterCursor(context.chatHistory, context.lastSeenMessageId);
7605
- }
7606
- return context.chatHistory.slice(findLastAgentMessageIndex(context.chatHistory) + 1);
7607
- }
7608
- function isCodeReviewRun(mode, agentMode) {
7609
- return mode === "code-review" || mode !== "pm" && agentMode === "review";
7610
- }
7611
- function isActionableRelaunchMessage(m) {
7612
- if (m.source && CRITICAL_AUTOMATED_SOURCES.has(m.source)) return true;
7613
- return m.role === "user" && m.source !== EXTERNAL_AGENT_MESSAGE_SOURCE;
7614
- }
7615
- function isReviewHostHold(context, newMessages, isCodeReview) {
7616
- if (isCodeReview) return false;
7617
- if (context.status !== "ReviewPR" || !context.githubPRUrl) return false;
7618
- return !newMessages.some((m) => isActionableRelaunchMessage(m));
7619
- }
7620
- function buildReviewHostHoldParts(context, newMessages) {
7621
- const parts = [
7622
- `You were relaunched while this task is in review, but nothing here asks you to change the code.`,
7623
- `Most likely this workspace was woken to HOST the code review session, not to work.`,
7624
- `Do NOT take a work turn: do not re-review your own diff, re-run gates, commit, or push. The PR is mid-review and moving the branch now would pull it out from under the reviewer.`,
7625
- `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`
7626
- ];
7627
- if (newMessages.length > 0) {
7628
- parts.push(
7629
- `
7630
- New messages since your last run:`,
7631
- ...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`)
7632
- );
7633
- }
7634
- parts.push(
7635
- `
7636
- Review these messages and wait for the team to provide instructions before taking action.`,
7637
- `If the review requests changes, or CI fails, you will be woken again with that feedback \u2014 act then, not now.`
7638
- );
7639
- if (context.githubPRUrl) {
7640
- parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
7641
- }
7642
- return parts;
7643
- }
7644
-
7645
- // src/execution/pack-runner-prompt.ts
7646
- function findLastAgentMessageIndex2(history) {
7647
- for (let i = history.length - 1; i >= 0; i--) {
7648
- if (history[i].role === "assistant") return i;
7649
- }
7650
- return -1;
7651
- }
7652
- function formatProjectAgents(agents) {
7653
- const parts = [``, `## Project Agents`];
7654
- for (const agent of agents) {
7655
- const role = agent.role ? `role: ${agent.role}` : "role: unassigned";
7656
- const sp = agent.storyPoints === null || agent.storyPoints === void 0 ? "" : `, story points: ${agent.storyPoints}`;
7657
- parts.push(`- ${agent.name} (${role}${sp})`);
7658
- }
7659
- return parts;
7660
- }
7661
- function formatStoryPoints(storyPoints) {
7662
- const parts = [``, `## Story Point Tiers`];
7663
- for (const sp of storyPoints) {
7664
- const desc = sp.description ? ` \u2014 ${sp.description}` : "";
7665
- parts.push(`- Value ${sp.value}: "${sp.name}"${desc}`);
7666
- }
7667
- return parts;
7668
- }
7669
- var PACK_RUNNER_FOOTER = [
7670
- ``,
7671
- `Your turn output appears ONLY in the live agent terminal \u2014 it is NOT posted to the task chat. The team does NOT see your replies unless you post them.`,
7672
- `Use post_to_chat (omit task_id \u2192 this task's chat) to report which children you fired, status as you orchestrate, and any blocker or escalation the team needs to act on.`,
7673
- `Pass task_id only to message a DIFFERENT task's chat (e.g. a child task).`,
7674
- `Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`,
7675
- ``,
7676
- `If a Conveyor tool call fails or reports the MCP server is unavailable/disconnected, this is almost always a transient socket reconnect \u2014 RETRY the same call; it will succeed once the connection re-establishes. A tool error is NOT a reason to go idle or stop the loop. (Going idle is only correct when you are genuinely waiting on child-task status changes or CI \u2014 see the loop above.)`,
7677
- ``,
7678
- HUMAN_PROSE_WRITING_STYLE
7679
- ];
7680
- function formatBaseSync(packBranch, devBranch) {
7681
- return [
7682
- ``,
7683
- `## Pack Branch Base Sync`,
7684
- `This is a feature-branch pack: children branch from \`${packBranch}\` and PR back into it, and the whole pack lands on \`${devBranch}\` in one final PR at the end.`,
7685
- `YOU are the reviewer of record for child PRs \u2014 the automated code reviewer skips PRs that target \`${packBranch}\` (the full automated review runs on the pack's final PR into \`${devBranch}\`). Review each child diff properly before merging; a merged child advances to ReviewDev automatically.`,
7686
- `Before each child launch the server merges \`${devBranch}\` into \`${packBranch}\` for you, so children start from a fresh base. You do not need to do this yourself.`,
7687
- `When start_child_cloud_build reports a base-sync conflict, that merge could not be applied automatically. Fix it before firing more children:`,
7688
- `1. \`git fetch origin ${devBranch} && git checkout ${packBranch} && git pull origin ${packBranch}\``,
7689
- `2. \`git merge origin/${devBranch}\`, resolve the conflicts, commit, and push to \`${packBranch}\`.`,
7690
- `3. Resume the loop.`,
7691
- `Resolving this merge is git coordination, not code-writing \u2014 it does NOT violate the "Do NOT attempt to write code yourself" rule below. Keep the resolution to the conflict markers; if a conflict needs real code decisions, escalate to the team instead of authoring the fix.`,
7692
- `The launch itself is never blocked by a conflict \u2014 the child was already started from the un-synced branch, so sync promptly.`
7693
- ];
7694
- }
7695
- function resolveMergedWorkBranch(context) {
7696
- return context.featureBranch && context.githubBranch ? context.githubBranch : context.baseBranch;
7697
- }
7698
- function resolveSinglePodPackBranch(context) {
7699
- return context.githubBranch || context.baseBranch;
7700
- }
7701
- function buildPackRunnerSystemPrompt(context, config, setupLog) {
7702
- const mergedWorkBranch = resolveMergedWorkBranch(context);
7703
- const parts = [
7704
- `You are an autonomous Pack Runner managing child tasks for the "${context.title}" project.`,
7705
- `You are running locally with full access to the repository and task management tools.`,
7706
- `Your job is to execute child tasks by firing cloud builds, reviewing their PRs, and merging them \u2014 respecting dependency chains for parallel execution.`,
7707
- ``,
7708
- `## Child Task Status Lifecycle`,
7709
- `- "Planning" \u2014 Not ready for execution. If its plan is solid, promote it yourself: update_subtask with status "Open" (plus storyPointValue and agentIdOrName if unset \u2014 setting story points does NOT auto-promote). Otherwise skip it (or escalate if blocking).`,
7710
- `- "Open" \u2014 Ready to execute (if dependencies are met). Use start_child_cloud_build to fire it.`,
7711
- `- "InProgress" \u2014 Currently being worked on by a Task Runner. Wait \u2014 it will move to ReviewPR when done.`,
7712
- `- "ReviewPR" \u2014 Task Runner finished and opened a PR. Review and merge it.`,
7713
- `- "ReviewDev" \u2014 PR was merged (to dev, or into this pack's feature branch for feature-branch packs). This child is complete. Move on.`,
7714
- `- "Complete" \u2014 Fully done. Move on.`,
7715
- ``,
7716
- `## Autonomous Loop`,
7717
- `Follow this loop each time you are launched or relaunched:`,
7718
- ``,
7719
- `1. Call list_subtasks to see the current state of all child tasks.`,
7720
- ` The response includes PR info, agent assignment, **dependency info** (dependsOn array + allDependenciesMet flag), and the **packSlots** build-slot picture.`,
7721
- ` If list_subtasks returns NO children, this is a fresh parent card: break the work down now \u2014 explore the codebase, save a parent-level plan with update_task, then create child tasks with create_subtask, each with a detailed plan (file:line citations, verification steps) and dependsOn set for any child that blocks on another. Then fire the ready children and continue the loop.`,
7722
- ``,
7723
- `2. Evaluate children by status and dependency readiness:`,
7724
- ` - "ReviewPR": Review and merge its PR with approve_and_merge_pr. (Highest priority)`,
7725
- ` - If merge fails due to pending CI: post a status update to chat, state you are going idle.`,
7726
- ` - If merge fails due to failed CI: use get_execution_logs(childTaskId) to check. Escalate to team.`,
7727
- ` - "InProgress": A Task Runner is actively working. Do nothing \u2014 wait.`,
7728
- ` - "Open" + allDependenciesMet=true: Ready to fire. Use start_child_cloud_build.`,
7729
- ` - "Open" + allDependenciesMet=false: Blocked \u2014 skip for now. Will be unblocked when deps complete.`,
7730
- ` - "ReviewDev" / "Complete": Already done. Skip.`,
7731
- ` - "Planning": Not ready. Promote it with update_subtask (status "Open" + story points + agent) once its plan is solid; if it genuinely isn't plannable, notify team.`,
7732
- ``,
7733
- `3. Fire ALL ready "Open" tasks whose dependencies are met, not just one \u2014 independent tasks run in parallel. There is a concurrency limit: if start_child_cloud_build returns a PACK_CHILD_LIMIT error, that is backpressure, not a failure. Check list_subtasks' packSlots to see which children hold the in-flight slots \u2014 merge or wait on them (or stop_child_build a stale holder that isn't actually running), then start more as slots free up.`,
7734
- ` A successful start_child_cloud_build moves that child to "InProgress" \u2014 that status IS your confirmation the fire landed. Never re-fire a child that already reads "InProgress".`,
7735
- ``,
7736
- `4. After merging a PR: run \`git pull origin ${mergedWorkBranch}\` then re-check list_subtasks \u2014 previously blocked tasks may now be ready.`,
7737
- ``,
7738
- `5. After firing all ready tasks: report which tasks you fired to chat, then state you are going idle.`,
7739
- ``,
7740
- `6. When ALL children are in "ReviewDev" or "Complete" (no "Open", "InProgress", or "ReviewPR" remaining): do a final review, summarize results in chat, and mark this parent task complete with force_update_task_status("Complete").`,
7741
- ``,
7742
- `## Important Rules`,
7743
- `- When dependencies are set on children, use them to determine execution order. Fire all ready tasks in parallel (up to the PACK_CHILD_LIMIT backpressure \u2014 see the loop above).`,
7744
- `- Dependencies are explicit card metadata (set at creation via create_subtask's dependsOn, or rewired with update_subtask). Prefer them over reading order out of plan text. When NO dependencies are set on any children, fall back to ordinal order (one at a time) \u2014 this is legacy behavior; set dependsOn when a child truly blocks on another.`,
7745
- `- After firing builds OR when waiting on CI, explicitly state you are going idle. Go idle when waiting \u2014 the system wakes you (or relaunches this environment) when a child changes status.`,
7746
- `- Do NOT attempt to write code yourself. Your role is coordination only.`,
7747
- `- If a child is stuck in "InProgress" for an unusually long time, use get_execution_logs(childTaskId) to check its logs and escalate to the team if it appears stuck.`,
7748
- `- stop_child_build is a signal, not an immediate teardown: it tells the child's agent to stop, but the build slot stays held until that environment actually tears down. A slot still held right after a stop is normal, NOT a wedge \u2014 re-check list_subtasks' packSlots on a later turn instead of re-firing, stopping again, or escalating.`,
7749
- `- You can use get_task(childTaskId) to get a child's full details including PR URL and branch.`,
7750
- `- list_subtasks (compact view, the default) returns per child: status, agent assignment (agentId), story points, PR number/state, dependency info, and holdsBuildSlot \u2014 plus a packSlots summary of in-flight environments vs the cap. Use this to verify readiness before firing builds; pass verbose:true only if you need full plan/description text.`,
7751
- `- You can use read_task_chat to check for team messages.`
7752
- ];
7753
- if (context.featureBranch && context.githubBranch) {
7754
- parts.push(...formatBaseSync(context.githubBranch, context.baseBranch));
7755
- }
7756
- if (context.storyPoints && context.storyPoints.length > 0) {
7757
- parts.push(...formatStoryPoints(context.storyPoints));
7758
- }
7759
- if (context.agents && context.agents.length > 0) {
7760
- parts.push(...formatProjectAgents(context.agents));
7761
- }
7762
- if (setupLog.length > 0) {
7763
- parts.push(``, `## Environment setup log`, "```", ...setupLog, "```");
7764
- }
7765
- if (context.agentInstructions) {
7766
- parts.push(``, `## Agent Instructions`, context.agentInstructions);
7767
- }
7768
- if (config.instructions) {
7769
- parts.push(``, `## Additional Instructions`, config.instructions);
7770
- }
7771
- parts.push(...PACK_RUNNER_FOOTER);
7772
- return parts.join("\n");
7773
- }
7774
- function buildSinglePodPackPrompt(context, config, setupLog) {
7775
- const packBranch = resolveSinglePodPackBranch(context);
7776
- const parts = [
7777
- `You are an autonomous Pack Runner for the "${context.title}" pack, and you implement the work yourself.`,
7778
- ``,
7779
- `Run the \`/conveyor-build\` skill and follow its PACK PATH. It is the source of truth for the loop \u2014 child selection, dependency order, the reviewer-of-record pass, the finale \u2014 and it carries the pod substitution this session runs under. Do not re-derive the workflow from memory.`,
7780
- ``,
7781
- // Session-bound facts only. Everything the skill CAN know now lives there;
7782
- // what stays is what is true of THIS pod and this card.
7783
- `## This session`,
7784
- `- You are ALREADY bound to this parent card. There is nothing to claim.`,
7785
- `- One checkout, one branch: \`${packBranch}\`. Every child's work is commits on it.`,
7786
- `- Children get NO branches and NO pull requests here. \`create_pull_request\` always opens the PR for the card you are bound to, so calling it mid-pack opens the PARENT's PR early and strands the remaining children. One PR, at the very end, for this parent.`,
7787
- `- You move each child's status yourself: \`update_task(task_id: <child>, status: ...)\`. No per-child build fires, so a status you do not write is a board that silently lies. A child is "ReviewDev" once its commits are on \`${packBranch}\`.`,
7788
- `- \`start_child_cloud_build\` and \`stop_child_build\` belong to the fan-out model and are not available to you \u2014 you are the implementer.`,
7789
- `- Merge \`origin/${context.baseBranch}\` into \`${packBranch}\` after each child, never rebase: the branch is shared with WIP refs and rewriting it breaks them.`,
7790
- `- Nothing will wake you to start the next child. Go idle only when genuinely blocked on something external.`,
7791
- ``
7792
- ];
7793
- if (context.storyPoints && context.storyPoints.length > 0) {
7794
- parts.push(...formatStoryPoints(context.storyPoints));
7795
- }
7796
- if (context.agents && context.agents.length > 0) {
7797
- parts.push(...formatProjectAgents(context.agents));
7798
- }
7799
- if (setupLog.length > 0) {
7800
- parts.push(``, `## Environment setup log`, "```", ...setupLog, "```");
7801
- }
7802
- if (context.agentInstructions) {
7803
- parts.push(``, `## Agent Instructions`, context.agentInstructions);
5271
+ });
5272
+ return () => {
5273
+ if (this.resizeHandler === handler) this.resizeHandler = null;
5274
+ off();
5275
+ };
7804
5276
  }
7805
- if (config.instructions) {
7806
- parts.push(``, `## Additional Instructions`, config.instructions);
5277
+ async dispose() {
5278
+ if (this.disposed) return;
5279
+ this.disposed = true;
5280
+ this.relayCoalescer.dispose();
5281
+ this.reporter.reportPtyStream(null);
5282
+ const current = this.server;
5283
+ this.server = null;
5284
+ if (current) await current.close();
7807
5285
  }
7808
- parts.push(...PACK_RUNNER_FOOTER);
7809
- return parts.join("\n");
5286
+ };
5287
+ function wrapBridgeWithDirectStream(inner, reporter, options = {}) {
5288
+ const controller = new DirectStreamController(inner, reporter, options);
5289
+ return {
5290
+ bridge: {
5291
+ ...inner,
5292
+ sendOutput: (data, dims) => controller.sendOutput(data, dims),
5293
+ sendEnded: () => controller.sendEnded(),
5294
+ onInput: (handler) => controller.onInput(handler),
5295
+ onResize: (handler) => controller.onResize(handler)
5296
+ },
5297
+ dispose: () => controller.dispose()
5298
+ };
7810
5299
  }
7811
- function buildSinglePodPackInstructions(context, scenario) {
7812
- const parts = [`
7813
- ## Instructions`];
5300
+
5301
+ // src/execution/query-executor.ts
5302
+ import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
5303
+ import { existsSync, readFileSync as readFileSync3, renameSync as renameSync2, truncateSync } from "fs";
5304
+
5305
+ // src/execution/chat-instructions.ts
5306
+ function buildChatInstructions(context, scenario, newMessages) {
5307
+ const userMessages = newMessages.filter((m) => m.role === "user");
5308
+ const parts = [];
7814
5309
  if (scenario === "fresh") {
7815
5310
  parts.push(
7816
- `You are the Pack Runner for this task and its subtasks, and you implement them yourself.`,
7817
- `Start now: call list_subtasks to see where the pack stands.`,
7818
- `No children yet? Break the work down first \u2014 save a parent-level plan with update_task, then create_subtask each child with a detailed plan and dependsOn set where one blocks another.`,
7819
- `Otherwise take the first "Open" child whose dependencies are met, claim it with update_task(task_id, status: "InProgress"), and implement it on this branch.`
7820
- );
7821
- } else if (scenario === "idle_relaunch") {
7822
- parts.push(
7823
- `You have been relaunched. Re-derive where the pack stands \u2014 call list_subtasks and check the branch, never rely on what you remember.`,
7824
- `A child left "InProgress" is one YOU were implementing: check the working tree and the log for what already landed before redoing any of it.`,
7825
- `Otherwise continue the loop: next ready child, or the finale if none remain.`
5311
+ `The user's message is above. Do what it asks and reply with post_to_chat \u2014 your turn output is NOT shown in chat, so post_to_chat is the only way the user sees your answer.`
7826
5312
  );
7827
- } else {
7828
- const lastAgentIdx = findLastAgentMessageIndex2(context.chatHistory);
7829
- const newMessages = context.chatHistory.slice(lastAgentIdx + 1).filter((m) => m.role === "user");
5313
+ } else if (userMessages.length > 0) {
7830
5314
  parts.push(
7831
- `You have been relaunched with new messages.`,
5315
+ `You have new messages on this card.`,
7832
5316
  `
7833
5317
  New messages since your last run:`,
7834
- ...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`),
5318
+ ...userMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`),
7835
5319
  `
7836
- After addressing the feedback, resume: call list_subtasks and continue with the next ready child.`
7837
- );
7838
- }
7839
- return parts;
7840
- }
7841
- function buildPackRunnerInstructions(context, scenario) {
7842
- const parts = [`
7843
- ## Instructions`];
7844
- if (scenario === "fresh") {
7845
- parts.push(
7846
- `You are the Pack Runner for this task and its subtasks.`,
7847
- `Begin your autonomous loop immediately: call list_subtasks to assess the current state.`,
7848
- `If there are no children yet, create them first \u2014 save a parent-level plan with update_task, then create child tasks with detailed plans and dependsOn set where one blocks on another. Then fire the ready ones.`,
7849
- `If any child is in "ReviewPR" status, review and merge its PR first.`,
7850
- `Then fire the next "Open" child task.`
7851
- );
7852
- } else if (scenario === "idle_relaunch") {
7853
- parts.push(
7854
- `You have been relaunched \u2014 a child task likely changed status.`,
7855
- `Call list_subtasks to check the current state of all children.`,
7856
- `Look for children in "ReviewPR" status first \u2014 review and merge their PRs.`,
7857
- `Check if any previously blocked tasks now have allDependenciesMet=true \u2014 fire them.`,
7858
- `If a child you previously fired is now in "ReviewDev", pull latest with \`git pull origin ${resolveMergedWorkBranch(context)}\`.`,
7859
- `If no children need action, state you are going idle.`
5320
+ Respond to them with post_to_chat (your turn output is NOT shown in chat).`
7860
5321
  );
7861
5322
  } else {
7862
- const lastAgentIdx = findLastAgentMessageIndex2(context.chatHistory);
7863
- const newMessages = context.chatHistory.slice(lastAgentIdx + 1).filter((m) => m.role === "user");
7864
5323
  parts.push(
7865
- `You have been relaunched with new messages.`,
7866
- `
7867
- New messages since your last run:`,
7868
- ...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`),
7869
- `
7870
- After addressing the feedback, resume your autonomous loop: call list_subtasks and proceed accordingly.`
5324
+ `You were relaunched but no new messages have arrived since your last run.`,
5325
+ `Post a brief status update with post_to_chat if you still owe the team a response, then wait for them.`
7871
5326
  );
7872
5327
  }
5328
+ if (context.githubPRUrl) {
5329
+ parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
5330
+ }
7873
5331
  return parts;
7874
5332
  }
7875
5333
 
7876
- // src/execution/plan-revision-notice.ts
7877
- var MINUTE_MS = 60 * 1e3;
7878
- var HOUR_MS = 60 * MINUTE_MS;
7879
- var DAY_MS = 24 * HOUR_MS;
7880
- function describeAge(revisedAt, now) {
7881
- const at = Date.parse(revisedAt);
7882
- if (Number.isNaN(at)) return "";
7883
- const ago = now - at;
7884
- if (ago < MINUTE_MS) return "just now";
7885
- if (ago < HOUR_MS) return `${Math.round(ago / MINUTE_MS)} minutes ago`;
7886
- if (ago < DAY_MS) return `${Math.round(ago / HOUR_MS)} hours ago`;
7887
- return `${Math.round(ago / DAY_MS)} days ago`;
5334
+ // src/execution/relaunch-hold.ts
5335
+ function findLastAgentMessageIndex(history) {
5336
+ for (let i = history.length - 1; i >= 0; i--) {
5337
+ if (history[i].role === "assistant") return i;
5338
+ }
5339
+ return -1;
7888
5340
  }
7889
- function buildPlanRevisionNotice(context, now = Date.now()) {
7890
- const revisedAt = context.planRevisedAt;
7891
- if (!revisedAt) return [];
7892
- const age = describeAge(revisedAt, now);
7893
- const when = age ? `${age} (${revisedAt})` : revisedAt;
7894
- return [
7895
- `
7896
- ## \u26A0\uFE0F The plan changed since this build was dispatched`,
7897
- `Someone revised this card's plan ${when}. It was NOT you \u2014 an agent is never told about its own plan edit.`,
7898
- `Before your next Write or Edit:`,
7899
- `1. Call \`get_current_plan\` and read the current plan in full.`,
7900
- `2. Compare it against the plan you were launched with. Treat the current plan as the truth.`,
7901
- `3. Drop or redo any work the revision supersedes. Do NOT keep building the old plan.`,
7902
- `If the revision invalidates work you already committed, say so with post_to_chat before you continue.`
5341
+ function messagesAfterCursor(history, lastSeenMessageId) {
5342
+ if (!lastSeenMessageId) return history;
5343
+ const idx = history.findIndex((m) => m.id === lastSeenMessageId);
5344
+ return idx === -1 ? history : history.slice(idx + 1);
5345
+ }
5346
+ function relaunchMessageBatch(context) {
5347
+ if (context.lastSeenMessageId) {
5348
+ return messagesAfterCursor(context.chatHistory, context.lastSeenMessageId);
5349
+ }
5350
+ return context.chatHistory.slice(findLastAgentMessageIndex(context.chatHistory) + 1);
5351
+ }
5352
+ function isCodeReviewRun(mode, agentMode) {
5353
+ return mode === "code-review" || mode !== "pm" && agentMode === "review";
5354
+ }
5355
+ function isActionableRelaunchMessage(m) {
5356
+ if (m.source && CRITICAL_AUTOMATED_SOURCES.has(m.source)) return true;
5357
+ return m.role === "user" && m.source !== EXTERNAL_AGENT_MESSAGE_SOURCE;
5358
+ }
5359
+ function isReviewHostHold(context, newMessages, isCodeReview) {
5360
+ if (isCodeReview) return false;
5361
+ if (context.status !== "ReviewPR" || !context.githubPRUrl) return false;
5362
+ return !newMessages.some((m) => isActionableRelaunchMessage(m));
5363
+ }
5364
+ function buildReviewHostHoldParts(context, newMessages) {
5365
+ const parts = [
5366
+ `You were relaunched while this task is in review, but nothing here asks you to change the code.`,
5367
+ `Most likely this workspace was woken to HOST the code review session, not to work.`,
5368
+ `Do NOT take a work turn: do not re-review your own diff, re-run gates, commit, or push. The PR is mid-review and moving the branch now would pull it out from under the reviewer.`,
5369
+ `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`
7903
5370
  ];
5371
+ if (newMessages.length > 0) {
5372
+ parts.push(
5373
+ `
5374
+ New messages since your last run:`,
5375
+ ...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`)
5376
+ );
5377
+ }
5378
+ parts.push(
5379
+ `
5380
+ Review these messages and wait for the team to provide instructions before taking action.`,
5381
+ `If the review requests changes, or CI fails, you will be woken again with that feedback \u2014 act then, not now.`
5382
+ );
5383
+ if (context.githubPRUrl) {
5384
+ parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
5385
+ }
5386
+ return parts;
7904
5387
  }
7905
5388
 
7906
5389
  // src/execution/prompt-formatters.ts
@@ -8177,11 +5660,6 @@ function buildAutoPrompt(context, runnerMode) {
8177
5660
  `You are in Auto mode \u2014 plan this card, then build it, without stopping for approval.`,
8178
5661
  `Run \`/conveyor-plan\` FIRST to produce the plan and save it to the card, then run \`/conveyor-build\` to implement it. Both are sources of truth; do not re-derive them.`,
8179
5662
  ...buildSkillInvocation("/conveyor-build"),
8180
- ``,
8181
- `### What "auto" changes`,
8182
- `- There is no plan-approval step and no read-only phase. The plan is a RECORD for the team, not a gate \u2014 save it, then immediately write the code. A card whose only output is a plan (or a plan-only PR) has not been built.`,
8183
- `- Decide independently. Escalate only when genuinely blocked: ambiguous requirements, missing access, conflicting instructions. Everything else is yours to call.`,
8184
- `- Skip \`/conveyor-plan\` only if the card already carries a plan you are not materially diverging from.`,
8185
5663
  ...buildHarnessContracts(context)
8186
5664
  ];
8187
5665
  if (context) parts.push(...buildPropertyInstructions(context, runnerMode));
@@ -8215,81 +5693,175 @@ function buildModePrompt(agentMode, context, runnerMode) {
8215
5693
  return null;
8216
5694
  }
8217
5695
  }
8218
- function buildChatPrompt(context) {
8219
- const branch = context?.githubBranch?.trim();
8220
- const gitScope = branch ? ` on branch \`${branch}\`` : "";
8221
- return [
8222
- `
8223
- ## Mode: Interactive`,
8224
- `This is a direct Claude Code session on this card. The user drives it message by message; you have the same access you would have running Claude Code locally in this repo (full read/write, shell, git${gitScope}).`,
8225
- `- Your turn output is NOT shown in chat \u2014 reply with \`post_to_chat\`.`,
8226
- `- There is no required Conveyor workflow here. Do what the message asks: answer, investigate, write files, build, or open a PR. Skills such as \`/conveyor-plan\` and \`/conveyor-build\` are available if the user asks for them or they clearly help; nothing runs them for you.`,
8227
- ``,
8228
- `### Card facts`,
8229
- `- This card starts Unidentified and stays so until a plan is saved with \`update_task\`; that first plan identifies it and moves it to In Progress.`,
8230
- `- Files the user should keep go on the card via \`upload_attachment\` (any file type, up to 25MB), not an off-platform link.`,
8231
- `- \`create_pull_request\` opens the PR for this card and moves it to Review PR. Save a short plan first so the card is identified \u2014 a record, not an approval gate.`,
8232
- `- When the user says they are done, call \`force_update_task_status\` with status \`"Complete"\`. Not if a PR is open \u2014 the review flow takes it from there.`
8233
- ].join("\n");
8234
- }
8235
- function buildReviewTagSection(context) {
8236
- const assignedIds = new Set(context?.taskTagIds ?? []);
8237
- const assigned = (context?.projectTags ?? []).filter((t) => assignedIds.has(t.id));
8238
- if (assigned.length === 0) return [];
5696
+ function buildChatPrompt(context) {
5697
+ const branch = context?.githubBranch?.trim();
5698
+ const gitScope = branch ? ` on branch \`${branch}\`` : "";
5699
+ return [
5700
+ `
5701
+ ## Mode: Interactive`,
5702
+ `This is a direct Claude Code session on this card. The user drives it message by message; you have the same access you would have running Claude Code locally in this repo (full read/write, shell, git${gitScope}).`,
5703
+ `- Your turn output is NOT shown in chat \u2014 reply with \`post_to_chat\`.`,
5704
+ `- There is no required Conveyor workflow here. Do what the message asks: answer, investigate, write files, build, or open a PR. Skills such as \`/conveyor-plan\` and \`/conveyor-build\` are available if the user asks for them or they clearly help; nothing runs them for you.`,
5705
+ ``,
5706
+ `### Card facts`,
5707
+ `- This card starts Unidentified and stays so until a plan is saved with \`update_task\`; that first plan identifies it and moves it to In Progress.`,
5708
+ `- Files the user should keep go on the card via \`upload_attachment\` (any file type, up to 25MB), not an off-platform link.`,
5709
+ `- \`create_pull_request\` opens the PR for this card and moves it to Review PR. Save a short plan first so the card is identified \u2014 a record, not an approval gate.`,
5710
+ `- When the user says they are done, call \`force_update_task_status\` with status \`"Complete"\`. Not if a PR is open \u2014 the review flow takes it from there.`
5711
+ ].join("\n");
5712
+ }
5713
+ function buildReviewTagSection(context) {
5714
+ const assignedIds = new Set(context?.taskTagIds ?? []);
5715
+ const assigned = (context?.projectTags ?? []).filter((t) => assignedIds.has(t.id));
5716
+ if (assigned.length === 0) return [];
5717
+ const parts = [
5718
+ `### Card Tags & Domain Context`,
5719
+ `This card carries these project glossary tags:`
5720
+ ];
5721
+ for (const tag of assigned) parts.push(...formatTagWithContextPaths(tag));
5722
+ parts.push(
5723
+ ``,
5724
+ `A tag's rules and overview define the conventions the code under review must follow. Use them when you judge Pattern Consistency:`,
5725
+ `- Read the tag entries that match the files in this diff. The "Reference Guides" section of the task brief lists the same tags with a one-line summary of each linked doc.`,
5726
+ `- Call get_tag("<name>") for a term's full spec (overview, linked files, hierarchy) when the linked docs are not enough.`,
5727
+ `- A change that contradicts a tagged system's documented conventions is a review finding, even when it reads fine on its own.`,
5728
+ `- Skip the tags this diff does not touch \u2014 do not read them all up front.`,
5729
+ `- If the diff changes how a tagged system behaves and the tag's overview is now wrong, say so in your review.`,
5730
+ ``
5731
+ );
5732
+ return parts;
5733
+ }
5734
+ function buildReviewPrompt(context) {
5735
+ return [
5736
+ `
5737
+ ## Mode: Review`,
5738
+ ...buildSkillInvocation("/conveyor-review"),
5739
+ ``,
5740
+ // Everything below is harness-bound: the skill cannot know this session's
5741
+ // resolved branch, and the verdict tool names differ per surface (the skill
5742
+ // documents both pairs; this names the one that exists here).
5743
+ `### This session`,
5744
+ `- The diff under review: \`${baseDiffCommand(context?.baseBranch)}\``,
5745
+ `- Your verdict tools on this pod are \`approve_code_review\` and \`request_code_changes\`. The local conveyor-mcp pair does not exist here.`,
5746
+ ...buildEnforcedToolContracts(),
5747
+ ...buildReviewTagSection(context)
5748
+ ].join("\n");
5749
+ }
5750
+
5751
+ // src/execution/pack-runner-prompt.ts
5752
+ function formatProjectAgents(agents) {
5753
+ const parts = [``, `## Project Agents`];
5754
+ for (const agent of agents) {
5755
+ const role = agent.role ? `role: ${agent.role}` : "role: unassigned";
5756
+ const sp = agent.storyPoints === null || agent.storyPoints === void 0 ? "" : `, story points: ${agent.storyPoints}`;
5757
+ parts.push(`- ${agent.name} (${role}${sp})`);
5758
+ }
5759
+ return parts;
5760
+ }
5761
+ function formatStoryPoints(storyPoints) {
5762
+ const parts = [``, `## Story Point Tiers`];
5763
+ for (const sp of storyPoints) {
5764
+ const desc = sp.description ? ` \u2014 ${sp.description}` : "";
5765
+ parts.push(`- Value ${sp.value}: "${sp.name}"${desc}`);
5766
+ }
5767
+ return parts;
5768
+ }
5769
+ var PACK_RUNNER_FOOTER = [
5770
+ ``,
5771
+ `Your turn output appears ONLY in the live agent terminal \u2014 it is NOT posted to the task chat. The team does NOT see your replies unless you post them.`,
5772
+ `Use post_to_chat (omit task_id \u2192 this task's chat) to report which child you are on, status as you work the pack, and any blocker or escalation the team needs to act on.`,
5773
+ `Pass task_id only to message a DIFFERENT task's chat (e.g. a child task).`,
5774
+ `Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`,
5775
+ ``,
5776
+ `If a Conveyor tool call fails or reports the MCP server is unavailable/disconnected, this is almost always a transient socket reconnect \u2014 RETRY the same call; it will succeed once the connection re-establishes. A tool error is NOT a reason to go idle or stop the pack. (When a turn may end is the pack path's call \u2014 see its goal and finish line.)`,
5777
+ ``,
5778
+ HUMAN_PROSE_WRITING_STYLE
5779
+ ];
5780
+ function resolveSinglePodPackBranch(context) {
5781
+ return context.githubBranch || context.baseBranch;
5782
+ }
5783
+ function isPackRunnerSession(mode, isAuto, isParentTask) {
5784
+ return mode === "pack" || mode === "pm" && !!isAuto && !!isParentTask;
5785
+ }
5786
+ function buildSinglePodPackPrompt(context, setupLog) {
5787
+ const packBranch = resolveSinglePodPackBranch(context);
5788
+ const parts = [
5789
+ `You are an autonomous Pack Runner for the "${context.title}" pack, and you implement the work yourself.`,
5790
+ ``,
5791
+ `Run the \`/conveyor-build\` skill and follow its PACK PATH. It is the source of truth for the loop \u2014 child selection, dependency order, the reviewer-of-record pass, the finale \u2014 and it carries the pod substitution this session runs under. Do not re-derive the workflow from memory.`,
5792
+ ``,
5793
+ // Session-bound facts only. Everything the skill CAN know now lives there;
5794
+ // what stays is what is true of THIS pod and this card.
5795
+ `## This session`,
5796
+ `- You are ALREADY bound to this parent card. There is nothing to claim.`,
5797
+ `- One checkout, one branch: \`${packBranch}\`. Every child's work is commits on it.`,
5798
+ `- Children get NO branches and NO pull requests here. \`create_pull_request\` always opens the PR for the card you are bound to, so calling it mid-pack opens the PARENT's PR early and strands the remaining children. One PR, at the very end, for this parent.`,
5799
+ `- This pack's base branch is \`${context.baseBranch}\`: sync \`origin/${context.baseBranch}\` into \`${packBranch}\` after each child (merge, never rebase \u2014 the branch carries WIP refs), and the finale PR targets it; pass it explicitly when you open the PR.`,
5800
+ `- Child status writes carry the child's id: \`update_task(task_id: <child>, status: ...)\`. Without \`task_id\` the write targets this parent card.`,
5801
+ ``
5802
+ ];
5803
+ if (context.storyPoints && context.storyPoints.length > 0) {
5804
+ parts.push(...formatStoryPoints(context.storyPoints));
5805
+ }
5806
+ if (context.agents && context.agents.length > 0) {
5807
+ parts.push(...formatProjectAgents(context.agents));
5808
+ }
5809
+ if (setupLog.length > 0) {
5810
+ parts.push(``, `## Environment setup log`, "```", ...setupLog, "```");
5811
+ }
5812
+ if (context.agentInstructions) {
5813
+ parts.push(``, `## Agent Instructions`, context.agentInstructions);
5814
+ }
5815
+ parts.push(...PACK_RUNNER_FOOTER);
5816
+ return parts.join("\n");
5817
+ }
5818
+ function buildSinglePodPackInstructions(scenario) {
8239
5819
  const parts = [
8240
- `### Card Tags & Domain Context`,
8241
- `This card carries these project glossary tags:`
5820
+ `
5821
+ ## Instructions`,
5822
+ `You are the Pack Runner for this pack and you implement every child yourself, on this pod.`
8242
5823
  ];
8243
- for (const tag of assigned) parts.push(...formatTagWithContextPaths(tag));
8244
- parts.push(
8245
- ``,
8246
- `A tag's rules and overview define the conventions the code under review must follow. Use them when you judge Pattern Consistency:`,
8247
- `- Read the tag entries that match the files in this diff. The "Reference Guides" section of the task brief lists the same tags with a one-line summary of each linked doc.`,
8248
- `- Call get_tag("<name>") for a term's full spec (overview, linked files, hierarchy) when the linked docs are not enough.`,
8249
- `- A change that contradicts a tagged system's documented conventions is a review finding, even when it reads fine on its own.`,
8250
- `- Skip the tags this diff does not touch \u2014 do not read them all up front.`,
8251
- `- If the diff changes how a tagged system behaves and the tag's overview is now wrong, say so in your review.`,
8252
- ``
8253
- );
5824
+ if (scenario === "idle_relaunch") {
5825
+ parts.push(
5826
+ `You were relaunched with no new messages \u2014 re-derive pack state from the card, never from memory.`
5827
+ );
5828
+ } else if (scenario === "feedback_relaunch") {
5829
+ parts.push(
5830
+ `You have been relaunched with the feedback in New Messages above \u2014 address it, then continue the pack.`
5831
+ );
5832
+ }
5833
+ parts.push(...buildSkillInvocation("/conveyor-build"));
8254
5834
  return parts;
8255
5835
  }
8256
- function buildReviewPrompt(context) {
8257
- if (context?.isParentTask) return buildParentReviewPrompt();
8258
- return [
8259
- `
8260
- ## Mode: Review`,
8261
- ...buildSkillInvocation("/conveyor-review"),
8262
- ``,
8263
- // Everything below is harness-bound: the skill cannot know this session's
8264
- // resolved branch, and the verdict tool names differ per surface (the skill
8265
- // documents both pairs; this names the one that exists here).
8266
- `### This session`,
8267
- `- The diff under review: \`${baseDiffCommand(context?.baseBranch)}\``,
8268
- `- Your verdict tools on this pod are \`approve_code_review\` and \`request_code_changes\`. The local conveyor-mcp pair does not exist here.`,
8269
- ...buildEnforcedToolContracts(),
8270
- ...buildReviewTagSection(context)
8271
- ].join("\n");
5836
+
5837
+ // src/execution/plan-revision-notice.ts
5838
+ var MINUTE_MS = 60 * 1e3;
5839
+ var HOUR_MS = 60 * MINUTE_MS;
5840
+ var DAY_MS = 24 * HOUR_MS;
5841
+ function describeAge(revisedAt, now) {
5842
+ const at = Date.parse(revisedAt);
5843
+ if (Number.isNaN(at)) return "";
5844
+ const ago = now - at;
5845
+ if (ago < MINUTE_MS) return "just now";
5846
+ if (ago < HOUR_MS) return `${Math.round(ago / MINUTE_MS)} minutes ago`;
5847
+ if (ago < DAY_MS) return `${Math.round(ago / HOUR_MS)} hours ago`;
5848
+ return `${Math.round(ago / DAY_MS)} days ago`;
8272
5849
  }
8273
- function buildParentReviewPrompt() {
5850
+ function buildPlanRevisionNotice(context, now = Date.now()) {
5851
+ const revisedAt = context.planRevisedAt;
5852
+ if (!revisedAt) return [];
5853
+ const age = describeAge(revisedAt, now);
5854
+ const when = age ? `${age} (${revisedAt})` : revisedAt;
8274
5855
  return [
8275
5856
  `
8276
- ## Mode: Review`,
8277
- `### Parent Task Review`,
8278
- `You are reviewing and coordinating child tasks.`,
8279
- `- Use \`list_subtasks\` to see current child task state and progress.`,
8280
- `- For children in ReviewPR status: review their code quality and merge with \`approve_and_merge_pr\`.`,
8281
- `- For children with failing CI: check with \`get_execution_logs(childTaskId)\` and escalate if stuck.`,
8282
- `- Fire next child builds with \`start_child_cloud_build\` when ready.`,
8283
- `- Create follow-up tasks for issues discovered during review.`,
8284
- ``,
8285
- `### Coordination Workflow`,
8286
- `1. Check child task statuses with \`list_subtasks\``,
8287
- `2. Review completed children \u2014 check PRs, run tests if needed`,
8288
- `3. Approve and merge passing PRs`,
8289
- `4. Fire builds for children that are ready`,
8290
- `5. Create follow-up tasks for anything out of scope`,
8291
- `6. As children complete, correct their story points with update_subtask (storyPointValue) when the actual work diverged from the estimate \u2014 either direction`
8292
- ].join("\n");
5857
+ ## \u26A0\uFE0F The plan changed since this build was dispatched`,
5858
+ `Someone revised this card's plan ${when}. It was NOT you \u2014 an agent is never told about its own plan edit.`,
5859
+ `Before your next Write or Edit:`,
5860
+ `1. Call \`get_current_plan\` and read the current plan in full.`,
5861
+ `2. Compare it against the plan you were launched with. Treat the current plan as the truth.`,
5862
+ `3. Drop or redo any work the revision supersedes. Do NOT keep building the old plan.`,
5863
+ `If the revision invalidates work you already committed, say so with post_to_chat before you continue.`
5864
+ ];
8293
5865
  }
8294
5866
 
8295
5867
  // src/execution/tag-context-resolver.ts
@@ -8618,15 +6190,17 @@ Your plan has been approved \u2014 you are the Builder on this card now.`,
8618
6190
  `When finished, use the mcp__conveyor__create_pull_request tool to open a PR. Do NOT use gh CLI.`
8619
6191
  ];
8620
6192
  }
8621
- function buildPmReviewRelaunchParts() {
8622
- return [
6193
+ function buildPmReviewRelaunchParts(context) {
6194
+ const parts = [
8623
6195
  `
8624
- Resume reviewing and coordinating this task.`,
8625
- `Call list_subtasks to check current child-task state and progress.`,
8626
- `Review children in ReviewPR status first, then approve and merge passing PRs.`,
8627
- `Fire next child builds with start_child_cloud_build when ready.`,
8628
- `Do not implement code directly or create a new PR from the PM review session.`
6196
+ You are the Reviewer on this card now.`,
6197
+ ...buildSkillInvocation("/conveyor-review"),
6198
+ branchLine(context)
8629
6199
  ];
6200
+ if (context.githubPRUrl) {
6201
+ parts.push(`The PR under review is ${context.githubPRUrl}. Do not create a new PR.`);
6202
+ }
6203
+ return parts;
8630
6204
  }
8631
6205
  function buildPmAutoWithPlanRelaunchParts(context) {
8632
6206
  return [
@@ -8657,7 +6231,7 @@ function buildPmRelaunchParts(context, lastAgentIdx, isAuto, agentMode) {
8657
6231
  const intent = resolvePmRelaunchIntent(context, isAuto, agentMode);
8658
6232
  const intentPartsByIntent = {
8659
6233
  ["build" /* Build */]: () => buildPmBuildRelaunchParts(context),
8660
- ["review" /* Review */]: buildPmReviewRelaunchParts,
6234
+ ["review" /* Review */]: () => buildPmReviewRelaunchParts(context),
8661
6235
  ["auto_with_plan" /* AutoWithPlan */]: () => buildPmAutoWithPlanRelaunchParts(context),
8662
6236
  ["auto_planning" /* AutoPlanning */]: () => buildPmAutoPlanningRelaunchParts(context),
8663
6237
  ["wait_for_team" /* WaitForTeam */]: buildPmWaitForTeamRelaunchParts
@@ -8700,15 +6274,6 @@ Workflow:`,
8700
6274
  `- After saving the plan, call post_to_chat with a short summary for the team (your turn output is NOT posted to chat \u2014 they only see what you post), then end your turn. Do NOT attempt to execute the plan yourself.`,
8701
6275
  `- A separate task agent will handle execution after the team reviews and approves your plan.`
8702
6276
  ];
8703
- if (context.isParentTask) {
8704
- parts.push(
8705
- `
8706
- You are the Project Manager for this set of tasks.`,
8707
- `This task has child tasks (subtasks) that are tracked on the board.`,
8708
- `Your role is to coordinate, plan, and manage the subtasks \u2014 not to write code directly.`,
8709
- `Use the subtask tools (create_subtask, update_subtask, list_subtasks) to manage work breakdown.`
8710
- );
8711
- }
8712
6277
  if (context.agents && context.agents.length > 0) {
8713
6278
  parts.push(`
8714
6279
  Project Agents:`);
@@ -8744,7 +6309,10 @@ Workflow:`,
8744
6309
  `- If you toggled into active mode temporarily, mention when you're done so the team can switch you back to planning mode.`
8745
6310
  ].filter(Boolean);
8746
6311
  }
8747
- function buildTaskAgentPreamble(context, workspaceDir, runtimeTui) {
6312
+ function ciWaitRule(agentMode) {
6313
+ return agentMode === "review" ? `A review session has no \`wait_for_checks\`: after pushing a fix, read \`gh pr checks\` ONCE and decide on what it shows; never loop on it, and never end the turn expecting a CI wake.` : `Waiting on CI is never a \`gh pr checks\` loop, a pr-wait script, or a sleep: call \`wait_for_checks\` (mcp__conveyor__wait_for_checks) and END THE TURN. Conveyor wakes this session with the result; the pod sleeps in between instead of holding compute for a run it cannot influence.`;
6314
+ }
6315
+ function buildTaskAgentPreamble(context, workspaceDir, runtimeTui, agentMode) {
8748
6316
  const managedStack = repoHasScript(workspaceDir, "web:rebuild");
8749
6317
  const stackLines = managedStack ? [
8750
6318
  `- The web app is served on port 3050, the API on port 7090.`,
@@ -8762,7 +6330,7 @@ Environment \u2014 already built${managedStack ? " and running" : ""}. These are
8762
6330
  `- Browser automation is the Playwright CLI (\`playwright\`, pinned 1.62.1), NOT an MCP server \u2014 there are no \`mcp__playwright__*\` tools here. Only the headless shell is baked, so a launch must name it AND disable the sandbox: \`chromium.launch({ channel: "chromium-headless-shell", args: ["--no-sandbox"] })\`. A bare \`chromium.launch()\` FAILS: since playwright 1.49 that resolves to the full browser, which is deliberately not installed (pods have no display and cannot run Chromium's sandbox). Screenshots land wherever you write them \u2014 move or delete them before committing.`,
8763
6331
  `- The clone is \`--single-branch\`, so a bare \`git fetch origin <branch>\` does NOT create \`origin/<branch>\`. To reference any other branch, use the explicit refspec: \`git fetch origin <branch>:refs/remotes/origin/<branch>\`.`,
8764
6332
  `- The shell cwd resets between Bash calls, and so does every shell variable. A var you export in one call is EMPTY in the next, which silently redirects output to \`/\` and loses it. Write literal absolute paths (\`git -C\`, \`bun run --cwd\`), and \`mkdir -p\` a directory in the SAME call as the redirect that writes to it.`,
8765
- `- The \`gh\` CLI is available for READ-ONLY PR and CI state (\`gh pr view\`, \`gh pr checks\`, \`gh pr diff\`). Use the mcp__conveyor__* tools for anything that mutates a PR or card.`,
6333
+ `- The \`gh\` CLI is available for READ-ONLY PR and CI state (\`gh pr view\`, \`gh pr checks\`, \`gh pr diff\`), one read at a time. ${ciWaitRule(agentMode)} Use the mcp__conveyor__* tools for anything that mutates a PR or card.`,
8766
6334
  `- The core mcp__conveyor__* tools are preloaded \u2014 call them directly; do NOT spend a ToolSearch call on them. For any tool that IS still deferred (schema not loaded), load ALL the schemas you expect to need in ONE ToolSearch call using fully-qualified names (query "select:Monitor,mcp__conveyor__<name>" \u2014 bare MCP tool names without the mcp__<server>__ prefix do not match); never guess a deferred tool's parameters.`,
8767
6335
  managedStack ? `Because the environment is already up, do not run installs, builds, database setup, dev-server starts, or exploratory \`pwd\`/\`ls\` probes to confirm any of the above. Run them only when a specific error demands it.` : `Do not run installs, database setup, or exploratory \`pwd\`/\`ls\` probes to confirm any of the above \u2014 they are done. If the task needs a running app, check whether one is already up before you start it, and use the repo's own scripts.`,
8768
6336
  `
@@ -8770,25 +6338,22 @@ Working rules:`,
8770
6338
  `- Read a file before your first Write/Edit to it, and batch multiple changes to the same file into a single call instead of many sequential edits.`,
8771
6339
  `- To learn what calls a symbol or where it lives, query the prebuilt code graph before grepping: \`graphify query "<SymbolName>"\` from the repo root. Query a SYMBOL, never a sentence \u2014 \`graphify query "resolveTaskBaseBranch"\` returns the definition plus every call site, while "how does a task get its base branch" seeds unrelated start nodes and returns test files and loggers. Don't know the symbol yet? Grep for the name first, then query it: grep finds names, the graph finds relationships. \`No matching nodes found\` means "not in this graph" (it is prebuilt, so very recent code is absent), NOT "not in the codebase" \u2014 fall back to \`git grep\`. Skip all of this if \`graphify-out/graph.json\` is not present.`,
8772
6340
  `- When a build/lint/test run fails, capture its output to a file once and grep the file \u2014 never re-run the suite just to re-filter the same output.`,
8773
- runtimeTui === "codex" ? `- Waiting on long-running commands: retain and resume the command session until it returns an exit result. Finish each required gate before ending the task. Never pretend a completion notification will resume you, and never start another gate while the current one is running.` : `- Waiting on long-running commands: if a gate finishes in under ~2 minutes, run it in the foreground with a timeout. For a longer one, launch it with run_in_background and STOP; a completion notification arrives when it finishes, and the workspace stays awake for as long as background work is outstanding, so a backgrounded gate will not be killed by an idle sleep. For the final pre-PR gate a bounded foreground run (\`timeout 590 <gate>\` with Bash \`timeout: 600000\`) is still preferred as defense in depth \u2014 it survives a pod resume, which a background job does not. Never busy-wait with sleep/pgrep/tail loops, and never re-run the suite to escape a wait that looks stalled.`,
8774
- runtimeTui === "codex" ? `- Do not end your turn to wait on a required gate. Resume its command session, inspect its exit result, and then continue the checklist.` : `- Ending your turn with NO tool call is the correct way to wait, and it is safe: the pod stays alive and the next notification re-invokes you. Never emit filler commands (\`echo waiting\`, \`true\`, \`sleep N; echo done\`) to "stay alive" \u2014 they are detected and blocked. The proven long-wait shape: start the job with run_in_background, then end the turn. Arm a ScheduleWakeup (delaySeconds 900-1500, prompt restating your next steps) only when nothing will notify you \u2014 an external CI run, a deploy, a remote queue \u2014 never as insurance against a background job's own notification, which does fire.`,
6341
+ `- App servers (including development and production servers) keep running after startup. Start them with the harness background/session facility, then run a separate bounded HTTP readiness check and continue verification. Never wait for server exit or a completion notification to mean the app is ready. If readiness times out, inspect build output and process/resource state before restarting.`,
6342
+ runtimeTui === "codex" ? `- Waiting on long-running commands: retain and resume the command session until it returns an exit result. Finish each required gate before ending the task. Never pretend a completion notification will resume you, and never start another gate while the current one is running.` : `- Waiting on long-running commands: if a gate finishes in under ~2 minutes, run it in the foreground with a timeout. For a longer one, launch it with run_in_background and STOP; a completion notification arrives when it finishes, and tracked background work holds the workspace awake for up to 45 minutes. Inspect any gate approaching that cap. For the final pre-PR gate a bounded foreground run (\`timeout 590 <gate>\` with Bash \`timeout: 600000\`) is still preferred as defense in depth. A pod restart loses both foreground and background processes. Never busy-wait with sleep/pgrep/tail loops, and never re-run the suite to escape a wait that looks stalled.`,
6343
+ runtimeTui === "codex" ? `- Do not end your turn to wait on a required gate. Resume its command session, inspect its exit result, and then continue the checklist.` : `- For a finite background gate, end your turn and its completion notification re-invokes you within the bounded liveness window. Never emit filler commands (\`echo waiting\`, \`true\`, \`sleep N; echo done\`) to "stay alive" \u2014 they are detected and blocked. The proven long-wait shape: start the job with run_in_background, then end the turn.${agentMode === "review" ? "" : " For CI on a pushed commit the shape is `wait_for_checks`, then end the turn (the pod may sleep; the GitHub result wakes it)."} Arm a ScheduleWakeup (delaySeconds 900-1500, prompt restating your next steps) only when nothing will notify you \u2014 a deploy, a remote queue \u2014 never for CI, and never as insurance against a background job's own notification, which does fire.`,
8775
6344
  `
8776
6345
  Git:`,
8777
6346
  `- Stay on \`${context.githubBranch}\` for the whole task: do not check out another branch and do not create one. It was cut from \`${context.baseBranch}\`, and PRs target that automatically.`,
8778
6347
  `- If \`git push\` is rejected as non-fast-forward, run \`git push --force-with-lease origin ${context.githubBranch}\`. This branch is exclusively yours, so force-with-lease is safe.`
8779
6348
  ];
8780
6349
  }
8781
- function buildPackPrompt(mode, context, config, setupLog) {
8782
- return mode === "pack" && config.packExecution !== "fan-out" ? buildSinglePodPackPrompt(context, config, setupLog) : buildPackRunnerSystemPrompt(context, config, setupLog);
8783
- }
8784
6350
  function buildSystemPrompt(mode, context, config, setupLog, agentMode) {
8785
6351
  const isPm = mode === "pm";
8786
6352
  const isPmActive = isPm && agentMode === "building";
8787
- const isPackRunner = mode === "pack" || isPm && !!config.isAuto && !!context.isParentTask;
8788
- if (isPackRunner) {
8789
- return buildPackPrompt(mode, context, config, setupLog);
6353
+ if (isPackRunnerSession(mode, config.isAuto, context.isParentTask)) {
6354
+ return buildSinglePodPackPrompt(context, setupLog);
8790
6355
  }
8791
- const parts = isPmActive ? buildActivePreamble(context, config.workspaceDir) : isPm ? buildPmPreamble(context) : buildTaskAgentPreamble(context, config.workspaceDir, config.runtimeTui);
6356
+ const parts = isPmActive ? buildActivePreamble(context, config.workspaceDir) : isPm ? buildPmPreamble(context) : buildTaskAgentPreamble(context, config.workspaceDir, config.runtimeTui, agentMode);
8792
6357
  if (setupLog.length > 0) {
8793
6358
  parts.push(
8794
6359
  `
@@ -8802,11 +6367,6 @@ Environment setup log (already executed before you started \u2014 proof that set
8802
6367
  parts.push(`
8803
6368
  Agent Instructions:
8804
6369
  ${context.agentInstructions}`);
8805
- }
8806
- if (config.instructions) {
8807
- parts.push(`
8808
- Additional Instructions:
8809
- ${config.instructions}`);
8810
6370
  }
8811
6371
  parts.push(
8812
6372
  `
@@ -8896,6 +6456,14 @@ function buildRelaunchWithSession(mode, context, agentMode, isAuto) {
8896
6456
  allNew.filter((m) => m.role === "user")
8897
6457
  ).join("\n");
8898
6458
  }
6459
+ if (isCodeReviewRun(mode, agentMode)) {
6460
+ parts.push(
6461
+ scenario === "feedback_relaunch" ? `You have been relaunched with new feedback.` : `You were relaunched but no new instructions have been given since your last run.`,
6462
+ ...newMessagesBlock(allNew.filter((m) => m.role === "user")),
6463
+ ...buildFreshCodeReviewInstructions(context)
6464
+ );
6465
+ return parts.join("\n");
6466
+ }
8899
6467
  if (mode === "pm") {
8900
6468
  parts.push(...buildPmRelaunchParts(context, lastAgentIdx, isAuto, agentMode));
8901
6469
  } else if (isPlanningSession(mode, agentMode, isAuto)) {
@@ -8911,23 +6479,10 @@ function buildRelaunchWithSession(mode, context, agentMode, isAuto) {
8911
6479
  ...builderKickoff(context, `You are the Builder on this card \u2014 address the feedback above.`)
8912
6480
  );
8913
6481
  } else {
8914
- parts.push(`You were relaunched but no new instructions have been given since your last run.`);
8915
- if (agentMode === "auto" || agentMode === "building" || isAuto) {
8916
- parts.push(
8917
- ...builderKickoff(
8918
- context,
8919
- `You are the Builder on this card \u2014 pick up where you left off.`
8920
- )
8921
- );
8922
- } else {
8923
- parts.push(
8924
- branchRule(context),
8925
- `Post a brief status update with post_to_chat (your turn output is NOT shown in chat \u2014 post_to_chat is how the team sees it), then wait for further instructions.`
8926
- );
8927
- if (context.githubPRUrl) {
8928
- parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
8929
- }
8930
- }
6482
+ parts.push(
6483
+ `You were relaunched but no new instructions have been given since your last run.`,
6484
+ ...builderKickoff(context, `You are the Builder on this card \u2014 pick up where you left off.`)
6485
+ );
8931
6486
  }
8932
6487
  return parts.join("\n");
8933
6488
  }
@@ -9039,7 +6594,7 @@ function buildFeedbackInstructions(context, isPlanning) {
9039
6594
  `You have been relaunched to address the feedback in New Messages above.`
9040
6595
  );
9041
6596
  }
9042
- function buildIdleRelaunchInstructions(context, isPlanning, agentMode, isAuto) {
6597
+ function buildIdleRelaunchInstructions(context, isPlanning) {
9043
6598
  const opener = `You were relaunched but no new instructions have been given since your last run.`;
9044
6599
  if (isPlanning) {
9045
6600
  return [
@@ -9047,28 +6602,16 @@ function buildIdleRelaunchInstructions(context, isPlanning, agentMode, isAuto) {
9047
6602
  `You are the Planner on this card. Post a brief status update with post_to_chat (your turn output is NOT shown in chat \u2014 post_to_chat is how the team sees it), then wait for the team.`
9048
6603
  ];
9049
6604
  }
9050
- if (agentMode === "auto" || agentMode === "building" || isAuto) {
9051
- return [
9052
- opener,
9053
- ...builderKickoff(context, `You are the Builder on this card \u2014 pick up where you left off.`)
9054
- ];
9055
- }
9056
- const parts = [
6605
+ return [
9057
6606
  opener,
9058
- branchRule(context),
9059
- `Post a brief status update summarizing where things stand with post_to_chat (your turn output is NOT shown in chat \u2014 post_to_chat is how the team sees it).`,
9060
- `Then wait for further instructions \u2014 do NOT redo work that was already completed.`
6607
+ ...builderKickoff(context, `You are the Builder on this card \u2014 pick up where you left off.`)
9061
6608
  ];
9062
- if (context.githubPRUrl) {
9063
- parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
9064
- }
9065
- return parts;
9066
6609
  }
9067
6610
  function buildInstructions(mode, context, scenario, agentMode, isAuto) {
9068
6611
  const parts = [`
9069
6612
  ## Instructions`];
9070
6613
  const isPm = mode === "pm";
9071
- const isCodeReview = mode === "code-review" || !isPm && agentMode === "review";
6614
+ const isCodeReview = isCodeReviewRun(mode, agentMode);
9072
6615
  const isPlanning = isPlanningSession(mode, agentMode, isAuto);
9073
6616
  if (agentMode === "chat") {
9074
6617
  parts.push(...buildChatInstructions(context, scenario, relaunchMessageBatch(context)));
@@ -9093,14 +6636,14 @@ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
9093
6636
  return parts;
9094
6637
  }
9095
6638
  if (scenario === "idle_relaunch") {
9096
- parts.push(...buildIdleRelaunchInstructions(context, isPlanning, agentMode, isAuto));
6639
+ parts.push(...buildIdleRelaunchInstructions(context, isPlanning));
9097
6640
  return parts;
9098
6641
  }
9099
6642
  parts.push(...buildFeedbackInstructions(context, isPlanning));
9100
6643
  return parts;
9101
6644
  }
9102
- async function buildInitialPrompt(mode, context, isAuto, agentMode, packExecution) {
9103
- const isPackRunner = mode === "pack" || mode === "pm" && !!isAuto && !!context.isParentTask;
6645
+ async function buildInitialPrompt(mode, context, isAuto, agentMode) {
6646
+ const isPackRunner = isPackRunnerSession(mode, isAuto, context.isParentTask);
9104
6647
  if (!isPackRunner) {
9105
6648
  const sessionRelaunch = buildRelaunchWithSession(mode, context, agentMode, isAuto);
9106
6649
  if (sessionRelaunch) {
@@ -9113,13 +6656,12 @@ async function buildInitialPrompt(mode, context, isAuto, agentMode, packExecutio
9113
6656
  scenario = "fresh";
9114
6657
  }
9115
6658
  const body = await buildTaskBody(context, mode);
9116
- const singlePodPack = mode === "pack" && packExecution !== "fan-out";
9117
- const instructions = isPackRunner ? singlePodPack ? buildSinglePodPackInstructions(context, scenario) : buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario, agentMode, isAuto);
6659
+ const instructions = isPackRunner ? buildSinglePodPackInstructions(scenario) : buildInstructions(mode, context, scenario, agentMode, isAuto);
9118
6660
  return [...body, ...instructions].join("\n");
9119
6661
  }
9120
6662
 
9121
6663
  // src/tools/task-context-tools.ts
9122
- import { z as z14 } from "zod";
6664
+ import { z as z4 } from "zod";
9123
6665
 
9124
6666
  // ../shared/dist/tool-contracts/index.js
9125
6667
  var f = {
@@ -9148,14 +6690,14 @@ var f = {
9148
6690
  return { kind: "nullable", inner };
9149
6691
  }
9150
6692
  };
9151
- function compileString(z21, spec) {
9152
- let schema = z21.string();
6693
+ function compileString(z11, spec) {
6694
+ let schema = z11.string();
9153
6695
  if (spec.min !== void 0) schema = schema.min(spec.min);
9154
6696
  if (spec.max !== void 0) schema = schema.max(spec.max);
9155
6697
  return schema;
9156
6698
  }
9157
- function compileNumber(z21, spec) {
9158
- let schema = z21.number();
6699
+ function compileNumber(z11, spec) {
6700
+ let schema = z11.number();
9159
6701
  if (spec.int) schema = schema.int();
9160
6702
  if (spec.positive) schema = schema.positive();
9161
6703
  if (spec.nonnegative) schema = schema.nonnegative();
@@ -9163,49 +6705,49 @@ function compileNumber(z21, spec) {
9163
6705
  if (spec.max !== void 0) schema = schema.max(spec.max);
9164
6706
  return schema;
9165
6707
  }
9166
- function compileArray(z21, spec) {
9167
- let schema = z21.array(compileField(z21, spec.item));
6708
+ function compileArray(z11, spec) {
6709
+ let schema = z11.array(compileField(z11, spec.item));
9168
6710
  if (spec.min !== void 0) schema = schema.min(spec.min);
9169
6711
  return schema;
9170
6712
  }
9171
- function compileBase(z21, spec) {
6713
+ function compileBase(z11, spec) {
9172
6714
  switch (spec.kind) {
9173
6715
  case "string":
9174
- return compileString(z21, spec);
6716
+ return compileString(z11, spec);
9175
6717
  case "number":
9176
- return compileNumber(z21, spec);
6718
+ return compileNumber(z11, spec);
9177
6719
  case "boolean":
9178
- return z21.boolean();
6720
+ return z11.boolean();
9179
6721
  case "enum":
9180
- return z21.enum([...spec.values]);
6722
+ return z11.enum([...spec.values]);
9181
6723
  case "array":
9182
- return compileArray(z21, spec);
6724
+ return compileArray(z11, spec);
9183
6725
  case "object":
9184
- return z21.object(compileShape(z21, spec.fields));
6726
+ return z11.object(compileShape(z11, spec.fields));
9185
6727
  }
9186
6728
  }
9187
6729
  function descriptionOf(spec) {
9188
6730
  if (spec.kind === "optional" || spec.kind === "nullable") return descriptionOf(spec.inner);
9189
6731
  return spec.desc;
9190
6732
  }
9191
- function compileUndescribed(z21, spec) {
6733
+ function compileUndescribed(z11, spec) {
9192
6734
  if (spec.kind === "optional") {
9193
- return compileUndescribed(z21, spec.inner).optional();
6735
+ return compileUndescribed(z11, spec.inner).optional();
9194
6736
  }
9195
6737
  if (spec.kind === "nullable") {
9196
- return compileUndescribed(z21, spec.inner).nullable();
6738
+ return compileUndescribed(z11, spec.inner).nullable();
9197
6739
  }
9198
- return compileBase(z21, spec);
6740
+ return compileBase(z11, spec);
9199
6741
  }
9200
- function compileField(z21, spec) {
9201
- const schema = compileUndescribed(z21, spec);
6742
+ function compileField(z11, spec) {
6743
+ const schema = compileUndescribed(z11, spec);
9202
6744
  const desc = descriptionOf(spec);
9203
6745
  return desc === void 0 ? schema : schema.describe(desc);
9204
6746
  }
9205
- function compileShape(z21, fields) {
6747
+ function compileShape(z11, fields) {
9206
6748
  const shape = {};
9207
6749
  for (const [key, spec] of Object.entries(fields)) {
9208
- shape[key] = compileField(z21, spec);
6750
+ shape[key] = compileField(z11, spec);
9209
6751
  }
9210
6752
  return shape;
9211
6753
  }
@@ -9374,25 +6916,6 @@ var searchTasksContract = defineToolContract({
9374
6916
  }
9375
6917
  }
9376
6918
  });
9377
- var childTaskIdForMerge = f.string({
9378
- desc: "The child task ID whose PR should be approved and merged"
9379
- });
9380
- var approveAndMergePrContract = defineToolContract({
9381
- name: "approve_and_merge_pr",
9382
- agent: {
9383
- description: "Approve and merge a child task's PR. Preconditions: child in ReviewPR. Returns { merged }: true = merged (status\u2192ReviewDev); false = automerge queued, wait for ReviewDev. Requires project Admin, or a sub-project merge grant covering every changed file; release PRs require Admin.",
9384
- fields: {
9385
- childTaskId: childTaskIdForMerge
9386
- }
9387
- },
9388
- mcp: {
9389
- description: "Approve a child task's pull request and QUEUE it for merge \u2014 the merge lands asynchronously (~30s sweep) once the CI and code-review gates pass; the response says whether it merged or was queued, so verify PR state before depending on it. Pass projectId to target a specific project; otherwise the configured default project is used. The child task must be in ReviewPR status with a PR. Requires project Admin, or a sub-project merge grant covering every changed file; release PRs require Admin.",
9390
- fields: {
9391
- projectId: mcpProjectId,
9392
- childTaskId: childTaskIdForMerge
9393
- }
9394
- }
9395
- });
9396
6919
  var getConnectionContextContract = defineToolContract({
9397
6920
  name: "get_connection_context",
9398
6921
  agent: {
@@ -9412,7 +6935,6 @@ var tasksContracts = [
9412
6935
  readTaskChatContract,
9413
6936
  listTagsContract,
9414
6937
  searchTasksContract,
9415
- approveAndMergePrContract,
9416
6938
  getConnectionContextContract
9417
6939
  ];
9418
6940
  var STATUS_ENUM = [
@@ -9803,12 +7325,12 @@ var updateSubtaskContract = defineToolContract({
9803
7325
  plan: f.optional(f.string()),
9804
7326
  status: f.optional(
9805
7327
  f.enum(["Planning", "Open"], {
9806
- desc: 'Move the child between "Planning" and "Open". "Open" marks it ready to execute \u2014 required before start_child_cloud_build. Execution statuses transition automatically.'
7328
+ desc: 'Move the child between "Planning" and "Open". "Open" marks it ready to execute \u2014 the pack runner takes Open children in dependency order. Execution statuses transition automatically.'
9807
7329
  })
9808
7330
  ),
9809
7331
  agentIdOrName: f.optional(
9810
7332
  f.string({
9811
- desc: "Assign a project agent to the child (agent id or exact name from the Project Agents list). Required before start_child_cloud_build."
7333
+ desc: "Assign a project agent to the child (agent id or exact name from the Project Agents list)."
9812
7334
  })
9813
7335
  ),
9814
7336
  ordinal: f.optional(f.number()),
@@ -9860,7 +7382,7 @@ var deleteSubtaskContract = defineToolContract({
9860
7382
  var listSubtasksContract = defineToolContract({
9861
7383
  name: "list_subtasks",
9862
7384
  agent: {
9863
- description: "List all subtasks under the current parent task. Default compact view returns per child: status, agent, story points, PR number/state, and dependencies. On a FAN-OUT pack it also returns holdsBuildSlot per child plus a packSlots summary (in-flight environments vs the PACK_CHILD_LIMIT cap, and which children hold the slots); both are omitted on the default single-pod path, where one pod implements every child and nothing holds a slot. Use to coordinate child work; pass verbose:true only when you need full description/plan text (large). For non-child tasks use get_task.",
7385
+ description: "List all subtasks under the current parent task. Default compact view returns per child: status, agent, story points, PR number/state, and dependencies (dependsOn + allDependenciesMet). Use to pick the next ready child and record progress; pass verbose:true only when you need full description/plan text (large). For non-child tasks use get_task.",
9864
7386
  fields: {
9865
7387
  verbose: f.optional(
9866
7388
  f.boolean({
@@ -10637,6 +8159,50 @@ var logsContracts = [
10637
8159
  queryGcpLogsContract,
10638
8160
  queryGrafanaLogsContract
10639
8161
  ];
8162
+ var SHA = f.optional(
8163
+ f.string({
8164
+ desc: "Commit sha to watch, full or abbreviated. Defaults to the head of prNumber, else this card's PR, else the tip of this card's branch.",
8165
+ min: 7,
8166
+ max: 40
8167
+ })
8168
+ );
8169
+ var PR_NUMBER = f.optional(
8170
+ f.number({
8171
+ desc: "PR number whose current head to watch. Use this for a child's PR when orchestrating a pack.",
8172
+ int: true,
8173
+ positive: true
8174
+ })
8175
+ );
8176
+ var TIMEOUT_MINUTES = f.optional(
8177
+ f.number({
8178
+ desc: `Give up after this many minutes (default ${DEFAULT_CI_WAIT_TIMEOUT_MINUTES}, max ${MAX_CI_WAIT_TIMEOUT_MINUTES}); you are woken with timed_out.`,
8179
+ int: true,
8180
+ min: 1,
8181
+ max: MAX_CI_WAIT_TIMEOUT_MINUTES
8182
+ })
8183
+ );
8184
+ var waitForChecksContract = defineToolContract({
8185
+ name: "wait_for_checks",
8186
+ agent: {
8187
+ description: "Wait for CI without holding the pod. Records the commit to watch on this session and returns immediately: if CI already finished you get the result now; otherwise the reply says `parked` and you must END YOUR TURN with no further tool calls. The pod idles out during the wait and Conveyor wakes this session with the result (success or failure with the failing job names and run URL, head_moved when the PR gets a new commit, timed_out at the deadline). Never poll `gh pr checks`, pr-wait scripts, or a sleep loop for CI. If something else wakes you before the result arrives, handle it and call this again.",
8188
+ fields: {
8189
+ sha: SHA,
8190
+ prNumber: PR_NUMBER,
8191
+ timeoutMinutes: TIMEOUT_MINUTES
8192
+ }
8193
+ },
8194
+ mcp: {
8195
+ description: "Park a task's agent session until GitHub reports the CI result for a commit. Pass projectId to target a specific project; otherwise the configured default project is used.",
8196
+ fields: {
8197
+ projectId: mcpProjectId,
8198
+ taskId: f.string({ desc: "The task whose agent session should wait" }),
8199
+ sha: SHA,
8200
+ prNumber: PR_NUMBER,
8201
+ timeoutMinutes: TIMEOUT_MINUTES
8202
+ }
8203
+ }
8204
+ });
8205
+ var ciWaitContracts = [waitForChecksContract];
10640
8206
  var TOOL_CONTRACTS = Object.fromEntries(
10641
8207
  [
10642
8208
  ...tasksContracts,
@@ -10651,14 +8217,15 @@ var TOOL_CONTRACTS = Object.fromEntries(
10651
8217
  ...integrationsContracts,
10652
8218
  ...driveContracts,
10653
8219
  ...meetingsContracts,
10654
- ...logsContracts
8220
+ ...logsContracts,
8221
+ ...ciWaitContracts
10655
8222
  ].map((contract) => [contract.name, contract])
10656
8223
  );
10657
8224
 
10658
8225
  // src/tools/contract-tool.ts
10659
- import { z as z13 } from "zod";
8226
+ import { z as z3 } from "zod";
10660
8227
  function agentShape(surface) {
10661
- return compileShape(z13, surface.fields);
8228
+ return compileShape(z3, surface.fields);
10662
8229
  }
10663
8230
  function defineContractTool(contract, handler, options) {
10664
8231
  return {
@@ -10815,11 +8382,11 @@ function buildGetExecutionLogsTool(connection) {
10815
8382
  "get_execution_logs",
10816
8383
  "Read CLI execution logs \u2014 agent reasoning, tool calls, and setup/dev-server output. Filter via source='agent' or 'application'. For human chat use read_task_chat.",
10817
8384
  {
10818
- task_id: z14.string().optional().describe(
8385
+ task_id: z4.string().optional().describe(
10819
8386
  "Task ID or slug. Omit to read logs from the current task. Only the current task or one of its child tasks can be read."
10820
8387
  ),
10821
- source: z14.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
10822
- limit: z14.number().optional().describe("Max number of log entries to return (default 50, max 500).")
8388
+ source: z4.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
8389
+ limit: z4.number().optional().describe("Max number of log entries to return (default 50, max 500).")
10823
8390
  },
10824
8391
  async ({ task_id, source, limit }) => {
10825
8392
  try {
@@ -10914,7 +8481,7 @@ function buildTaskContextTools(connection) {
10914
8481
  }
10915
8482
 
10916
8483
  // src/tools/dependency-suggestion-tools.ts
10917
- import { z as z15 } from "zod";
8484
+ import { z as z5 } from "zod";
10918
8485
  function buildGetDependenciesTool(connection) {
10919
8486
  return defineContractTool(
10920
8487
  getDependenciesContract,
@@ -10938,10 +8505,10 @@ function buildGetSuggestionsTool(connection) {
10938
8505
  "get_suggestions",
10939
8506
  "List project suggestions sorted by vote score. Filter by status or cap with limit (default 20). Suggestions are project-level ideas, not tasks \u2014 use get_task for tasks.",
10940
8507
  {
10941
- status: z15.string().optional().describe(
8508
+ status: z5.string().optional().describe(
10942
8509
  "Filter by status: Planning, Open, InProgress, ReviewPR, ReviewDev, ReviewLive, Complete, Cancelled"
10943
8510
  ),
10944
- limit: z15.number().int().min(1).max(100).optional().describe("Max results (default 20)")
8511
+ limit: z5.number().int().min(1).max(100).optional().describe("Max results (default 20)")
10945
8512
  },
10946
8513
  async ({ status, limit }) => {
10947
8514
  try {
@@ -10965,7 +8532,7 @@ function buildGetSuggestionsTool(connection) {
10965
8532
  }
10966
8533
 
10967
8534
  // src/tools/mutation-tools.ts
10968
- import { z as z16 } from "zod";
8535
+ import { z as z6 } from "zod";
10969
8536
 
10970
8537
  // src/runner/refresh-verify-heal.ts
10971
8538
  async function refreshAndVerifyGithubCredential(cwd, mint) {
@@ -11097,7 +8664,7 @@ function buildForceUpdateTaskStatusTool(connection) {
11097
8664
  "force_update_task_status",
11098
8665
  "EMERGENCY ONLY: force-override a task's Kanban status. Use when an automatic transition failed and the task is wedged. Normal flow transitions status automatically.",
11099
8666
  {
11100
- status: z16.enum([
8667
+ status: z6.enum([
11101
8668
  "Planning",
11102
8669
  "Open",
11103
8670
  "InProgress",
@@ -11107,7 +8674,7 @@ function buildForceUpdateTaskStatusTool(connection) {
11107
8674
  "Complete",
11108
8675
  "Cancelled"
11109
8676
  ]).describe("The new status for the task"),
11110
- task_id: z16.string().optional().describe("Child task ID to update. Omit to update the current task.")
8677
+ task_id: z6.string().optional().describe("Child task ID to update. Omit to update the current task.")
11111
8678
  },
11112
8679
  async ({ status, task_id }) => {
11113
8680
  try {
@@ -11260,10 +8827,10 @@ function buildCreateFollowUpTaskTool(connection) {
11260
8827
  "create_follow_up_task",
11261
8828
  "Create a follow-up task that depends on the current task. The new card is a SIBLING of this one (same parent) and is blocked until this task merges \u2014 it is NOT a child of this card. To break this card into child cards that build as a pack, use create_subtask. For blockers use add_dependency.",
11262
8829
  {
11263
- title: z16.string().describe("Follow-up task title"),
11264
- description: z16.string().optional().describe(cardDescriptionDesc("Brief description of the follow-up work")),
11265
- plan: z16.string().optional().describe("Implementation plan if known"),
11266
- story_point_value: z16.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
8830
+ title: z6.string().describe("Follow-up task title"),
8831
+ description: z6.string().optional().describe(cardDescriptionDesc("Brief description of the follow-up work")),
8832
+ plan: z6.string().optional().describe("Implementation plan if known"),
8833
+ story_point_value: z6.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
11267
8834
  },
11268
8835
  async ({ title, description, plan, story_point_value }) => {
11269
8836
  try {
@@ -11290,11 +8857,11 @@ function buildCreateSuggestionTool(connection) {
11290
8857
  "create_suggestion",
11291
8858
  "Suggest a feature, improvement, rule, or idea for the project. Duplicates are deduped and your upvote is recorded. For actionable work on this task open a follow-up task.",
11292
8859
  {
11293
- title: z16.string().describe("Short title for the suggestion"),
11294
- description: z16.string().optional().describe(
8860
+ title: z6.string().describe("Short title for the suggestion"),
8861
+ description: z6.string().optional().describe(
11295
8862
  "1-2 sentence description of what should change and why. Keep concise and project-focused."
11296
8863
  ),
11297
- tag_names: z16.array(z16.string()).optional().describe("Tag names to categorize the suggestion")
8864
+ tag_names: z6.array(z6.string()).optional().describe("Tag names to categorize the suggestion")
11298
8865
  },
11299
8866
  async ({ title, description, tag_names }) => {
11300
8867
  try {
@@ -11323,8 +8890,8 @@ function buildVoteSuggestionTool(connection) {
11323
8890
  "vote_suggestion",
11324
8891
  "Vote +1 or -1 on a project suggestion. Use to express support or disagreement with a specific suggestion returned by get_suggestions.",
11325
8892
  {
11326
- suggestion_id: z16.string().describe("The suggestion ID to vote on"),
11327
- value: z16.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
8893
+ suggestion_id: z6.string().describe("The suggestion ID to vote on"),
8894
+ value: z6.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
11328
8895
  },
11329
8896
  async ({ suggestion_id, value }) => {
11330
8897
  try {
@@ -11686,6 +9253,47 @@ function buildRejectManualTestTool(connection) {
11686
9253
  });
11687
9254
  }
11688
9255
 
9256
+ // src/tools/ci-wait-tools.ts
9257
+ function short(sha) {
9258
+ return sha.slice(0, 7);
9259
+ }
9260
+ function describeHead(result) {
9261
+ return `${short(result.sha)}${result.prNumber ? ` (PR #${result.prNumber})` : ""}`;
9262
+ }
9263
+ function formatParkOnCheckResult(result) {
9264
+ if (result.status === "parked") {
9265
+ const running = result.pendingChecks.length > 0 ? ` Still running: ${result.pendingChecks.join(", ")}.` : " No check has registered yet.";
9266
+ return `Parked on ${describeHead(result)} until ${result.deadline}.${running}
9267
+
9268
+ END YOUR TURN NOW: no further tool calls, no polling, no filler commands. Conveyor wakes this session with the result (SUCCESS or FAILURE with the failing jobs and run URL), with head_moved if the PR gets a new commit, or with timed_out at the deadline. Uncommitted work is covered by the per-turn WIP snapshot, but anything already committed and pushed needs no recovery at all. If something else wakes you first, handle it and call wait_for_checks again.`;
9269
+ }
9270
+ if (result.status === "completed") {
9271
+ if (result.conclusion === "success") {
9272
+ return `CI for ${describeHead(result)} already finished: SUCCESS. All ${result.checksTotal} checks passed. Continue with the next step.`;
9273
+ }
9274
+ const pending = result.pendingChecks.length > 0 ? ` Still running: ${result.pendingChecks.join(", ")}.` : "";
9275
+ return `CI for ${describeHead(result)} already has a result: FAILURE. Failing: ${result.failingChecks.join(", ")}.${result.runUrl ? ` Run: ${result.runUrl}.` : ""}${pending} Triage before treating it as your bug (stale head? runner preemption? file not in your diff? base branch red?), then fix, push, and call wait_for_checks again.`;
9276
+ }
9277
+ return `Cannot park this session: ${result.reason}. Read the checks ONCE with read-only \`gh pr checks\` and act on what you see; never loop on it.`;
9278
+ }
9279
+ function buildWaitForChecksTool(connection) {
9280
+ return defineContractTool(waitForChecksContract, async ({ sha, prNumber, timeoutMinutes }) => {
9281
+ try {
9282
+ const result = await connection.call("parkOnCheckResult", {
9283
+ sessionId: connection.sessionId,
9284
+ ...sha === void 0 ? {} : { sha },
9285
+ ...prNumber === void 0 ? {} : { prNumber },
9286
+ ...timeoutMinutes === void 0 ? {} : { timeoutMinutes }
9287
+ });
9288
+ return textResult(formatParkOnCheckResult(result));
9289
+ } catch (error) {
9290
+ return textResult(
9291
+ `wait_for_checks failed: ${error instanceof Error ? error.message : String(error)}. Retry once if this was a transient socket reconnect; otherwise read the checks once with read-only \`gh pr checks\` and end your turn instead of polling.`
9292
+ );
9293
+ }
9294
+ });
9295
+ }
9296
+
11689
9297
  // src/tools/common-tools.ts
11690
9298
  function buildCommonTools(connection, config) {
11691
9299
  return [
@@ -11705,7 +9313,7 @@ function buildCommonTools(connection, config) {
11705
9313
  }
11706
9314
 
11707
9315
  // src/tools/pm-tools.ts
11708
- import { z as z17 } from "zod";
9316
+ import { z as z7 } from "zod";
11709
9317
 
11710
9318
  // src/tools/task-update-tools.ts
11711
9319
  var CURRENT_TASK_ONLY = ["title", "description", "plan", "githubBranch"];
@@ -11778,8 +9386,8 @@ function buildUpdateTaskTool(connection) {
11778
9386
  "update_task_plan",
11779
9387
  "Save the plan and/or description to the current task. In auto/building mode, save the plan BEFORE writing code and keep it current as the approach evolves \u2014 post it, then build; never pause the build waiting for approval. For children use update_subtask; for title/tags/PR use update_task_properties.",
11780
9388
  {
11781
- plan: z17.string().optional().describe("The task plan in markdown"),
11782
- description: z17.string().optional().describe(cardDescriptionDesc("Updated task description"))
9389
+ plan: z7.string().optional().describe("The task plan in markdown"),
9390
+ description: z7.string().optional().describe(cardDescriptionDesc("Updated task description"))
11783
9391
  },
11784
9392
  async ({ plan, description }) => {
11785
9393
  try {
@@ -11802,10 +9410,10 @@ function buildHandoffTool(connection) {
11802
9410
  "handoff_to_agent",
11803
9411
  "Hand this task off to an implementer agent for the build phase \u2014 mid-conversation, same session, no restart. Call this once the plan is compiled and saved (update_task). The server swaps this task to the difficulty-sized implementer agent (which may run at a different model level), announces the handoff in the activity log + chat, and switches you into build mode to start implementing. Size the work with the storyPoints arg (or set it first via update_task_properties). Returns the implementer's name + model.",
11804
9412
  {
11805
- storyPoints: z17.number().int().positive().optional().describe(
9413
+ storyPoints: z7.number().int().positive().optional().describe(
11806
9414
  "Difficulty sizing (1=Common, 2=Magic, 3=Rare, 5=Unique, 8=Pack) \u2014 picks which implementer agent takes over. Omit to use the task's current story points."
11807
9415
  ),
11808
- message: z17.string().optional().describe("Optional kickoff note posted to the chat alongside the handoff notice.")
9416
+ message: z7.string().optional().describe("Optional kickoff note posted to the chat alongside the handoff notice.")
11809
9417
  },
11810
9418
  async ({ storyPoints, message }) => {
11811
9419
  try {
@@ -11950,81 +9558,8 @@ function buildListSubtasksTool(connection) {
11950
9558
  { annotations: { readOnlyHint: true } }
11951
9559
  );
11952
9560
  }
11953
- function buildPackTools(connection) {
9561
+ function buildPmTools(connection) {
11954
9562
  return [
11955
- defineTool(
11956
- "start_child_cloud_build",
11957
- "Start a cloud build (codespace) for a child task. Preconditions: child status is `Open`, story points set, and an agent assigned \u2014 satisfy all three with update_subtask (status/agentIdOrName/storyPointValue) first; none happen automatically. A PACK_CHILD_LIMIT error is backpressure, not failure: check list_subtasks packSlots for which children hold the in-flight slots, merge/stop one, then retry. On a feature-branch pack, dev is merged into the pack branch first so the child branches from a fresh base; a conflict is reported in the result and never blocks the launch.",
11958
- {
11959
- childTaskId: z17.string().describe("The child task ID to start a cloud build for")
11960
- },
11961
- async ({ childTaskId }) => {
11962
- try {
11963
- const result = await connection.call("startChildCloudBuild", {
11964
- sessionId: connection.sessionId,
11965
- childTaskId
11966
- });
11967
- const started = `Cloud build started for child task: ${result.childTaskId}`;
11968
- const sync = result.baseSync;
11969
- if (sync?.error) {
11970
- return textResult(
11971
- `${started}
11972
-
11973
- Base sync: dev \u2192 ${sync.branch} failed \u2014 ${sync.error}. The child was launched from the un-synced pack branch. Merge dev into ${sync.branch} in your local checkout, resolve the conflicts, and push before firing more children.`
11974
- );
11975
- }
11976
- return textResult(started);
11977
- } catch (error) {
11978
- return textResult(
11979
- `Failed to start child cloud build: ${error instanceof Error ? error.message : "Unknown error"}`
11980
- );
11981
- }
11982
- }
11983
- ),
11984
- defineTool(
11985
- "stop_child_build",
11986
- "Send a graceful stop signal to a running child build's agent. Not a force-kill \u2014 the agent may take a moment to wind down. Stopping a child eventually frees its PACK_CHILD_LIMIT build slot (see list_subtasks packSlots).",
11987
- {
11988
- childTaskId: z17.string().describe("The child task ID whose build should be stopped")
11989
- },
11990
- async ({ childTaskId }) => {
11991
- try {
11992
- await connection.call("stopChildBuild", {
11993
- sessionId: connection.sessionId,
11994
- childTaskId
11995
- });
11996
- return textResult(`Stop signal sent to child task: ${childTaskId}`);
11997
- } catch (error) {
11998
- return textResult(
11999
- `Failed to stop child build: ${error instanceof Error ? error.message : "Unknown error"}`
12000
- );
12001
- }
12002
- }
12003
- ),
12004
- defineContractTool(approveAndMergePrContract, async ({ childTaskId }) => {
12005
- try {
12006
- const result = await connection.call("approveAndMergePR", {
12007
- sessionId: connection.sessionId,
12008
- childTaskId
12009
- });
12010
- if (result.merged) {
12011
- return textResult(
12012
- `PR #${result.prNumber} approved and merged for task ${result.childTaskId}. Task status updated to ReviewDev.`
12013
- );
12014
- }
12015
- return textResult(
12016
- `PR #${result.prNumber} merge queued for task ${result.childTaskId} \u2014 CI checks still in progress. The PR will auto-merge when all checks pass. Do NOT proceed as if merged. Wait for the child task status to change to ReviewDev before continuing.`
12017
- );
12018
- } catch (error) {
12019
- return textResult(
12020
- `Failed to approve and merge PR: ${error instanceof Error ? error.message : "Unknown error"}`
12021
- );
12022
- }
12023
- })
12024
- ];
12025
- }
12026
- function buildPmTools(connection, options) {
12027
- const tools = [
12028
9563
  buildUpdateTaskTool(connection),
12029
9564
  // The shared-skill vocabulary alongside the split legacy tools above.
12030
9565
  // Both stay REGISTERED, but the migration this comment used to defer has
@@ -12039,12 +9574,10 @@ function buildPmTools(connection, options) {
12039
9574
  buildDeleteSubtaskTool(connection),
12040
9575
  buildListSubtasksTool(connection)
12041
9576
  ];
12042
- if (!options?.includePackTools) return tools;
12043
- return [...tools, ...buildPackTools(connection)];
12044
9577
  }
12045
9578
 
12046
9579
  // src/tools/discovery-tools.ts
12047
- import { z as z18 } from "zod";
9580
+ import { z as z8 } from "zod";
12048
9581
  var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique). The key is 'storyPointValue' \u2014 not 'storyPoints'.";
12049
9582
  var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk";
12050
9583
  function describeUpdatedFields(p) {
@@ -12063,12 +9596,12 @@ function buildDiscoveryTools(connection) {
12063
9596
  "update_task_properties",
12064
9597
  "Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
12065
9598
  {
12066
- title: z18.string().optional().describe("The new task title"),
12067
- storyPointValue: z18.number().optional().describe(SP_DESCRIPTION2),
12068
- tagNames: z18.array(z18.string()).optional().describe("Array of tag names to assign"),
12069
- githubPRUrl: z18.string().url().optional().describe("GitHub pull request URL to link to this task"),
12070
- githubBranch: z18.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
12071
- risk: z18.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
9599
+ title: z8.string().optional().describe("The new task title"),
9600
+ storyPointValue: z8.number().optional().describe(SP_DESCRIPTION2),
9601
+ tagNames: z8.array(z8.string()).optional().describe("Array of tag names to assign"),
9602
+ githubPRUrl: z8.string().url().optional().describe("GitHub pull request URL to link to this task"),
9603
+ githubBranch: z8.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
9604
+ risk: z8.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
12072
9605
  "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
12073
9606
  )
12074
9607
  },
@@ -12108,7 +9641,7 @@ function buildDiscoveryTools(connection) {
12108
9641
  }
12109
9642
 
12110
9643
  // src/tools/project-tools.ts
12111
- import { z as z19 } from "zod";
9644
+ import { z as z9 } from "zod";
12112
9645
 
12113
9646
  // src/execution/context-path-verifier.ts
12114
9647
  import { readFile as readFile3 } from "fs/promises";
@@ -12186,16 +9719,16 @@ function formatContextPathProblems(problems) {
12186
9719
  }
12187
9720
 
12188
9721
  // src/tools/project-tools.ts
12189
- var CONTEXT_PATH_SHAPE = z19.object({
12190
- type: z19.enum(["rule", "doc", "file", "folder"]).describe(
9722
+ var CONTEXT_PATH_SHAPE = z9.object({
9723
+ type: z9.enum(["rule", "doc", "file", "folder"]).describe(
12191
9724
  "Link kind \u2014 all paths are repo-relative; doc marks a synced project doc, which resolves from the workspace like rule/file"
12192
9725
  ),
12193
- path: z19.string().min(1).max(500).describe("Repo-relative path"),
12194
- label: z19.string().max(100).optional(),
12195
- locator: z19.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional().describe(
9726
+ path: z9.string().min(1).max(500).describe("Repo-relative path"),
9727
+ label: z9.string().max(100).optional(),
9728
+ locator: z9.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional().describe(
12196
9729
  'Verified-link tether: text that must keep existing in the file. With locatorType "test" it must appear inside a real it/test/describe TITLE; with "code" anywhere in the file. Validated at write time against the checkout and re-checked by the periodic sweep \u2014 a rename/delete flags the link stale. Locators containing <> are placeholders and never checked.'
12197
9730
  ),
12198
- locatorType: z19.enum(["test", "code"]).optional().describe(
9731
+ locatorType: z9.enum(["test", "code"]).optional().describe(
12199
9732
  "How the locator must match \u2014 required iff locator is set; not valid on folder links"
12200
9733
  )
12201
9734
  }).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
@@ -12244,15 +9777,15 @@ function buildCreateTagTool(connection, projectId, workspaceDir) {
12244
9777
  "create_tag",
12245
9778
  "Create a project tag. Include a crisp description (\u2264255 chars \u2014 the summary) and contextPaths (rule/doc/file/folder links agents auto-load when working on matching tasks); put the full spec in overview. Set parentTagIds to place the tag in the hierarchy right away. Every contextPath is checked against the repo checkout \u2014 a path that does not exist, or whose type does not match what is on disk, rejects the whole call. Fails if the name already exists.",
12246
9779
  {
12247
- name: z19.string().min(1).max(50),
12248
- color: z19.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("#RRGGBB (default gray)"),
12249
- description: z19.string().max(TAG_DESCRIPTION_MAX).optional(),
12250
- overview: z19.string().max(TAG_OVERVIEW_MAX).optional().describe("Full markdown glossary body \u2014 the term's spec"),
12251
- overviewPath: z19.string().min(1).max(500).optional().describe(
9780
+ name: z9.string().min(1).max(50),
9781
+ color: z9.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("#RRGGBB (default gray)"),
9782
+ description: z9.string().max(TAG_DESCRIPTION_MAX).optional(),
9783
+ overview: z9.string().max(TAG_OVERVIEW_MAX).optional().describe("Full markdown glossary body \u2014 the term's spec"),
9784
+ overviewPath: z9.string().min(1).max(500).optional().describe(
12252
9785
  "Repo file to source the overview from (base-branch content is served everywhere). A not-yet-merged path is fine \u2014 the tag serves the stored overview as fallback until the file lands."
12253
9786
  ),
12254
- parentTagIds: z19.array(z19.string()).max(25).optional().describe("Parent tag ids from list_tags (multi-parent DAG) to link at create time"),
12255
- contextPaths: z19.array(CONTEXT_PATH_SHAPE).max(20).optional()
9787
+ parentTagIds: z9.array(z9.string()).max(25).optional().describe("Parent tag ids from list_tags (multi-parent DAG) to link at create time"),
9788
+ contextPaths: z9.array(CONTEXT_PATH_SHAPE).max(20).optional()
12256
9789
  },
12257
9790
  async ({ name, color, description, overview, overviewPath, parentTagIds, contextPaths }) => {
12258
9791
  const rejection = await rejectBadContextPaths(contextPaths, workspaceDir);
@@ -12280,19 +9813,19 @@ function buildUpdateTagTool(connection, projectId, taskId, workspaceDir) {
12280
9813
  "update_tag",
12281
9814
  "Update a tag's name, color, description (\u2264255), markdown overview, parent tags, or contextPaths. contextPaths and parentTagIds are FULL replacements \u2014 include what you want to keep. ALWAYS pass a short reason; it lands in the tag's revision history (with your current card auto-stamped) so the team sees why the glossary changed. Every contextPath is checked against the repo checkout \u2014 a path that does not exist, or whose type does not match what is on disk, rejects the whole call and nothing is written.",
12282
9815
  {
12283
- tagId: z19.string().describe("Tag id from list_tags"),
12284
- name: z19.string().min(1).max(50).optional(),
12285
- color: z19.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
12286
- description: z19.string().max(TAG_DESCRIPTION_MAX).optional(),
12287
- overview: z19.string().max(TAG_OVERVIEW_MAX).nullable().optional().describe(
9816
+ tagId: z9.string().describe("Tag id from list_tags"),
9817
+ name: z9.string().min(1).max(50).optional(),
9818
+ color: z9.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
9819
+ description: z9.string().max(TAG_DESCRIPTION_MAX).optional(),
9820
+ overview: z9.string().max(TAG_OVERVIEW_MAX).nullable().optional().describe(
12288
9821
  "Full markdown glossary body; null clears it. REJECTED while overviewPath is set \u2014 edit the sourced file in the repo instead"
12289
9822
  ),
12290
- overviewPath: z19.string().min(1).max(500).nullable().optional().describe(
9823
+ overviewPath: z9.string().min(1).max(500).nullable().optional().describe(
12291
9824
  "Repo file to source the overview from (null clears back to the stored overview). A not-yet-merged path is fine \u2014 the stored overview serves as fallback until the file lands on the base branch."
12292
9825
  ),
12293
- parentTagIds: z19.array(z19.string()).max(25).optional().describe("Full-set replacement of the tag's parent tags (multi-parent DAG)"),
12294
- reason: z19.string().max(TAG_REASON_MAX).optional().describe("One line on why \u2014 shown in the tag's revision history"),
12295
- contextPaths: z19.array(CONTEXT_PATH_SHAPE).max(20).optional()
9826
+ parentTagIds: z9.array(z9.string()).max(25).optional().describe("Full-set replacement of the tag's parent tags (multi-parent DAG)"),
9827
+ reason: z9.string().max(TAG_REASON_MAX).optional().describe("One line on why \u2014 shown in the tag's revision history"),
9828
+ contextPaths: z9.array(CONTEXT_PATH_SHAPE).max(20).optional()
12296
9829
  },
12297
9830
  async ({
12298
9831
  tagId,
@@ -12350,8 +9883,8 @@ function buildPostToProjectChatTool(connection, projectId) {
12350
9883
  "post_to_project_chat",
12351
9884
  "Post a markdown message to the PROJECT chat \u2014 use once at the end of an audit for the summary the team reads.",
12352
9885
  {
12353
- message: z19.string().min(1).max(2e4),
12354
- kind: z19.enum(["tag_audit_summary"]).optional().describe(
9886
+ message: z9.string().min(1).max(2e4),
9887
+ kind: z9.enum(["tag_audit_summary"]).optional().describe(
12355
9888
  "Set to 'tag_audit_summary' when posting a tag-audit summary so it is also saved to the persistent tag history"
12356
9889
  )
12357
9890
  },
@@ -12370,7 +9903,7 @@ function buildGetProjectTaskTool(connection, projectId) {
12370
9903
  "get_project_task",
12371
9904
  "Fetch any task in the project by id or slug: title, description, plan, status, and metadata. The audit evidence trail starts here.",
12372
9905
  {
12373
- taskId: z19.string().describe("Task id or slug")
9906
+ taskId: z9.string().describe("Task id or slug")
12374
9907
  },
12375
9908
  async ({ taskId }) => {
12376
9909
  try {
@@ -12388,8 +9921,8 @@ function buildReadProjectTaskChatTool(connection, projectId) {
12388
9921
  "read_project_task_chat",
12389
9922
  "Read any project task's chat messages (newest last). role 'user' rows are HUMAN turns; 'assistant'/'system' rows are agent posts and activity-log entries.",
12390
9923
  {
12391
- taskId: z19.string().describe("Task id or slug"),
12392
- limit: z19.number().int().min(1).max(200).optional().describe("Messages to fetch (default 50)")
9924
+ taskId: z9.string().describe("Task id or slug"),
9925
+ limit: z9.number().int().min(1).max(200).optional().describe("Messages to fetch (default 50)")
12393
9926
  },
12394
9927
  async ({ taskId, limit }) => {
12395
9928
  try {
@@ -12411,9 +9944,9 @@ function buildGetProjectTaskLogsTool(connection, projectId) {
12411
9944
  "get_project_task_logs",
12412
9945
  "Read any project task's persisted agent event stream (message / tool_use / turn_end / error / completed). Turn boundaries are turn_end events. Entries are truncated to ~2KB each; max 500 per call.",
12413
9946
  {
12414
- taskId: z19.string().describe("Task id or slug"),
12415
- limit: z19.number().int().min(1).max(500).optional().describe("Entries to fetch (default 50)"),
12416
- source: z19.enum(["agent", "application"]).optional().describe("Filter: 'agent' = model events (default useful for grading)")
9947
+ taskId: z9.string().describe("Task id or slug"),
9948
+ limit: z9.number().int().min(1).max(500).optional().describe("Entries to fetch (default 50)"),
9949
+ source: z9.enum(["agent", "application"]).optional().describe("Filter: 'agent' = model events (default useful for grading)")
12417
9950
  },
12418
9951
  async ({ taskId, limit, source }) => {
12419
9952
  try {
@@ -12431,44 +9964,44 @@ function buildGetProjectTaskLogsTool(connection, projectId) {
12431
9964
  { annotations: { readOnlyHint: true } }
12432
9965
  );
12433
9966
  }
12434
- var TURN_GRADE_SHAPE = z19.object({
12435
- turnIndex: z19.number().int().min(0),
12436
- phase: z19.enum(["planning", "building", "human"]),
12437
- grade: z19.enum(["correct", "neutral", "blunder"]),
12438
- reasoning: z19.string(),
12439
- eventType: z19.string().describe('e.g. "message", "tool_use", "human_message"'),
12440
- eventSummary: z19.string().max(200).describe("\u2264120 chars of what happened this turn")
9967
+ var TURN_GRADE_SHAPE = z9.object({
9968
+ turnIndex: z9.number().int().min(0),
9969
+ phase: z9.enum(["planning", "building", "human"]),
9970
+ grade: z9.enum(["correct", "neutral", "blunder"]),
9971
+ reasoning: z9.string(),
9972
+ eventType: z9.string().describe('e.g. "message", "tool_use", "human_message"'),
9973
+ eventSummary: z9.string().max(200).describe("\u2264120 chars of what happened this turn")
12441
9974
  });
12442
- var HUMAN_EVAL_SHAPE = z19.object({
12443
- messageIndex: z19.number().int().min(0).describe("Index into the task's human messages, oldest first"),
12444
- rating: z19.number().int().min(-1).max(1),
12445
- reasoning: z19.string()
9975
+ var HUMAN_EVAL_SHAPE = z9.object({
9976
+ messageIndex: z9.number().int().min(0).describe("Index into the task's human messages, oldest first"),
9977
+ rating: z9.number().int().min(-1).max(1),
9978
+ reasoning: z9.string()
12446
9979
  });
12447
9980
  function buildReportTaskAuditResultTool(connection, projectId) {
12448
9981
  return defineTool(
12449
9982
  "report_task_audit_result",
12450
9983
  "Persist one audited task's grades (call once per task after grading it). Pass error instead to mark the audit failed when the evidence is unusable.",
12451
9984
  {
12452
- taskId: z19.string().describe("The audited task's id (NOT slug)"),
12453
- summary: z19.string().describe("3-6 sentences: what went well, what was wasted"),
12454
- turnGrades: z19.array(TURN_GRADE_SHAPE),
12455
- planningAccuracy: z19.number().min(0).max(1).nullable(),
12456
- buildingAccuracy: z19.number().min(0).max(1).nullable(),
12457
- humanAccuracy: z19.number().min(0).max(1).nullable(),
12458
- planningCorrect: z19.number().int().min(0),
12459
- planningNeutral: z19.number().int().min(0),
12460
- planningBlunder: z19.number().int().min(0),
12461
- buildingCorrect: z19.number().int().min(0),
12462
- buildingNeutral: z19.number().int().min(0),
12463
- buildingBlunder: z19.number().int().min(0),
12464
- humanCorrect: z19.number().int().min(0),
12465
- humanNeutral: z19.number().int().min(0),
12466
- humanBlunder: z19.number().int().min(0),
12467
- humanEvaluations: z19.array(HUMAN_EVAL_SHAPE).optional(),
12468
- suggestionIds: z19.array(z19.string()).describe("Suggestion ids filed for this task, or []"),
12469
- auditCostUsd: z19.number().nullable(),
12470
- model: z19.string().nullable().describe("The model you are running as"),
12471
- error: z19.string().optional().describe("Set ONLY to mark this task's audit failed")
9985
+ taskId: z9.string().describe("The audited task's id (NOT slug)"),
9986
+ summary: z9.string().describe("3-6 sentences: what went well, what was wasted"),
9987
+ turnGrades: z9.array(TURN_GRADE_SHAPE),
9988
+ planningAccuracy: z9.number().min(0).max(1).nullable(),
9989
+ buildingAccuracy: z9.number().min(0).max(1).nullable(),
9990
+ humanAccuracy: z9.number().min(0).max(1).nullable(),
9991
+ planningCorrect: z9.number().int().min(0),
9992
+ planningNeutral: z9.number().int().min(0),
9993
+ planningBlunder: z9.number().int().min(0),
9994
+ buildingCorrect: z9.number().int().min(0),
9995
+ buildingNeutral: z9.number().int().min(0),
9996
+ buildingBlunder: z9.number().int().min(0),
9997
+ humanCorrect: z9.number().int().min(0),
9998
+ humanNeutral: z9.number().int().min(0),
9999
+ humanBlunder: z9.number().int().min(0),
10000
+ humanEvaluations: z9.array(HUMAN_EVAL_SHAPE).optional(),
10001
+ suggestionIds: z9.array(z9.string()).describe("Suggestion ids filed for this task, or []"),
10002
+ auditCostUsd: z9.number().nullable(),
10003
+ model: z9.string().nullable().describe("The model you are running as"),
10004
+ error: z9.string().optional().describe("Set ONLY to mark this task's audit failed")
12472
10005
  },
12473
10006
  async (input) => {
12474
10007
  try {
@@ -12950,34 +10483,34 @@ function connectedToolsFor(connection, context) {
12950
10483
  // src/tools/code-review-tools.ts
12951
10484
  import { execFile } from "child_process";
12952
10485
  import { promisify } from "util";
12953
- import { z as z20 } from "zod";
10486
+ import { z as z10 } from "zod";
12954
10487
  async function endReviewSession(connection, reason) {
12955
10488
  await connection.call("endReviewSession", {
12956
10489
  sessionId: connection.sessionId,
12957
10490
  reason
12958
10491
  });
12959
10492
  }
12960
- var RISK_LEVELS2 = ["critical", "high", "medium", "low"];
12961
- var reviewedShaSchema = z20.string().regex(/^[0-9a-f]{40}$/i).describe("REQUIRED. The full 40-character commit SHA this verdict reviews.");
10493
+ var RISK_LEVELS = ["critical", "high", "medium", "low"];
10494
+ var reviewedShaSchema = z10.string().regex(/^[0-9a-f]{40}$/i).describe("REQUIRED. The full 40-character commit SHA this verdict reviews.");
12962
10495
  var riskDescription = "REQUIRED. The risk level this change carries, judged by the surface area it touches: critical = touches critical/foundational surface, high = important surface, medium = moderate, low = small/isolated. Set this on every verdict. You have authority to override a risk level already set on the task if you disagree with it.";
12963
- var ReviewGuideToolSchema = z20.strictObject({
12964
- reviewedSha: z20.string().regex(/^[0-9a-f]{40}$/i).describe(
10496
+ var ReviewGuideToolSchema = z10.strictObject({
10497
+ reviewedSha: z10.string().regex(/^[0-9a-f]{40}$/i).describe(
12965
10498
  "REQUIRED. The PR's current head as a full 40-char SHA. Run `git rev-parse HEAD` immediately before this call \u2014 never extend an abbreviated hash into 40 characters."
12966
10499
  ),
12967
- overview: z20.string().min(1).max(6e4).describe("REQUIRED. Plain-text walkthrough intro, max 3000 characters. Keep it short."),
12968
- sections: z20.array(
12969
- z20.strictObject({
12970
- title: z20.string().min(1).max(160),
12971
- explanation: z20.string().min(1).max(2e3),
12972
- classification: z20.enum(["core", "supporting"]).optional(),
12973
- files: z20.array(
12974
- z20.strictObject({
12975
- path: z20.string().min(1).max(500).describe(
10500
+ overview: z10.string().min(1).max(6e4).describe("REQUIRED. Plain-text walkthrough intro, max 3000 characters. Keep it short."),
10501
+ sections: z10.array(
10502
+ z10.strictObject({
10503
+ title: z10.string().min(1).max(160),
10504
+ explanation: z10.string().min(1).max(2e3),
10505
+ classification: z10.enum(["core", "supporting"]).optional(),
10506
+ files: z10.array(
10507
+ z10.strictObject({
10508
+ path: z10.string().min(1).max(500).describe(
12976
10509
  "A file the PR's diff actually changed. Context files you merely read are rejected."
12977
10510
  ),
12978
- startLine: z20.number().int().positive().max(1e6).optional(),
12979
- endLine: z20.number().int().positive().max(1e6).optional(),
12980
- hunkHeader: z20.string().min(1).max(300).optional().describe(
10511
+ startLine: z10.number().int().positive().max(1e6).optional(),
10512
+ endLine: z10.number().int().positive().max(1e6).optional(),
10513
+ hunkHeader: z10.string().min(1).max(300).optional().describe(
12981
10514
  "Optional anchor, matched byte-exactly against the full hunk header line from `git diff` INCLUDING the context text after the second @@. Copy it verbatim from `git diff <base>..HEAD -- <file> | grep '^@@'`, or omit anchors entirely (path-only entries always validate)."
12982
10515
  )
12983
10516
  })
@@ -13061,8 +10594,8 @@ function buildApproveCodeReviewTool(connection) {
13061
10594
  "Approve the code review and exit. Use when the diff passes all review criteria. Requires a summary and a risk level \u2014 for changes, use request_code_changes with a structured issues[] list.",
13062
10595
  {
13063
10596
  reviewedSha: reviewedShaSchema,
13064
- summary: z20.string().describe("Brief summary of what was reviewed and why it looks good"),
13065
- risk: z20.enum(RISK_LEVELS2).describe(riskDescription)
10597
+ summary: z10.string().describe("Brief summary of what was reviewed and why it looks good"),
10598
+ risk: z10.enum(RISK_LEVELS).describe(riskDescription)
13066
10599
  },
13067
10600
  async ({ reviewedSha, summary, risk }) => {
13068
10601
  const content = `**Code Review: Approved** :white_check_mark:
@@ -13093,16 +10626,16 @@ function buildRequestCodeChangesTool(connection) {
13093
10626
  "Request changes during code review and exit. Use when substantive issues must be fixed before merge. Each issue: { file, line?, severity: critical|major|minor, description }.",
13094
10627
  {
13095
10628
  reviewedSha: reviewedShaSchema,
13096
- issues: z20.array(
13097
- z20.object({
13098
- file: z20.string().describe("File path where the issue was found"),
13099
- line: z20.number().optional().describe("Line number (if applicable)"),
13100
- severity: z20.enum(["critical", "major", "minor"]).describe("Issue severity"),
13101
- description: z20.string().describe("What is wrong and how to fix it")
10629
+ issues: z10.array(
10630
+ z10.object({
10631
+ file: z10.string().describe("File path where the issue was found"),
10632
+ line: z10.number().optional().describe("Line number (if applicable)"),
10633
+ severity: z10.enum(["critical", "major", "minor"]).describe("Issue severity"),
10634
+ description: z10.string().describe("What is wrong and how to fix it")
13102
10635
  })
13103
10636
  ).describe("List of issues found during review"),
13104
- summary: z20.string().describe("Brief overall summary of the review findings"),
13105
- risk: z20.enum(RISK_LEVELS2).describe(riskDescription)
10637
+ summary: z10.string().describe("Brief overall summary of the review findings"),
10638
+ risk: z10.enum(RISK_LEVELS).describe(riskDescription)
13106
10639
  },
13107
10640
  async ({ reviewedSha, issues, summary, risk }) => {
13108
10641
  const issueLines = issues.map((issue) => {
@@ -13145,41 +10678,38 @@ function buildCodeReviewTools(connection) {
13145
10678
  // src/tools/index.ts
13146
10679
  function getTaskModeTools(agentMode, connection) {
13147
10680
  if (agentMode === "discovery" || agentMode === "auto" || agentMode === "building" || agentMode === "chat") {
13148
- return buildPmTools(connection, { includePackTools: false });
10681
+ return buildPmTools(connection);
13149
10682
  }
13150
10683
  return [];
13151
10684
  }
13152
10685
  function getModeTools(agentMode, connection, config, context) {
13153
- if (config.mode === "pack") {
13154
- return buildPmTools(connection, {
13155
- includePackTools: config.packExecution === "fan-out"
13156
- });
13157
- }
10686
+ if (config.mode === "pack") return buildPmTools(connection);
13158
10687
  if (config.mode === "task") return getTaskModeTools(agentMode, connection);
13159
- const packToolsFor = (isParent) => ({
13160
- includePackTools: !!isParent && config.packExecution === "fan-out"
13161
- });
13162
10688
  switch (agentMode) {
13163
10689
  case "building":
13164
- return context?.isParentTask ? buildPmTools(connection, packToolsFor(true)) : [];
10690
+ return context?.isParentTask ? buildPmTools(connection) : [];
13165
10691
  case "review":
13166
10692
  case "auto":
13167
10693
  case "discovery":
13168
10694
  case "help":
13169
- return buildPmTools(connection, packToolsFor(context?.isParentTask));
10695
+ return buildPmTools(connection);
13170
10696
  default:
13171
- return config.mode === "pm" ? buildPmTools(connection, { includePackTools: false }) : [];
10697
+ return config.mode === "pm" ? buildPmTools(connection) : [];
13172
10698
  }
13173
10699
  }
13174
10700
  function buildPrGuideToolsFor(effectiveMode, connection, config, context) {
13175
10701
  const isLeafBuild = effectiveMode === "building" || effectiveMode === "auto" || effectiveMode === "chat";
13176
- const isSinglePodPack = config.mode === "pack" && config.packExecution !== "fan-out";
13177
- return (isLeafBuild || isSinglePodPack) && (isSinglePodPack || !context?.isParentTask) ? [
10702
+ const isPackRunner = config.mode === "pack";
10703
+ return (isLeafBuild || isPackRunner) && (isPackRunner || !context?.isParentTask) ? [
13178
10704
  buildPublishReviewGuideTool(connection, {
13179
10705
  resolveHeadSha: () => resolveGitHeadSha(config.workspaceDir)
13180
10706
  })
13181
10707
  ] : [];
13182
10708
  }
10709
+ function ciWaitToolsFor(effectiveMode, connection, config) {
10710
+ const isPackRunner = config.mode === "pack";
10711
+ return effectiveMode === "review" && !isPackRunner ? [] : [buildWaitForChecksTool(connection)];
10712
+ }
13183
10713
  var ALWAYS_LOADED_TOOLS = /* @__PURE__ */ new Set([
13184
10714
  // Every mode/session
13185
10715
  "post_to_chat",
@@ -13218,26 +10748,29 @@ var ALWAYS_LOADED_TOOLS = /* @__PURE__ */ new Set([
13218
10748
  ]);
13219
10749
  var ORCHESTRATION_PROMOTED_TOOLS = /* @__PURE__ */ new Set([
13220
10750
  "list_subtasks",
13221
- "update_subtask",
13222
- "start_child_cloud_build",
13223
- "stop_child_build",
13224
- "approve_and_merge_pr"
10751
+ "update_subtask"
13225
10752
  ]);
13226
10753
  var BUILDING_PROMOTED_TOOLS = /* @__PURE__ */ new Set([
13227
10754
  "upload_attachment",
13228
10755
  "get_attachment",
13229
10756
  "create_suggestion",
13230
10757
  "create_follow_up_task",
13231
- "list_manual_tests"
10758
+ "list_manual_tests",
10759
+ // Every PR-opening session waits on CI at least once; deferring the wait
10760
+ // tool behind ToolSearch is one more reason to fall back to a poll loop.
10761
+ "wait_for_checks"
13232
10762
  ]);
13233
10763
  var REVIEW_PROMOTED_TOOLS = /* @__PURE__ */ new Set(["create_suggestion"]);
13234
- var PACK_PROMOTED_TOOLS = /* @__PURE__ */ new Set(["get_execution_logs"]);
10764
+ var PACK_PROMOTED_TOOLS = /* @__PURE__ */ new Set([
10765
+ "get_execution_logs",
10766
+ "wait_for_checks"
10767
+ ]);
13235
10768
  function glossaryToolsFor(connection, config, context) {
13236
10769
  return context?.projectId ? buildGlossaryTools(connection, context.projectId, config.taskId, config.workspaceDir) : [];
13237
10770
  }
13238
- function promotedToolsFor(effectiveMode, isPack, isProjectAgent, isSinglePodPack = false) {
10771
+ function promotedToolsFor(effectiveMode, isPack, isProjectAgent, isPackRunner = false) {
13239
10772
  const names = /* @__PURE__ */ new Set();
13240
- if (effectiveMode === "building" || effectiveMode === "auto" || isSinglePodPack) {
10773
+ if (effectiveMode === "building" || effectiveMode === "auto" || isPackRunner) {
13241
10774
  for (const name of BUILDING_PROMOTED_TOOLS) names.add(name);
13242
10775
  }
13243
10776
  if (effectiveMode === "review") {
@@ -13263,6 +10796,7 @@ function buildConveyorTools(connection, config, context, agentMode) {
13263
10796
  const discoveryTools = effectiveMode === "discovery" || effectiveMode === "auto" || effectiveMode === "building" || effectiveMode === "chat" ? buildDiscoveryTools(connection) : [];
13264
10797
  const codeReviewTools = effectiveMode === "review" ? buildCodeReviewTools(connection) : [];
13265
10798
  const prGuideTools = buildPrGuideToolsFor(effectiveMode, connection, config, context);
10799
+ const ciWaitTools = ciWaitToolsFor(effectiveMode, connection, config);
13266
10800
  const handoffTools = config.mode === "pm" && (effectiveMode === "discovery" || effectiveMode === "auto") ? [buildHandoffTool(connection)] : [];
13267
10801
  const emergencyTools = [buildForceUpdateTaskStatusTool(connection)];
13268
10802
  const glossaryTools = glossaryToolsFor(connection, config, context);
@@ -13275,17 +10809,13 @@ function buildConveyorTools(connection, config, context, agentMode) {
13275
10809
  ...discoveryTools,
13276
10810
  ...codeReviewTools,
13277
10811
  ...prGuideTools,
10812
+ ...ciWaitTools,
13278
10813
  ...handoffTools,
13279
10814
  ...glossaryTools,
13280
10815
  ...connectedTools,
13281
10816
  ...emergencyTools
13282
10817
  ],
13283
- promotedToolsFor(
13284
- effectiveMode,
13285
- isPack,
13286
- config.mode === "pm",
13287
- config.mode === "pack" && config.packExecution !== "fan-out"
13288
- )
10818
+ promotedToolsFor(effectiveMode, isPack, config.mode === "pm", config.mode === "pack")
13289
10819
  );
13290
10820
  }
13291
10821
  function createConveyorMcpServer(harness, connection, config, context, agentMode) {
@@ -13895,7 +11425,7 @@ function buildNoOpKeepAliveMessage(noOpCount) {
13895
11425
  if (process.env.CONVEYOR_TUI === "codex") {
13896
11426
  return `Conveyor blocked this call: it is a no-op keep-alive (${noOpCount} this session). If a required command is running, resume its session for the exit result. Otherwise do real work or end the turn.`;
13897
11427
  }
13898
- return `Conveyor blocked this call: it is a no-op keep-alive (${noOpCount} this session). You do not need to emit tool calls to stay alive. Ending your turn with NO tool call is safe and expected while waiting: the pod stays up and the completion notification re-invokes you. If nothing is running, do real work or end the turn. For a long wait with no notification source, use Monitor or ScheduleWakeup instead of filler commands.`;
11428
+ return `Conveyor blocked this call: it is a no-op keep-alive (${noOpCount} this session). You do not need to emit tool calls to stay alive. Ending your turn with NO tool call is safe and expected while waiting: the pod stays up and the completion notification re-invokes you. If nothing is running, do real work or end the turn. Waiting on CI is wait_for_checks, then end the turn. For any other long wait with no notification source, use Monitor or ScheduleWakeup instead of filler commands.`;
13899
11429
  }
13900
11430
  function buildRepeatLoopChatMessage(repeatCount, forceStopped) {
13901
11431
  if (forceStopped) {
@@ -14347,6 +11877,11 @@ function buildQueryOptions(host, context) {
14347
11877
  context,
14348
11878
  {
14349
11879
  ...host.config,
11880
+ // ModeController's live value, NOT the boot-frozen `config.isAuto` the
11881
+ // spread carries: the pack gate reads this, and the turn prompt below
11882
+ // gates on `host.isAuto`. Letting the two read different sources is how
11883
+ // a codespace parent card got "you cannot write or edit files" beside
11884
+ // "run /conveyor-build".
14350
11885
  isAuto: host.isAuto,
14351
11886
  runtimeTui: process.env.CONVEYOR_TUI
14352
11887
  },
@@ -14456,7 +11991,7 @@ async function buildFollowUpPrompt(host, context, followUpContent) {
14456
11991
  const followUpImages = typeof followUpContent === "string" ? [] : followUpContent.filter(
14457
11992
  (b) => b.type === "image"
14458
11993
  );
14459
- const textPrompt = isPmMode ? `${await buildInitialPrompt(host.config.mode, context, host.isAuto, host.agentMode, host.config.packExecution)}
11994
+ const textPrompt = isPmMode ? `${await buildInitialPrompt(host.config.mode, context, host.isAuto, host.agentMode)}
14460
11995
 
14461
11996
  ---
14462
11997
 
@@ -14666,8 +12201,7 @@ async function prewarmInitialTuiQuery(host, context) {
14666
12201
  host.config.mode,
14667
12202
  context,
14668
12203
  host.isAuto,
14669
- host.agentMode,
14670
- host.config.packExecution
12204
+ host.agentMode
14671
12205
  );
14672
12206
  const { appendSystemPrompt } = selectInitialPromptInput(
14673
12207
  promptDelivery,
@@ -14699,8 +12233,7 @@ async function runPrefilledFollowUp(host, context, options, resume, followUpCont
14699
12233
  host.config.mode,
14700
12234
  context,
14701
12235
  host.isAuto,
14702
- host.agentMode,
14703
- host.config.packExecution
12236
+ host.agentMode
14704
12237
  );
14705
12238
  queryOptions.appendSystemPrompt = [queryOptions.appendSystemPrompt, initialPrompt].filter(Boolean).join("\n\n").slice(0, APPEND_SYSTEM_PROMPT_MAX_CHARS);
14706
12239
  }
@@ -14771,8 +12304,7 @@ async function runInitialQuery(host, context, options, resume, promptDelivery) {
14771
12304
  host.config.mode,
14772
12305
  context,
14773
12306
  host.isAuto,
14774
- host.agentMode,
14775
- host.config.packExecution
12307
+ host.agentMode
14776
12308
  );
14777
12309
  const { prompt, appendSystemPrompt } = selectInitialPromptInput(
14778
12310
  promptDelivery,
@@ -14825,13 +12357,7 @@ async function buildRetryQuery(host, context, options, lastErrorWasImage) {
14825
12357
  );
14826
12358
  }
14827
12359
  const retryPrompt = buildMultimodalPrompt(
14828
- await buildInitialPrompt(
14829
- host.config.mode,
14830
- context,
14831
- host.isAuto,
14832
- host.agentMode,
14833
- host.config.packExecution
14834
- ),
12360
+ await buildInitialPrompt(host.config.mode, context, host.isAuto, host.agentMode),
14835
12361
  context,
14836
12362
  lastErrorWasImage || !supportsImageBlocks(host.harnessKind)
14837
12363
  );
@@ -14857,13 +12383,7 @@ async function handleAuthError(context, host, options) {
14857
12383
  context.claudeSessionId = null;
14858
12384
  host.connection.storeSessionId("");
14859
12385
  const freshPrompt = buildMultimodalPrompt(
14860
- await buildInitialPrompt(
14861
- host.config.mode,
14862
- context,
14863
- host.isAuto,
14864
- host.agentMode,
14865
- host.config.packExecution
14866
- ),
12386
+ await buildInitialPrompt(host.config.mode, context, host.isAuto, host.agentMode),
14867
12387
  context,
14868
12388
  !supportsImageBlocks(host.harnessKind)
14869
12389
  );
@@ -14878,13 +12398,7 @@ async function handleStaleSession(context, host, options) {
14878
12398
  context.claudeSessionId = null;
14879
12399
  host.connection.storeSessionId("");
14880
12400
  const freshPrompt = buildMultimodalPrompt(
14881
- await buildInitialPrompt(
14882
- host.config.mode,
14883
- context,
14884
- host.isAuto,
14885
- host.agentMode,
14886
- host.config.packExecution
14887
- ),
12401
+ await buildInitialPrompt(host.config.mode, context, host.isAuto, host.agentMode),
14888
12402
  context,
14889
12403
  !supportsImageBlocks(host.harnessKind)
14890
12404
  );
@@ -14978,13 +12492,7 @@ async function handleUsageCapRejection(context, host, options, rateLimitType, re
14978
12492
  context.claudeSessionId = null;
14979
12493
  host.connection.storeSessionId("");
14980
12494
  const freshPrompt = buildMultimodalPrompt(
14981
- await buildInitialPrompt(
14982
- host.config.mode,
14983
- context,
14984
- host.isAuto,
14985
- host.agentMode,
14986
- host.config.packExecution
14987
- ),
12495
+ await buildInitialPrompt(host.config.mode, context, host.isAuto, host.agentMode),
14988
12496
  context,
14989
12497
  !supportsImageBlocks(host.harnessKind)
14990
12498
  );
@@ -16568,8 +14076,8 @@ var SessionRunner = class _SessionRunner {
16568
14076
  "[conveyor-agent] Completed \u2014 entering dormant idle (staying connected)\n"
16569
14077
  );
16570
14078
  }
14079
+ this.pendingMessages = this.pendingMessages.filter((msg) => msg.source === "pty_passive");
16571
14080
  await this.flushWipNow("WIP: turn complete");
16572
- this.pendingMessages.length = 0;
16573
14081
  if (this._state !== "idle") await this.setState("idle");
16574
14082
  const remainingMs = Math.max(0, this.dormantDeadline - Date.now());
16575
14083
  this.lifecycle.startDormantTimer(remainingMs);
@@ -17075,10 +14583,8 @@ var SessionRunner = class _SessionRunner {
17075
14583
  taskToken: this.config.connection.taskToken,
17076
14584
  taskId: this.fullContext?.taskId ?? "",
17077
14585
  model: this.fullContext?.model ?? DEFAULT_SONNET_MODEL,
17078
- instructions: this.fullContext?.agentInstructions ?? "",
17079
14586
  workspaceDir: this.config.workspaceDir,
17080
14587
  mode: this.config.runnerMode,
17081
- packExecution: this.config.packExecution,
17082
14588
  isAuto: this.config.isAuto
17083
14589
  };
17084
14590
  const bridge = new QueryBridge(this.connection, this.mode, runnerConfig, {
@@ -17457,9 +14963,6 @@ function unshallowRepo(workspaceDir) {
17457
14963
  }
17458
14964
 
17459
14965
  export {
17460
- DEFAULT_SONNET_MODEL,
17461
- RUNNER_MODES,
17462
- parsePackExecution,
17463
14966
  isPermissionDeniedError,
17464
14967
  buildSynthesizedCredentials,
17465
14968
  claudeJsonPath,