@alecsibilia/luca 14.0.0-alpha.17 → 14.0.0-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/board/package.json +1 -1
- package/board/server/board-vocabulary-engine.ts +140 -0
- package/board/server/board-vocabulary-review.ts +98 -0
- package/board/server/board-vocabulary-run.ts +123 -0
- package/board/server/board-vocabulary-shapes.ts +132 -0
- package/board/server/board-vocabulary.ts +13 -444
- package/board/server/describe-record.ts +399 -0
- package/board/server/luca-version.ts +1 -1
- package/board/server/make-rows.ts +6 -702
- package/board/server/record-texts.ts +346 -0
- package/engine/agents/common-instructions.ts +29 -1
- package/engine/agents/prompt-sections.ts +95 -0
- package/engine/agents/rejoin-prompts.ts +135 -0
- package/engine/agents/role-prompts.ts +17 -216
- package/engine/agents/role-results.ts +65 -6
- package/engine/agents/writer-instructions.ts +25 -3
- package/engine/cli/config-reload-text.ts +39 -0
- package/engine/cli/decision-model-setup.ts +70 -0
- package/engine/cli/going-runs.ts +13 -3
- package/engine/cli/luca-run.ts +24 -1
- package/engine/cli/read-run.ts +81 -0
- package/engine/cli/run-args.ts +33 -6
- package/engine/cli/run-demo.ts +143 -0
- package/engine/cli/run-end.ts +184 -0
- package/engine/cli/run-modes-helpers.ts +81 -0
- package/engine/cli/run-modes.ts +35 -581
- package/engine/cli/unfinished-runs.ts +122 -0
- package/engine/config/config-fields.ts +127 -0
- package/engine/config/config-reload.ts +24 -0
- package/engine/config/engine-config-schema.ts +235 -0
- package/engine/config/engine-config.ts +51 -414
- package/engine/config/test-commands.ts +102 -0
- package/engine/core/agent-turn.ts +15 -1
- package/engine/core/build-action.ts +15 -3
- package/engine/core/build-launch.ts +15 -2
- package/engine/core/commit-in.ts +8 -1
- package/engine/core/decide-build-early.ts +108 -0
- package/engine/core/decide-build-ending.ts +145 -0
- package/engine/core/decide-build-tickets.ts +142 -0
- package/engine/core/decide-build-view.ts +103 -0
- package/engine/core/decide-build.ts +25 -332
- package/engine/core/decide-final-fix.ts +197 -0
- package/engine/core/decide-final-review.ts +15 -310
- package/engine/core/decide-red-green.ts +81 -7
- package/engine/core/decide-repo-wait.ts +129 -0
- package/engine/core/decide-run-budget.ts +20 -4
- package/engine/core/decide-stuck-helpers.ts +127 -0
- package/engine/core/decide-ticket-review.ts +9 -1
- package/engine/core/decide.ts +30 -3
- package/engine/core/engine-chores.ts +146 -0
- package/engine/core/environment-rerun.ts +83 -0
- package/engine/core/execute-action.ts +20 -0
- package/engine/core/execute-already-done-check.ts +1 -0
- package/engine/core/execute-build.ts +2 -0
- package/engine/core/execute-gates.ts +16 -1
- package/engine/core/execute-red-check.ts +11 -1
- package/engine/core/execute-repo-wait.ts +113 -0
- package/engine/core/execute-with-jev.ts +4 -0
- package/engine/core/execute.ts +24 -2
- package/engine/core/final-failure-cap.ts +38 -0
- package/engine/core/final-review-action.ts +124 -0
- package/engine/core/final-review-text.ts +18 -9
- package/engine/core/final-stuck-text.ts +4 -0
- package/engine/core/fix-loop-text.ts +13 -2
- package/engine/core/loop-caps.ts +7 -0
- package/engine/core/repo-wait-text.ts +68 -0
- package/engine/core/review-text.ts +55 -1
- package/engine/core/run-start.ts +17 -3
- package/engine/core/schedule-rules.ts +1 -0
- package/engine/core/stuck-detail-text.ts +6 -5
- package/engine/core/ticket-stuck-text.ts +5 -2
- package/engine/gates/gate-runner.ts +144 -73
- package/engine/gates/gate-schemas.ts +22 -2
- package/engine/gates/prepare-slots.ts +30 -10
- package/engine/gates/red-check.ts +65 -15
- package/engine/gates/test-runner.ts +58 -1
- package/engine/gates/test-slots.ts +44 -0
- package/engine/guards/permission-rules.ts +8 -1
- package/engine/guards/role-rules-helpers.ts +48 -0
- package/engine/guards/role-rules.ts +1 -1
- package/engine/guards/write-rules.ts +23 -4
- package/engine/journal/agent-entries.ts +29 -1
- package/engine/journal/agent-turn-replay.ts +109 -4
- package/engine/journal/final-review-replay.ts +9 -2
- package/engine/journal/final-review-round.ts +55 -7
- package/engine/journal/final-review-state.ts +17 -0
- package/engine/journal/gate-entries.ts +19 -1
- package/engine/journal/journal-record.ts +18 -0
- package/engine/journal/replay.ts +31 -0
- package/engine/journal/reply-replay.ts +5 -1
- package/engine/journal/repo-wait-entries.ts +42 -0
- package/engine/journal/run-state.ts +26 -0
- package/engine/journal/shared-failure-replay.ts +133 -0
- package/engine/journal/stuck-entries.ts +9 -4
- package/engine/journal/ticket-progress.ts +59 -3
- package/engine/journal/ticket-replay.ts +48 -15
- package/engine/journal/ticket-result-replay.ts +75 -8
- package/engine/journal/ticket-retry-replay.ts +4 -1
- package/engine/tracker/gh-retry.ts +217 -0
- package/engine/tracker/github-tracker.ts +193 -142
- package/package.json +1 -1
- package/skills/luca-unstick/SKILL.md +3 -1
package/CHANGELOG.md
CHANGED
package/board/package.json
CHANGED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The board's vocabulary for what the engine does while it builds: worktrees
|
|
3
|
+
* and installs, the checks, joins and rebases, stuck tickets, the PR, the
|
|
4
|
+
* decision model, agent messages, and plan limits and usage.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
|
|
8
|
+
import { JevJobSchema, TestRunSchema } from './board-vocabulary-shapes'
|
|
9
|
+
|
|
10
|
+
export const ENGINE_VOCABULARY = {
|
|
11
|
+
worktree_reset: z.looseObject({}),
|
|
12
|
+
/** The engine's install in a new worktree; `check` is null with no manifest. */
|
|
13
|
+
dependencies_installed: z.looseObject({
|
|
14
|
+
target: z.string(),
|
|
15
|
+
check: z
|
|
16
|
+
.looseObject({ command: z.string(), ok: z.boolean() })
|
|
17
|
+
.nullable(),
|
|
18
|
+
}),
|
|
19
|
+
red_check: z.looseObject({
|
|
20
|
+
ok: z.boolean(),
|
|
21
|
+
problems: z.array(z.string()).catch([]),
|
|
22
|
+
tests: TestRunSchema.optional(),
|
|
23
|
+
}),
|
|
24
|
+
leftover_scan: z.looseObject({
|
|
25
|
+
/** `red`, `green`, or `fix` (a review fix round). */
|
|
26
|
+
stage: z.string(),
|
|
27
|
+
hits: z.array(z.looseObject({ path: z.string() })),
|
|
28
|
+
}),
|
|
29
|
+
/** `stage` is `red`, `green`, or `fix` (a review fix round). */
|
|
30
|
+
commit_made: z.looseObject({ stage: z.string() }),
|
|
31
|
+
gates_run: z.looseObject({
|
|
32
|
+
target: z.string(),
|
|
33
|
+
ok: z.boolean(),
|
|
34
|
+
checks: z.array(z.looseObject({ name: z.string(), ok: z.boolean() })),
|
|
35
|
+
}),
|
|
36
|
+
ticket_joined: z.looseObject({
|
|
37
|
+
ok: z.boolean(),
|
|
38
|
+
error: z.string().optional(),
|
|
39
|
+
}),
|
|
40
|
+
/** A joined ticket sent back to be fixed on top of the run branch. */
|
|
41
|
+
ticket_rebased: z.looseObject({
|
|
42
|
+
/** `clash` or `join_gates`. */
|
|
43
|
+
cause: z.string(),
|
|
44
|
+
tests: z.array(z.string()).catch([]),
|
|
45
|
+
code: z.array(z.string()).catch([]),
|
|
46
|
+
}),
|
|
47
|
+
/**
|
|
48
|
+
* After a rebase, a test the joined tickets made wrong went back to the
|
|
49
|
+
* ticket's test-writer (#489). `round` counts these per ticket.
|
|
50
|
+
*/
|
|
51
|
+
tests_sent_back: z.looseObject({
|
|
52
|
+
round: z.number().int().min(1).catch(1),
|
|
53
|
+
bad_test: z
|
|
54
|
+
.looseObject({
|
|
55
|
+
file: z.string().catch(''),
|
|
56
|
+
name: z.string().catch(''),
|
|
57
|
+
})
|
|
58
|
+
.catch({ file: '', name: '' }),
|
|
59
|
+
joined: z.array(z.looseObject({ ticket: z.number().int() })).catch([]),
|
|
60
|
+
}),
|
|
61
|
+
run_branch_pushed: z.looseObject({ branch: z.string() }),
|
|
62
|
+
ticket_stuck: z.looseObject({
|
|
63
|
+
reason: z.string(),
|
|
64
|
+
detail: z.string().catch(''),
|
|
65
|
+
}),
|
|
66
|
+
/**
|
|
67
|
+
* The whole run is stuck (#435): `run_budget`, it used up its budget,
|
|
68
|
+
* or `pull_request_failed`, its PR didn't open (#508).
|
|
69
|
+
*/
|
|
70
|
+
run_stuck: z.looseObject({
|
|
71
|
+
reason: z.string(),
|
|
72
|
+
detail: z.string().catch(''),
|
|
73
|
+
}),
|
|
74
|
+
pull_request_opened: z.looseObject({
|
|
75
|
+
number: z.number().int(),
|
|
76
|
+
url: z.string(),
|
|
77
|
+
}),
|
|
78
|
+
/** The run's worktrees, removed at its end. Nothing visible changes. */
|
|
79
|
+
worktrees_removed: z.looseObject({
|
|
80
|
+
paths: z.array(z.string()).catch([]),
|
|
81
|
+
}),
|
|
82
|
+
// The decision model (Clef; Jev in older journals) in shadow mode:
|
|
83
|
+
// counted, never acted on.
|
|
84
|
+
jev_asked: JevJobSchema,
|
|
85
|
+
jev_answered: JevJobSchema,
|
|
86
|
+
jev_failed: JevJobSchema,
|
|
87
|
+
/**
|
|
88
|
+
* The decision model is off for the rest of the run (#534): no
|
|
89
|
+
* credentials, or Cloudflare turned them down. The board reads only the
|
|
90
|
+
* plain-words `detail`.
|
|
91
|
+
*/
|
|
92
|
+
decision_model_off: z.looseObject({ detail: z.string().catch('') }),
|
|
93
|
+
// Agent messages: sent inside an agent's turn, so they move nothing.
|
|
94
|
+
agent_message: z.looseObject({
|
|
95
|
+
from: z.string(),
|
|
96
|
+
to: z.string(),
|
|
97
|
+
text: z.string(),
|
|
98
|
+
status: z.enum(['queued', 'not_delivered', 'refused']),
|
|
99
|
+
reason: z.string().nullable().catch(null),
|
|
100
|
+
}),
|
|
101
|
+
agent_message_delivered: z.looseObject({
|
|
102
|
+
ids: z.array(z.string()).catch([]),
|
|
103
|
+
}),
|
|
104
|
+
/** Another process changed the shared `.git` during an agent's turn. */
|
|
105
|
+
shared_git_changed: z.looseObject({
|
|
106
|
+
role: z.string().catch(''),
|
|
107
|
+
changes: z.array(z.string()).catch([]),
|
|
108
|
+
}),
|
|
109
|
+
limit_wait_started: z.looseObject({
|
|
110
|
+
/** When the limit resets; `null` when not known. */
|
|
111
|
+
resets_at: z.string().nullable().catch(null),
|
|
112
|
+
/** When the engine wakes; always set by the engine. */
|
|
113
|
+
until: z.string().nullable().catch(null),
|
|
114
|
+
/** The window that was hit: `five_hour`, `seven_day`, ... */
|
|
115
|
+
rate_limit_type: z.string().nullable().catch(null),
|
|
116
|
+
}),
|
|
117
|
+
limit_wait_ended: z.looseObject({}),
|
|
118
|
+
/** The run paused at a usage line. */
|
|
119
|
+
usage_line_wait_started: z.looseObject({
|
|
120
|
+
/** `seven_day` or `five_hour`. */
|
|
121
|
+
window: z.string(),
|
|
122
|
+
/** The line, in percent. */
|
|
123
|
+
line: z.number(),
|
|
124
|
+
/** How full the window was, in percent. */
|
|
125
|
+
percent: z.number(),
|
|
126
|
+
/** When the window resets. */
|
|
127
|
+
resets_at: z.string(),
|
|
128
|
+
}),
|
|
129
|
+
/** The run carries on: `reason` is `reset` or `line_raised`. */
|
|
130
|
+
usage_line_wait_ended: z.looseObject({}),
|
|
131
|
+
/** How much of the plan a ticket (scope `ticket`) or the run used. */
|
|
132
|
+
usage_recorded: z.looseObject({
|
|
133
|
+
scope: z.enum(['ticket', 'run']),
|
|
134
|
+
ticket: z.number().int().nullable().catch(null),
|
|
135
|
+
/** Percents 0 to 100 per window, before and after, and the difference. */
|
|
136
|
+
windows: z
|
|
137
|
+
.record(z.string(), z.looseObject({ used: z.number() }))
|
|
138
|
+
.catch({}),
|
|
139
|
+
}),
|
|
140
|
+
} as const
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The board's vocabulary for the owner's replies and stuck work, the final
|
|
3
|
+
* review, memory, and the scheduler's steps.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
import { FindingCountsSchema } from './board-vocabulary-shapes'
|
|
8
|
+
|
|
9
|
+
export const REVIEW_VOCABULARY = {
|
|
10
|
+
/** The owner's reply on the spec issue, read by the engine (#366). */
|
|
11
|
+
reply_received: z.looseObject({
|
|
12
|
+
/** `ship` is only for a stuck final review. */
|
|
13
|
+
word: z.enum(['retry', 'skip', 'stop', 'ship']),
|
|
14
|
+
ticket: z.number().int().nullable().optional(),
|
|
15
|
+
}),
|
|
16
|
+
/** A reply the engine couldn't use; it answered why on the spec issue. */
|
|
17
|
+
reply_ignored: z.looseObject({
|
|
18
|
+
/** `no_ticket_named`, `not_stuck`, `nothing_stuck`, `ship_needs_final_review`, ... */
|
|
19
|
+
reason: z.string(),
|
|
20
|
+
}),
|
|
21
|
+
/** How the engine took a `retry` for a stuck ticket. */
|
|
22
|
+
ticket_retried: z.looseObject({
|
|
23
|
+
/** `resume`, `restart` (the ticket was edited), or `refused`. */
|
|
24
|
+
mode: z.enum(['resume', 'restart', 'refused']),
|
|
25
|
+
/** Why the edited ticket isn't ready to build, for `refused`. */
|
|
26
|
+
problems: z.array(z.string()).catch([]),
|
|
27
|
+
}),
|
|
28
|
+
/** `because`: `null` when the owner skipped it, else the skipped ticket it waits on. */
|
|
29
|
+
ticket_skipped: z.looseObject({
|
|
30
|
+
because: z.number().int().nullable().catch(null),
|
|
31
|
+
}),
|
|
32
|
+
/** A ticket whose work was already on the base branch (#484), done by `shas`. */
|
|
33
|
+
ticket_already_done: z.looseObject({
|
|
34
|
+
shas: z.array(z.string()).catch([]),
|
|
35
|
+
}),
|
|
36
|
+
/** The engine closed an already-done ticket, as the run opens no PR. */
|
|
37
|
+
ticket_closed: z.looseObject({}),
|
|
38
|
+
|
|
39
|
+
// The final review (#367). Its agent, gate, scan, commit, and push
|
|
40
|
+
// records are the usual kinds with `ticket: null`.
|
|
41
|
+
final_review_started: z.looseObject({}),
|
|
42
|
+
lens_started: z.looseObject({ lens: z.string() }),
|
|
43
|
+
lens_finished: z.looseObject({
|
|
44
|
+
lens: z.string(),
|
|
45
|
+
findings: FindingCountsSchema,
|
|
46
|
+
}),
|
|
47
|
+
final_review_fixing: z.looseObject({ round: z.number().int().min(0) }),
|
|
48
|
+
final_review_stuck: z.looseObject({
|
|
49
|
+
reason: z.string(),
|
|
50
|
+
detail: z.string().catch(''),
|
|
51
|
+
}),
|
|
52
|
+
final_review_passed: z.looseObject({}),
|
|
53
|
+
/** A `ship` reply to the stuck final review: the PR opens anyway. */
|
|
54
|
+
final_review_shipped: z.looseObject({}),
|
|
55
|
+
|
|
56
|
+
// Memory (#370): counted, and its learner shown. The learner's agent
|
|
57
|
+
// records are the usual kinds with `ticket: null` and role `learner`.
|
|
58
|
+
/** A search at a recall point: each vault's outcome, and what it showed. */
|
|
59
|
+
memory_recalled: z.looseObject({
|
|
60
|
+
/** `run_start`, `ticket`, `review`, or `fix_round`. */
|
|
61
|
+
point: z.string(),
|
|
62
|
+
vaults: z
|
|
63
|
+
.array(
|
|
64
|
+
z.looseObject({
|
|
65
|
+
vault: z.string(),
|
|
66
|
+
ok: z.boolean(),
|
|
67
|
+
error: z.string().nullable().catch(null),
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
.catch([]),
|
|
71
|
+
memories: z.array(z.unknown()).catch([]),
|
|
72
|
+
}),
|
|
73
|
+
/** The learner's memories saved, refused, or failed. */
|
|
74
|
+
memories_saved: z.looseObject({
|
|
75
|
+
saves: z
|
|
76
|
+
.array(
|
|
77
|
+
z.looseObject({
|
|
78
|
+
/** `added`, `updated`, `refused`, or `failed`. */
|
|
79
|
+
outcome: z.string(),
|
|
80
|
+
})
|
|
81
|
+
)
|
|
82
|
+
.catch([]),
|
|
83
|
+
}),
|
|
84
|
+
/** The engine gave up on the learner; the run ends without it. */
|
|
85
|
+
learning_skipped: z.looseObject({ reason: z.string().catch('') }),
|
|
86
|
+
/** With no PR, the new memories went on the spec issue. */
|
|
87
|
+
memories_reported: z.looseObject({ count: z.number().int().catch(0) }),
|
|
88
|
+
|
|
89
|
+
// The scheduler's steps (#403): what the engine is doing right now.
|
|
90
|
+
/**
|
|
91
|
+
* A step began. `key` is the scheduler's key (a ticket's number, `run`,
|
|
92
|
+
* `final`, `lens:<lens>`, ...); `step` its action type, plus `:<role>`
|
|
93
|
+
* for an agent's turn.
|
|
94
|
+
*/
|
|
95
|
+
step_started: z.looseObject({ key: z.string(), step: z.string() }),
|
|
96
|
+
/** The step on `key` settled. */
|
|
97
|
+
step_ended: z.looseObject({ key: z.string(), step: z.string() }),
|
|
98
|
+
} as const
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The board's vocabulary for the run and its tickets: how a run starts,
|
|
3
|
+
* resumes, and passes intake, the snapshots of the spec and its tickets, the
|
|
4
|
+
* baseline, and what each agent does and how its turn ends.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
AgentSessionSchema,
|
|
10
|
+
FindingListSchema,
|
|
11
|
+
FindingResponseListSchema,
|
|
12
|
+
LeftOutListSchema,
|
|
13
|
+
RulingListSchema,
|
|
14
|
+
SpecIssueSchema,
|
|
15
|
+
TestRunSchema,
|
|
16
|
+
WorktreeSchema,
|
|
17
|
+
} from './board-vocabulary-shapes'
|
|
18
|
+
|
|
19
|
+
export const RUN_VOCABULARY = {
|
|
20
|
+
run_started: z.looseObject({
|
|
21
|
+
spec_number: z.number().int(),
|
|
22
|
+
/** The engine config; the board reads only its run budget. */
|
|
23
|
+
config: z
|
|
24
|
+
.looseObject({
|
|
25
|
+
run_budget_tokens: z
|
|
26
|
+
.number()
|
|
27
|
+
.int()
|
|
28
|
+
.positive()
|
|
29
|
+
.optional()
|
|
30
|
+
.catch(undefined),
|
|
31
|
+
})
|
|
32
|
+
.optional()
|
|
33
|
+
.catch(undefined),
|
|
34
|
+
/** Luca's version the run started on (#460); older journals have none. */
|
|
35
|
+
luca_version: z.string().nullable().catch(null),
|
|
36
|
+
}),
|
|
37
|
+
/** The engine started again on the run's journal, on Luca's `luca_version`. */
|
|
38
|
+
engine_resumed: z.looseObject({ luca_version: z.string() }),
|
|
39
|
+
/**
|
|
40
|
+
* A resume took the repo's new build fields (#516); the board reads
|
|
41
|
+
* only which fields changed.
|
|
42
|
+
*/
|
|
43
|
+
config_reloaded: z.looseObject({
|
|
44
|
+
changes: z.array(z.looseObject({ field: z.string() })),
|
|
45
|
+
}),
|
|
46
|
+
/** A resume kept the run's config, as the repo's couldn't be used. */
|
|
47
|
+
config_reload_refused: z.looseObject({ reason: z.string() }),
|
|
48
|
+
intake_read: z.looseObject({ spec: SpecIssueSchema }),
|
|
49
|
+
intake_refused: z.looseObject({
|
|
50
|
+
problems: z.array(
|
|
51
|
+
z.looseObject({
|
|
52
|
+
ticket: z.number().nullable(),
|
|
53
|
+
missing: z.array(z.string()),
|
|
54
|
+
})
|
|
55
|
+
),
|
|
56
|
+
}),
|
|
57
|
+
/**
|
|
58
|
+
* The run ends with nothing to do; `already_done` is empty at intake
|
|
59
|
+
* (#484), and `left_out` names the tickets left for a person (#499).
|
|
60
|
+
*/
|
|
61
|
+
nothing_to_do: z.looseObject({
|
|
62
|
+
already_done: z.array(z.number().int()).catch([]).default([]),
|
|
63
|
+
left_out: LeftOutListSchema,
|
|
64
|
+
}),
|
|
65
|
+
spec_snapshot: z.looseObject({
|
|
66
|
+
spec: SpecIssueSchema,
|
|
67
|
+
ticket_order: z.array(z.number()).catch([]),
|
|
68
|
+
/** The tickets left out for a person (#499); older journals have none. */
|
|
69
|
+
left_out: LeftOutListSchema,
|
|
70
|
+
}),
|
|
71
|
+
ticket_snapshot: z.looseObject({
|
|
72
|
+
number: z.number().int(),
|
|
73
|
+
title: z.string(),
|
|
74
|
+
labels: z.array(z.string()).catch([]),
|
|
75
|
+
blockers: z.array(z.number().int()).catch([]),
|
|
76
|
+
}),
|
|
77
|
+
run_branch_created: WorktreeSchema,
|
|
78
|
+
ticket_worktree_created: WorktreeSchema,
|
|
79
|
+
baseline_tests: TestRunSchema,
|
|
80
|
+
/** Another ticket's baseline, taken from the same run-branch commit (#404). */
|
|
81
|
+
baseline_reused: z.looseObject({ from_ticket: z.number().int() }),
|
|
82
|
+
agent_started: z.looseObject({
|
|
83
|
+
role: z.string(),
|
|
84
|
+
/** The session a fix-loop or failed-try follow-up went to. */
|
|
85
|
+
follow_up_of: z.string().nullable().catch(null),
|
|
86
|
+
}),
|
|
87
|
+
agent_finished: z.looseObject({
|
|
88
|
+
role: z.string(),
|
|
89
|
+
result: z
|
|
90
|
+
.looseObject({
|
|
91
|
+
outcome: z.string().optional(),
|
|
92
|
+
verdict: z.string().optional(),
|
|
93
|
+
findings: FindingListSchema.optional(),
|
|
94
|
+
/** The ticket-reviewer's rulings on declined findings. */
|
|
95
|
+
rulings: RulingListSchema,
|
|
96
|
+
/** A review fixer's answer to each finding it got. */
|
|
97
|
+
finding_responses: FindingResponseListSchema,
|
|
98
|
+
/** The learner's proposed memories (#370). */
|
|
99
|
+
memories: z.array(z.unknown()).catch([]).optional(),
|
|
100
|
+
/** The shown memories the learner said helped. */
|
|
101
|
+
helped: z.array(z.string()).catch([]).optional(),
|
|
102
|
+
})
|
|
103
|
+
.catch({ rulings: [], finding_responses: [] }),
|
|
104
|
+
}),
|
|
105
|
+
agent_failed: z.looseObject({
|
|
106
|
+
role: z.string(),
|
|
107
|
+
error: z.string(),
|
|
108
|
+
/** `agent`, `result`, `guard`, or `engine`. */
|
|
109
|
+
failure: z.string().catch('agent'),
|
|
110
|
+
}),
|
|
111
|
+
agent_session: z.looseObject({
|
|
112
|
+
role: z.string(),
|
|
113
|
+
session: AgentSessionSchema,
|
|
114
|
+
}),
|
|
115
|
+
run_stopped: z.looseObject({
|
|
116
|
+
reason: z.string(),
|
|
117
|
+
role: z.string().nullable().catch(null),
|
|
118
|
+
/** A billing stop: the session would bill per token. */
|
|
119
|
+
billing: z.boolean().catch(false),
|
|
120
|
+
/** A stop after the same step crashed again and again. */
|
|
121
|
+
crashed: z.boolean().catch(false),
|
|
122
|
+
}),
|
|
123
|
+
} as const
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pieces the board's kind schemas are built from: findings and rulings,
|
|
3
|
+
* test runs, worktrees, the tickets left out for a person, the spec issue, an
|
|
4
|
+
* agent's session with its tokens and rate-limit readings, and the decision
|
|
5
|
+
* model's jobs. Loose on purpose, like the kinds: extra fields are fine.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod'
|
|
8
|
+
|
|
9
|
+
export const FindingCountsSchema = z.looseObject({
|
|
10
|
+
blocker: z.number().int().min(0).catch(0),
|
|
11
|
+
should_fix: z.number().int().min(0).catch(0),
|
|
12
|
+
nit: z.number().int().min(0).catch(0),
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export const FindingListSchema = z.array(
|
|
16
|
+
z.looseObject({
|
|
17
|
+
id: z.string().catch(''),
|
|
18
|
+
severity: z.enum(['blocker', 'should_fix', 'nit']),
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
/** A re-reviewer's ruling on a fixer's "won't fix" of a review finding. */
|
|
23
|
+
export const RulingListSchema = z
|
|
24
|
+
.array(
|
|
25
|
+
z.looseObject({
|
|
26
|
+
finding_id: z.string(),
|
|
27
|
+
ruling: z.enum(['accepted', 'rejected']),
|
|
28
|
+
reason: z.string().catch(''),
|
|
29
|
+
})
|
|
30
|
+
)
|
|
31
|
+
.catch([])
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A review fixer's answer to each finding: `fixed`, `wont_fix`, or
|
|
35
|
+
* `other_role` (the other writer role gets it, #616).
|
|
36
|
+
*/
|
|
37
|
+
export const FindingResponseListSchema = z
|
|
38
|
+
.array(
|
|
39
|
+
z.looseObject({
|
|
40
|
+
finding_id: z.string(),
|
|
41
|
+
response: z.enum(['fixed', 'wont_fix', 'other_role']),
|
|
42
|
+
reason: z.string().catch(''),
|
|
43
|
+
})
|
|
44
|
+
)
|
|
45
|
+
.catch([])
|
|
46
|
+
|
|
47
|
+
export const TestRunSchema = z.looseObject({
|
|
48
|
+
cases: z.array(z.looseObject({ status: z.string() })),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
export const WorktreeSchema = z.looseObject({ branch: z.string() })
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The open tickets intake left out for a person (#499): `for_a_person`, or
|
|
55
|
+
* `waits_on_person` with the tickets for a person it `waits_on`, and the
|
|
56
|
+
* blockers it waits `through`. Missing in older journals.
|
|
57
|
+
*/
|
|
58
|
+
export const LeftOutListSchema = z
|
|
59
|
+
.array(
|
|
60
|
+
z.looseObject({
|
|
61
|
+
number: z.number().int(),
|
|
62
|
+
title: z.string().catch(''),
|
|
63
|
+
reason: z.enum(['for_a_person', 'waits_on_person']),
|
|
64
|
+
waits_on: z.array(z.number().int()).catch([]).default([]),
|
|
65
|
+
through: z.array(z.number().int()).catch([]).default([]),
|
|
66
|
+
})
|
|
67
|
+
)
|
|
68
|
+
.catch([])
|
|
69
|
+
.default([])
|
|
70
|
+
|
|
71
|
+
export type LeftOutList = z.infer<typeof LeftOutListSchema>
|
|
72
|
+
|
|
73
|
+
const tokenCount = z.number().min(0).catch(0)
|
|
74
|
+
|
|
75
|
+
/** One plan window's reading: `utilization` 0 to 1, `resetsAt` in seconds. */
|
|
76
|
+
const WindowReadingSchema = z.looseObject({
|
|
77
|
+
utilization: z.number().optional().catch(undefined),
|
|
78
|
+
resetsAt: z.number().optional().catch(undefined),
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* One `rate_limit_event`'s info, as the Claude Agent SDK sends it:
|
|
83
|
+
* `utilization` is 0 to 1, `resetsAt` is in seconds since the epoch. Real
|
|
84
|
+
* readings carry no top-level `utilization`; each window's is in
|
|
85
|
+
* `unifiedWindows`, keyed by window name (`five_hour`, `seven_day`, ...).
|
|
86
|
+
*/
|
|
87
|
+
const RateLimitReadingSchema = z.looseObject({
|
|
88
|
+
rateLimitType: z.string().optional().catch(undefined),
|
|
89
|
+
utilization: z.number().optional().catch(undefined),
|
|
90
|
+
resetsAt: z.number().optional().catch(undefined),
|
|
91
|
+
unifiedWindows: z
|
|
92
|
+
.record(z.string(), WindowReadingSchema.catch({}))
|
|
93
|
+
.optional()
|
|
94
|
+
.catch(undefined),
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const TokensSchema = z
|
|
98
|
+
.looseObject({
|
|
99
|
+
input_tokens: tokenCount,
|
|
100
|
+
output_tokens: tokenCount,
|
|
101
|
+
cache_read_input_tokens: tokenCount,
|
|
102
|
+
cache_creation_input_tokens: tokenCount,
|
|
103
|
+
})
|
|
104
|
+
.catch({
|
|
105
|
+
input_tokens: 0,
|
|
106
|
+
output_tokens: 0,
|
|
107
|
+
cache_read_input_tokens: 0,
|
|
108
|
+
cache_creation_input_tokens: 0,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
/** The launcher's summary of an agent's session: tokens and readings. */
|
|
112
|
+
export const AgentSessionSchema = z.looseObject({
|
|
113
|
+
/** The main loop's tokens. */
|
|
114
|
+
usage: TokensSchema,
|
|
115
|
+
/** The turn's tokens per model, subagents included; older journals have none. */
|
|
116
|
+
model_usage: z.record(z.string(), TokensSchema).catch({}).default({}),
|
|
117
|
+
rate_limit_events: z.array(RateLimitReadingSchema).catch([]),
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
export const JevJobSchema = z.looseObject({ job: z.string() })
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The spec issue as intake read it. `author` (the spec's owner, whose
|
|
124
|
+
* replies alone count) and `url` feed the reply buttons (#503); older
|
|
125
|
+
* journals may lack them.
|
|
126
|
+
*/
|
|
127
|
+
export const SpecIssueSchema = z.looseObject({
|
|
128
|
+
number: z.number(),
|
|
129
|
+
title: z.string(),
|
|
130
|
+
author: z.string().optional().catch(undefined),
|
|
131
|
+
url: z.string().optional().catch(undefined),
|
|
132
|
+
})
|