dsh-tower 2.0.4 → 2.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -0
- package/COMPATIBILITY.md +38 -0
- package/CONTRIBUTING.md +150 -2
- package/README.en.md +40 -3
- package/README.md +38 -3
- package/SECURITY.md +27 -0
- package/adapters/default.js +10 -0
- package/adapters/store-fs.js +6 -0
- package/adapters/vcs-git.js +4 -0
- package/app/batch.js +79 -0
- package/app/board.js +25 -0
- package/app/checkCompletion.js +76 -0
- package/app/enrichedMissions.js +26 -0
- package/app/findings.js +78 -0
- package/app/gate.js +108 -0
- package/app/init.js +249 -0
- package/app/merge.js +567 -0
- package/app/messages.js +66 -0
- package/app/mission.js +35 -0
- package/app/missionContext.js +14 -0
- package/app/plan.js +42 -0
- package/app/provision.js +193 -0
- package/app/read.js +57 -0
- package/app/rebase.js +302 -0
- package/app/recording.js +72 -0
- package/app/review.js +145 -0
- package/app/roster.js +123 -0
- package/app/spawn.js +125 -0
- package/app/teardown.js +184 -0
- package/app/tower.js +524 -0
- package/cordis.patch.yml +61 -53
- package/docs/README.md +3 -1
- package/docs/architecture/overview.md +111 -12
- package/docs/board.md +72 -0
- package/docs/guide/hot-reload.md +1 -1
- package/docs/guide/install-upgrade-migration.md +8 -1
- package/docs/guide/release.md +125 -0
- package/docs/operations/security.md +47 -2
- package/docs/reference/config.generated.md +66 -0
- package/docs/reference/config.md +4 -1
- package/docs/reference/errors.generated.md +39 -0
- package/docs/reference/errors.md +4 -2
- package/docs/reference/tools.generated.md +33 -0
- package/docs/reference/tools.md +1 -1
- package/docs/zh/README.md +3 -2
- package/docs/zh/guide/hot-reload.md +1 -1
- package/docs/zh/guide/install-upgrade-migration.md +6 -1
- package/docs/zh/operations/security.md +30 -1
- package/docs/zh/reference/config.md +4 -1
- package/docs/zh/reference/errors.md +3 -1
- package/docs/zh/reference/tools.md +1 -1
- package/domain/collab/batch.js +112 -0
- package/domain/collab/messages.js +127 -0
- package/domain/collab/roster.js +158 -0
- package/domain/collab/spawn.js +188 -0
- package/domain/config/schema.js +404 -0
- package/domain/contracts/outputs.js +279 -0
- package/domain/contracts/tool-inputs/collaboration.js +58 -0
- package/domain/contracts/tool-inputs/findings.js +29 -0
- package/domain/contracts/tool-inputs/index.js +94 -0
- package/domain/contracts/tool-inputs/integration.js +37 -0
- package/domain/contracts/tool-inputs/missions.js +94 -0
- package/domain/contracts/tool-inputs/ops.js +75 -0
- package/domain/contracts/tool-inputs/shared.js +25 -0
- package/domain/errors.js +96 -0
- package/domain/gate.js +125 -0
- package/domain/integration/gate.js +176 -0
- package/domain/integration/init.js +225 -0
- package/domain/integration/liveness.js +31 -0
- package/domain/integration/merge.js +377 -0
- package/domain/integration/provision.js +124 -0
- package/domain/integration/rebase.js +236 -0
- package/domain/integration/review.js +52 -0
- package/domain/integration/teardown.js +118 -0
- package/domain/mission.js +68 -0
- package/domain/missions/completion.js +114 -0
- package/domain/missions/findings.js +201 -0
- package/domain/missions/lifecycle.js +185 -0
- package/domain/missions/plan.js +149 -0
- package/domain/missions/review.js +179 -0
- package/domain/planning.js +118 -0
- package/domain/read/enrichedMissions.js +58 -0
- package/domain/read/missionContext.js +53 -0
- package/domain/read/views.js +128 -0
- package/domain/report.js +59 -0
- package/domain/roster.js +40 -0
- package/domain/scope.js +105 -0
- package/domain/state/schema.js +233 -0
- package/domain/state/semantics.js +117 -0
- package/domain/state.js +33 -0
- package/domain/summary.js +114 -0
- package/domain/vocabulary.js +179 -0
- package/index.js +63 -20
- package/lib/briefing.js +176 -0
- package/lib/compatibility.js +108 -0
- package/lib/config.js +33 -375
- package/lib/doctor.js +97 -4
- package/lib/git.js +60 -16
- package/lib/hmr.js +4 -3
- package/lib/leases.js +3 -2
- package/lib/legacy.js +20 -9
- package/lib/modeState.js +39 -0
- package/lib/modeStore.js +11 -43
- package/lib/persistence.js +349 -0
- package/lib/presets.js +35 -0
- package/lib/spawn-prechecks.js +168 -0
- package/lib/spawn-support.js +79 -0
- package/lib/store.js +54 -137
- package/lib/tests.js +11 -10
- package/lib/tool-registry.js +544 -0
- package/lib/workspaceLock.js +2 -2
- package/lib/worktreeOwnership.js +5 -4
- package/locale/en.json +23 -1
- package/locale/zh.json +22 -0
- package/package.json +32 -4
- package/ports/clock.js +6 -0
- package/ports/ids.js +7 -0
- package/ports/index.js +2 -0
- package/ports/store.js +11 -0
- package/ports/vcs.js +11 -0
- package/scripts/architecture.mjs +94 -0
- package/scripts/check-docs.mjs +9 -3
- package/scripts/check-pack.mjs +49 -0
- package/scripts/domain-metrics.mjs +48 -0
- package/scripts/generate-config-reference.mjs +46 -0
- package/scripts/generate-error-reference.mjs +29 -0
- package/scripts/generate-tools-reference.mjs +56 -0
- package/scripts/issue-audit.mjs +259 -0
- package/scripts/lib/cli.mjs +76 -0
- package/scripts/lib/tmp.mjs +20 -0
- package/scripts/mount-smoke/fixtures.mjs +162 -0
- package/scripts/mount-smoke/host.json +4 -0
- package/scripts/mount-smoke/probe.mjs +125 -0
- package/scripts/mount-smoke/surface.mjs +141 -0
- package/scripts/mount-smoke.mjs +270 -0
- package/scripts/pack-manifest.txt +203 -0
- package/scripts/release-check.mjs +303 -0
- package/tools/adopt.js +72 -0
- package/tools/batch.js +78 -0
- package/tools/complete.js +53 -0
- package/tools/diff.js +39 -0
- package/tools/doctor.js +50 -0
- package/tools/finding.js +40 -0
- package/tools/findings.js +39 -0
- package/tools/import-legacy.js +42 -0
- package/tools/inbox.js +39 -0
- package/tools/index.js +39 -0
- package/tools/init.js +63 -0
- package/tools/interrupt.js +42 -0
- package/tools/legacy-map.js +44 -0
- package/tools/merge.js +39 -0
- package/tools/mission.js +41 -0
- package/tools/plan.js +39 -0
- package/tools/preflight.js +44 -0
- package/tools/rebase.js +39 -0
- package/tools/recover.js +45 -0
- package/tools/reload.js +52 -0
- package/tools/resource.js +54 -0
- package/tools/review.js +45 -0
- package/tools/roster.js +60 -0
- package/tools/send.js +147 -0
- package/tools/spawn.js +196 -0
- package/tools/status.js +94 -0
- package/tools/teardown.js +42 -0
- package/tools/test.js +61 -0
- package/types/cordis.d.ts +6 -0
- package/types/index.d.ts +339 -0
- package/lib/errors.js +0 -123
- package/lib/tools.js +0 -2460
- package/lib/tower.js +0 -2912
- /package/{lib → domain}/batch.js +0 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// Tool schema primitives and output schemas.
|
|
2
|
+
|
|
3
|
+
const S = {
|
|
4
|
+
str: { type: 'string' },
|
|
5
|
+
int: { type: 'integer' },
|
|
6
|
+
bool: { type: 'boolean' },
|
|
7
|
+
arr: (items) => ({ type: 'array', items }),
|
|
8
|
+
obj: (properties = {}, required = []) => ({ type: 'object', properties, required, additionalProperties: true }),
|
|
9
|
+
any: {},
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const blockerSchema = S.obj({ code: S.str, detail: S.str, cleanup: S.str, timeout: S.bool }, ['code', 'detail'])
|
|
13
|
+
const worktreeSchema = S.obj({ path: S.str, branch: S.str, baseCommit: S.str })
|
|
14
|
+
const missionSummarySchema = S.obj({
|
|
15
|
+
id: S.str,
|
|
16
|
+
title: S.str,
|
|
17
|
+
kind: S.str,
|
|
18
|
+
status: S.str,
|
|
19
|
+
scope: S.arr(S.str),
|
|
20
|
+
deps: S.arr(S.str),
|
|
21
|
+
owner: S.any,
|
|
22
|
+
worktree: S.any,
|
|
23
|
+
blockers: S.arr(S.str),
|
|
24
|
+
tasks: S.any,
|
|
25
|
+
scopeGeneration: S.int,
|
|
26
|
+
mergedCommit: S.any,
|
|
27
|
+
mergedAt: S.any,
|
|
28
|
+
updatedAt: S.any,
|
|
29
|
+
})
|
|
30
|
+
const findingSummarySchema = S.obj({
|
|
31
|
+
id: S.str,
|
|
32
|
+
seq: S.int,
|
|
33
|
+
type: S.str,
|
|
34
|
+
title: S.str,
|
|
35
|
+
severity: S.any,
|
|
36
|
+
status: S.str,
|
|
37
|
+
missionId: S.any,
|
|
38
|
+
branch: S.any,
|
|
39
|
+
reportedBy: S.any,
|
|
40
|
+
at: S.any,
|
|
41
|
+
updatedAt: S.any,
|
|
42
|
+
closedAt: S.any,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Per-tool output schemas. `additionalProperties: true` keeps future fields
|
|
46
|
+
// additive; `required` lists only fields every successful path returns.
|
|
47
|
+
const SCHEMAS = {
|
|
48
|
+
init: S.obj(
|
|
49
|
+
{
|
|
50
|
+
created: S.bool,
|
|
51
|
+
repoRoot: S.str,
|
|
52
|
+
commonDir: S.str,
|
|
53
|
+
statePath: S.str,
|
|
54
|
+
base: S.str,
|
|
55
|
+
revision: S.int,
|
|
56
|
+
missions: S.int,
|
|
57
|
+
},
|
|
58
|
+
['created', 'repoRoot', 'commonDir', 'statePath', 'base', 'revision', 'missions'],
|
|
59
|
+
),
|
|
60
|
+
plan: S.obj({ revision: S.int, missions: S.arr(missionSummarySchema) }, ['revision', 'missions']),
|
|
61
|
+
mission: S.obj({ revision: S.int, mission: S.obj() }, ['revision', 'mission']),
|
|
62
|
+
status: S.obj(
|
|
63
|
+
{
|
|
64
|
+
repoRoot: S.str,
|
|
65
|
+
commonDir: S.str,
|
|
66
|
+
statePath: S.str,
|
|
67
|
+
base: S.str,
|
|
68
|
+
revision: S.int,
|
|
69
|
+
counts: S.obj(),
|
|
70
|
+
openTasks: S.int,
|
|
71
|
+
roster: S.int,
|
|
72
|
+
messages: S.int,
|
|
73
|
+
unreadTower: S.int,
|
|
74
|
+
findings: S.int,
|
|
75
|
+
findingsByStatus: S.obj(),
|
|
76
|
+
reviews: S.int,
|
|
77
|
+
config: S.obj({ path: S.any, repoConfigPresent: S.bool, spawn: S.obj() }),
|
|
78
|
+
recentFindings: S.arr(S.obj()),
|
|
79
|
+
missions: S.arr(S.obj()),
|
|
80
|
+
activity: S.arr(S.obj()),
|
|
81
|
+
mode: S.any,
|
|
82
|
+
spawn: S.any,
|
|
83
|
+
towerAgentId: S.any,
|
|
84
|
+
ownerMissing: S.bool,
|
|
85
|
+
},
|
|
86
|
+
['repoRoot', 'commonDir', 'statePath', 'base', 'revision', 'counts', 'missions', 'activity'],
|
|
87
|
+
),
|
|
88
|
+
spawn: S.obj(
|
|
89
|
+
{
|
|
90
|
+
name: S.str,
|
|
91
|
+
agentId: S.str,
|
|
92
|
+
role: S.str,
|
|
93
|
+
missionId: S.str,
|
|
94
|
+
preset: S.str,
|
|
95
|
+
provider: S.str,
|
|
96
|
+
maxDepth: S.int,
|
|
97
|
+
agentOptions: S.any,
|
|
98
|
+
worktree: worktreeSchema,
|
|
99
|
+
created: S.bool,
|
|
100
|
+
briefingChars: S.int,
|
|
101
|
+
rosterEntry: S.obj(),
|
|
102
|
+
stopReason: S.any,
|
|
103
|
+
timedOut: S.bool,
|
|
104
|
+
output: S.any,
|
|
105
|
+
diagnostic: S.any,
|
|
106
|
+
},
|
|
107
|
+
['name', 'agentId', 'role', 'missionId', 'preset', 'provider', 'worktree'],
|
|
108
|
+
),
|
|
109
|
+
roster: S.obj(
|
|
110
|
+
{ revision: S.int, roster: S.arr(S.obj()), parentStatus: S.obj(), missions: S.arr(S.obj()) },
|
|
111
|
+
['revision', 'roster', 'parentStatus', 'missions'],
|
|
112
|
+
),
|
|
113
|
+
diff: S.obj(
|
|
114
|
+
{
|
|
115
|
+
missionId: S.str,
|
|
116
|
+
branch: S.str,
|
|
117
|
+
baseTip: S.str,
|
|
118
|
+
branchTip: S.str,
|
|
119
|
+
mergeBase: S.str,
|
|
120
|
+
files: S.arr(S.str),
|
|
121
|
+
commits: S.arr(S.str),
|
|
122
|
+
truncated: S.bool,
|
|
123
|
+
patch: S.str,
|
|
124
|
+
},
|
|
125
|
+
['missionId', 'branch', 'baseTip', 'branchTip', 'mergeBase', 'files', 'commits', 'truncated', 'patch'],
|
|
126
|
+
),
|
|
127
|
+
adopt: S.obj(
|
|
128
|
+
{ retired: S.arr(S.str), skippedUnknownParents: S.arr(S.str), roster: S.arr(S.obj()), parentStatus: S.obj() },
|
|
129
|
+
['retired', 'skippedUnknownParents', 'roster', 'parentStatus'],
|
|
130
|
+
),
|
|
131
|
+
send: S.obj({ message: S.obj(), deliveries: S.arr(S.obj()) }, ['message', 'deliveries']),
|
|
132
|
+
inbox: S.obj(
|
|
133
|
+
{ reader: S.str, messages: S.arr(S.obj()), pageSize: S.int, unreadRemaining: S.int },
|
|
134
|
+
['reader', 'messages', 'pageSize', 'unreadRemaining'],
|
|
135
|
+
),
|
|
136
|
+
complete: S.obj({ mission: S.obj(), commits: S.int, unread: S.int }, ['mission', 'commits', 'unread']),
|
|
137
|
+
finding: S.obj(
|
|
138
|
+
{
|
|
139
|
+
id: S.str,
|
|
140
|
+
seq: S.int,
|
|
141
|
+
type: S.str,
|
|
142
|
+
title: S.str,
|
|
143
|
+
severity: S.any,
|
|
144
|
+
summary: S.str,
|
|
145
|
+
location: S.any,
|
|
146
|
+
details: S.str,
|
|
147
|
+
suggested_fix: S.str,
|
|
148
|
+
missionId: S.any,
|
|
149
|
+
branch: S.any,
|
|
150
|
+
reportedBy: S.any,
|
|
151
|
+
at: S.any,
|
|
152
|
+
status: S.str,
|
|
153
|
+
updatedAt: S.any,
|
|
154
|
+
closedAt: S.any,
|
|
155
|
+
notes: S.arr(S.obj()),
|
|
156
|
+
},
|
|
157
|
+
['id', 'type', 'title', 'summary', 'details', 'suggested_fix', 'status'],
|
|
158
|
+
),
|
|
159
|
+
findings: S.obj(
|
|
160
|
+
{
|
|
161
|
+
findings: S.arr(findingSummarySchema),
|
|
162
|
+
finding: S.obj(),
|
|
163
|
+
counts: S.obj(),
|
|
164
|
+
total: S.int,
|
|
165
|
+
filtered: S.int,
|
|
166
|
+
action: S.str,
|
|
167
|
+
},
|
|
168
|
+
['counts', 'total'],
|
|
169
|
+
),
|
|
170
|
+
review: S.obj(
|
|
171
|
+
{ review: S.obj(), branchTip: S.str, missionStatus: S.str, round: S.int, reviewerRounds: S.int },
|
|
172
|
+
['review', 'branchTip', 'missionStatus', 'round', 'reviewerRounds'],
|
|
173
|
+
),
|
|
174
|
+
preflight: S.obj(
|
|
175
|
+
{
|
|
176
|
+
ok: S.bool,
|
|
177
|
+
blockers: S.arr(blockerSchema),
|
|
178
|
+
missionId: S.str,
|
|
179
|
+
branchTip: S.any,
|
|
180
|
+
baseTip: S.any,
|
|
181
|
+
changedFiles: S.any,
|
|
182
|
+
review: S.any,
|
|
183
|
+
},
|
|
184
|
+
['ok', 'blockers', 'missionId'],
|
|
185
|
+
),
|
|
186
|
+
merge: S.obj(
|
|
187
|
+
{
|
|
188
|
+
ok: S.bool,
|
|
189
|
+
missionId: S.str,
|
|
190
|
+
mergedCommit: S.any,
|
|
191
|
+
baseTip: S.any,
|
|
192
|
+
branchTip: S.any,
|
|
193
|
+
parents: S.any,
|
|
194
|
+
blockers: S.arr(blockerSchema),
|
|
195
|
+
warnings: S.any,
|
|
196
|
+
alreadyMerged: S.bool,
|
|
197
|
+
recoveredFromFailure: S.bool,
|
|
198
|
+
},
|
|
199
|
+
['ok'],
|
|
200
|
+
),
|
|
201
|
+
rebase: S.obj(
|
|
202
|
+
{
|
|
203
|
+
ok: S.bool,
|
|
204
|
+
oldTip: S.any,
|
|
205
|
+
newTip: S.any,
|
|
206
|
+
baseTip: S.any,
|
|
207
|
+
alreadyMerged: S.bool,
|
|
208
|
+
alreadyUpToDate: S.bool,
|
|
209
|
+
skipped: S.bool,
|
|
210
|
+
reviewWaived: S.bool,
|
|
211
|
+
note: S.str,
|
|
212
|
+
blockers: S.arr(blockerSchema),
|
|
213
|
+
},
|
|
214
|
+
['ok'],
|
|
215
|
+
),
|
|
216
|
+
teardown: S.obj(
|
|
217
|
+
{ ok: S.bool, dryRun: S.bool, removed: S.arr(S.str), decisions: S.arr(S.obj()) },
|
|
218
|
+
['ok', 'dryRun', 'removed', 'decisions'],
|
|
219
|
+
),
|
|
220
|
+
interrupt: S.obj(
|
|
221
|
+
{
|
|
222
|
+
ok: S.bool,
|
|
223
|
+
interrupted: S.bool,
|
|
224
|
+
previousActivity: S.str,
|
|
225
|
+
entry: S.obj(),
|
|
226
|
+
reason: S.str,
|
|
227
|
+
},
|
|
228
|
+
['ok', 'interrupted'],
|
|
229
|
+
),
|
|
230
|
+
reload: S.obj(
|
|
231
|
+
{
|
|
232
|
+
ok: S.bool,
|
|
233
|
+
scheduled: S.bool,
|
|
234
|
+
delayMs: S.int,
|
|
235
|
+
requiredIds: S.arr(S.str),
|
|
236
|
+
error: S.str,
|
|
237
|
+
restartHint: S.str,
|
|
238
|
+
},
|
|
239
|
+
['ok', 'scheduled'],
|
|
240
|
+
),
|
|
241
|
+
doctor: S.obj({ ok: S.bool, checks: S.arr(S.obj()), environmentContract: S.obj() }, ['ok', 'checks']),
|
|
242
|
+
recover: S.obj(
|
|
243
|
+
{ ok: S.bool, action: S.str, stateLock: S.any, cleared: S.bool, backup: S.any, error: S.str },
|
|
244
|
+
['ok', 'action'],
|
|
245
|
+
),
|
|
246
|
+
test: S.obj(
|
|
247
|
+
{
|
|
248
|
+
ok: S.bool,
|
|
249
|
+
configured: S.bool,
|
|
250
|
+
treeTip: S.any,
|
|
251
|
+
tests: S.arr(S.obj()),
|
|
252
|
+
commands: S.arr(S.obj()),
|
|
253
|
+
error: S.str,
|
|
254
|
+
receipt: S.obj(),
|
|
255
|
+
checks: S.arr(S.obj()),
|
|
256
|
+
},
|
|
257
|
+
['ok', 'configured'],
|
|
258
|
+
),
|
|
259
|
+
batch: S.obj(
|
|
260
|
+
{ ok: S.bool, action: S.str, batch: S.obj(), batches: S.arr(S.obj()), blockers: S.arr(blockerSchema), summary: S.obj() },
|
|
261
|
+
['ok', 'action'],
|
|
262
|
+
),
|
|
263
|
+
resource: S.obj(
|
|
264
|
+
{ ok: S.bool, action: S.str, lease: S.obj(), leases: S.arr(S.obj()), released: S.bool },
|
|
265
|
+
['ok', 'action'],
|
|
266
|
+
),
|
|
267
|
+
importLegacy: S.obj({ ok: S.bool, summary: S.obj(), snapshot: S.obj() }, ['ok', 'summary']),
|
|
268
|
+
legacyMap: S.obj({ ok: S.bool, map: S.obj(), maps: S.arr(S.obj()) }, ['ok']),
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function renderValue(_args, value) {
|
|
272
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }]
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function output(schema) {
|
|
276
|
+
return { schema, render: renderValue }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export { S, SCHEMAS, output }
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// tower_spawn / tower_roster / tower_interrupt / tower_send / tower_inbox /
|
|
2
|
+
// tower_adopt input contracts. Pure TypeBox only; no I/O.
|
|
3
|
+
import { ROLES } from '../../vocabulary.js'
|
|
4
|
+
import { Type, enumOf } from './shared.js'
|
|
5
|
+
|
|
6
|
+
export const SpawnInput = Type.Object({
|
|
7
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
8
|
+
missionId: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: id).' })),
|
|
9
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId; both are accepted.' })),
|
|
10
|
+
role: Type.Optional(enumOf(ROLES, { description: 'worker executes; reviewer audits the mission branch.' })),
|
|
11
|
+
name: Type.Optional(Type.String({ description: 'Unique roster name; defaults to <role>-<missionId>.' })),
|
|
12
|
+
instructions: Type.Optional(Type.String({ description: 'Extra tower instructions appended to the briefing.' })),
|
|
13
|
+
worktreeRoot: Type.Optional(Type.String({ description: 'Optional absolute parent directory for the worktree.' })),
|
|
14
|
+
wait: Type.Optional(Type.Boolean({ description: 'Wait for the child result instead of returning immediately (testing/debug).' })),
|
|
15
|
+
waitMs: Type.Optional(Type.Number({ description: 'Max wait with wait=true, default 180000; clamped to spawn.maxWaitMs (default 90000).' })),
|
|
16
|
+
wait_ms: Type.Optional(Type.Number({ description: 'Alias for waitMs.' })),
|
|
17
|
+
takeover: Type.Optional(Type.Boolean({ description: 'Replace a live worker entry that is no longer running (increments ownerGeneration).' })),
|
|
18
|
+
includeDirtyBase: Type.Optional(Type.Boolean({ description: 'Use the recorded snapshotCommit baseline when true (default true).' })),
|
|
19
|
+
}, { additionalProperties: false })
|
|
20
|
+
|
|
21
|
+
export const RosterInput = Type.Object({
|
|
22
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
23
|
+
}, { additionalProperties: false })
|
|
24
|
+
|
|
25
|
+
export const InterruptInput = Type.Object({
|
|
26
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
27
|
+
to: Type.Optional(Type.String({ description: 'Roster name to interrupt.' })),
|
|
28
|
+
agentId: Type.Optional(Type.String({ description: 'Agent/session id to interrupt (alternative to to).' })),
|
|
29
|
+
agent_id: Type.Optional(Type.String({ description: 'Alias for agentId.' })),
|
|
30
|
+
reason: Type.Optional(Type.String({ description: 'Why the agent is being interrupted (recorded in the roster).' })),
|
|
31
|
+
}, { additionalProperties: false })
|
|
32
|
+
|
|
33
|
+
export const SendInput = Type.Object({
|
|
34
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
35
|
+
to: Type.String({ description: 'Recipient: a roster agent name, "tower", or "all".' }),
|
|
36
|
+
subject: Type.String({ description: 'One-line subject.' }),
|
|
37
|
+
body: Type.String({ description: 'Full message body (markdown).' }),
|
|
38
|
+
scope: Type.Optional(Type.String({ description: 'Optional scope tag, e.g. the mission id.' })),
|
|
39
|
+
interrupt: Type.Optional(
|
|
40
|
+
Type.Boolean({
|
|
41
|
+
description: 'Interrupt the recipient\'s current turn before delivering (Kimi-style "message interrupts the running turn"); default false = queue at the next step boundary.',
|
|
42
|
+
}),
|
|
43
|
+
),
|
|
44
|
+
consentRef: Type.Optional(Type.String({ description: 'Optional audit reference recording who consented to this instruction.' })),
|
|
45
|
+
consent_ref: Type.Optional(Type.String({ description: 'Alias for consentRef.' })),
|
|
46
|
+
}, { additionalProperties: false })
|
|
47
|
+
|
|
48
|
+
export const InboxInput = Type.Object({
|
|
49
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
50
|
+
limit: Type.Optional(Type.Number({ description: 'Max messages to return (default 20), newest first.' })),
|
|
51
|
+
include_read: Type.Optional(Type.Boolean({ description: 'Include previously acknowledged messages for history lookup.' })),
|
|
52
|
+
includeRead: Type.Optional(Type.Boolean({ description: 'Alias for include_read.' })),
|
|
53
|
+
}, { additionalProperties: false })
|
|
54
|
+
|
|
55
|
+
export const AdoptInput = Type.Object({
|
|
56
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
57
|
+
retireMissing: Type.Optional(Type.Boolean({ description: 'Retire provably gone agents (default true); false marks them missing instead.' })),
|
|
58
|
+
}, { additionalProperties: false })
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// tower_finding / tower_findings input contracts. Pure TypeBox only; no I/O.
|
|
2
|
+
import { FINDING_FILTER_STATUSES, FINDING_SEVERITIES, FINDING_TYPES } from '../../vocabulary.js'
|
|
3
|
+
import { Type, enumOf } from './shared.js'
|
|
4
|
+
|
|
5
|
+
export const FindingInput = Type.Object({
|
|
6
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
7
|
+
type: enumOf(FINDING_TYPES, { description: 'Finding category.' }),
|
|
8
|
+
title: Type.String({ description: 'Short finding title.' }),
|
|
9
|
+
severity: Type.Optional(enumOf(FINDING_SEVERITIES, { description: 'Optional severity.' })),
|
|
10
|
+
summary: Type.String({ description: 'What was found, in a sentence or two.' }),
|
|
11
|
+
location: Type.Optional(Type.String({ description: 'File/symbol the finding concerns.' })),
|
|
12
|
+
details: Type.String({ description: 'Full details: evidence, reproduction, impact.' }),
|
|
13
|
+
suggested_fix: Type.String({ description: 'What you would do about it.' }),
|
|
14
|
+
missionId: Type.Optional(Type.String({ description: 'Optional mission this finding relates to (alias: mission_id).' })),
|
|
15
|
+
mission_id: Type.Optional(Type.String({ description: 'Alias for missionId.' })),
|
|
16
|
+
}, { additionalProperties: false })
|
|
17
|
+
|
|
18
|
+
export const FindingsInput = Type.Object({
|
|
19
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
20
|
+
id: Type.Optional(Type.String({ description: 'Finding id, e.g. F1. When present the call reads or updates that finding.' })),
|
|
21
|
+
status: Type.Optional(enumOf(FINDING_FILTER_STATUSES, { description: 'With id: target status. Without id: filter (default all).' })),
|
|
22
|
+
note: Type.Optional(Type.String({ description: 'Optional note recorded together with an update.' })),
|
|
23
|
+
type: Type.Optional(enumOf(FINDING_TYPES, { description: 'Filter by finding type.' })),
|
|
24
|
+
severity: Type.Optional(enumOf(FINDING_SEVERITIES, { description: 'Filter by severity.' })),
|
|
25
|
+
missionId: Type.Optional(Type.String({ description: 'Filter by mission id (alias: mission_id).' })),
|
|
26
|
+
mission_id: Type.Optional(Type.String({ description: 'Alias for missionId.' })),
|
|
27
|
+
open: Type.Optional(Type.Boolean({ description: 'Only findings that are not fixed or wontfix.' })),
|
|
28
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 200, description: 'Max findings to list (default 50).' })),
|
|
29
|
+
}, { additionalProperties: false })
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Tool input contracts (#56 P3c): named schemas grouped by area plus a
|
|
2
|
+
// `ToolInputs` map keyed by registered tool name. Pure data; no I/O.
|
|
3
|
+
import {
|
|
4
|
+
CompleteInput,
|
|
5
|
+
MissionInput,
|
|
6
|
+
PlanInput,
|
|
7
|
+
PreflightInput,
|
|
8
|
+
ReviewInput,
|
|
9
|
+
StatusInput,
|
|
10
|
+
} from './missions.js'
|
|
11
|
+
import {
|
|
12
|
+
AdoptInput,
|
|
13
|
+
InboxInput,
|
|
14
|
+
InterruptInput,
|
|
15
|
+
RosterInput,
|
|
16
|
+
SendInput,
|
|
17
|
+
SpawnInput,
|
|
18
|
+
} from './collaboration.js'
|
|
19
|
+
import { DiffInput, MergeInput, RebaseInput, TeardownInput } from './integration.js'
|
|
20
|
+
import { FindingInput, FindingsInput } from './findings.js'
|
|
21
|
+
import {
|
|
22
|
+
BatchInput,
|
|
23
|
+
DoctorInput,
|
|
24
|
+
ImportLegacyInput,
|
|
25
|
+
InitInput,
|
|
26
|
+
LegacyMapInput,
|
|
27
|
+
RecoverInput,
|
|
28
|
+
ReloadInput,
|
|
29
|
+
ResourceInput,
|
|
30
|
+
TestInput,
|
|
31
|
+
} from './ops.js'
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
CompleteInput,
|
|
35
|
+
MissionInput,
|
|
36
|
+
PlanInput,
|
|
37
|
+
PreflightInput,
|
|
38
|
+
ReviewInput,
|
|
39
|
+
StatusInput,
|
|
40
|
+
AdoptInput,
|
|
41
|
+
InboxInput,
|
|
42
|
+
InterruptInput,
|
|
43
|
+
RosterInput,
|
|
44
|
+
SendInput,
|
|
45
|
+
SpawnInput,
|
|
46
|
+
DiffInput,
|
|
47
|
+
MergeInput,
|
|
48
|
+
RebaseInput,
|
|
49
|
+
TeardownInput,
|
|
50
|
+
FindingInput,
|
|
51
|
+
FindingsInput,
|
|
52
|
+
BatchInput,
|
|
53
|
+
DoctorInput,
|
|
54
|
+
ImportLegacyInput,
|
|
55
|
+
InitInput,
|
|
56
|
+
LegacyMapInput,
|
|
57
|
+
RecoverInput,
|
|
58
|
+
ReloadInput,
|
|
59
|
+
ResourceInput,
|
|
60
|
+
TestInput,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @type {Record<string, import('@sinclair/typebox').TSchema>} */
|
|
64
|
+
const ToolInputs = {
|
|
65
|
+
tower_init: InitInput,
|
|
66
|
+
tower_plan: PlanInput,
|
|
67
|
+
tower_mission: MissionInput,
|
|
68
|
+
tower_status: StatusInput,
|
|
69
|
+
tower_spawn: SpawnInput,
|
|
70
|
+
tower_roster: RosterInput,
|
|
71
|
+
tower_interrupt: InterruptInput,
|
|
72
|
+
tower_adopt: AdoptInput,
|
|
73
|
+
tower_send: SendInput,
|
|
74
|
+
tower_inbox: InboxInput,
|
|
75
|
+
tower_complete: CompleteInput,
|
|
76
|
+
tower_finding: FindingInput,
|
|
77
|
+
tower_findings: FindingsInput,
|
|
78
|
+
tower_diff: DiffInput,
|
|
79
|
+
tower_review: ReviewInput,
|
|
80
|
+
tower_preflight: PreflightInput,
|
|
81
|
+
tower_merge: MergeInput,
|
|
82
|
+
tower_rebase: RebaseInput,
|
|
83
|
+
tower_teardown: TeardownInput,
|
|
84
|
+
tower_doctor: DoctorInput,
|
|
85
|
+
tower_recover: RecoverInput,
|
|
86
|
+
tower_test: TestInput,
|
|
87
|
+
tower_batch: BatchInput,
|
|
88
|
+
tower_resource: ResourceInput,
|
|
89
|
+
tower_import_legacy: ImportLegacyInput,
|
|
90
|
+
tower_legacy_map: LegacyMapInput,
|
|
91
|
+
tower_reload: ReloadInput,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { ToolInputs }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// tower_merge / tower_rebase / tower_teardown / tower_diff input contracts.
|
|
2
|
+
// Pure TypeBox only; no I/O.
|
|
3
|
+
import { Type } from './shared.js'
|
|
4
|
+
|
|
5
|
+
export const MergeInput = Type.Object({
|
|
6
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
7
|
+
missionId: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: id).' })),
|
|
8
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId; both are accepted.' })),
|
|
9
|
+
timeoutMs: Type.Optional(
|
|
10
|
+
Type.Integer({
|
|
11
|
+
minimum: 1000,
|
|
12
|
+
description: 'Optional timeout for the merge command in milliseconds, including pre-merge hooks (default 15 minutes).',
|
|
13
|
+
}),
|
|
14
|
+
),
|
|
15
|
+
timeout_ms: Type.Optional(Type.Integer({ minimum: 1000, description: 'Alias for timeoutMs.' })),
|
|
16
|
+
}, { additionalProperties: false })
|
|
17
|
+
|
|
18
|
+
export const RebaseInput = Type.Object({
|
|
19
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
20
|
+
missionId: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: id).' })),
|
|
21
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId; both are accepted.' })),
|
|
22
|
+
}, { additionalProperties: false })
|
|
23
|
+
|
|
24
|
+
export const TeardownInput = Type.Object({
|
|
25
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
26
|
+
force: Type.Optional(Type.Boolean({ description: 'Remove worktrees that contain uncommitted changes (never a live agent).' })),
|
|
27
|
+
exclude: Type.Optional(Type.Array(Type.String(), { description: 'Mission ids to keep.' })),
|
|
28
|
+
dry_run: Type.Optional(Type.Boolean({ description: 'Preview remove/keep decisions without changing disk or state.' })),
|
|
29
|
+
dryRun: Type.Optional(Type.Boolean({ description: 'Alias for dry_run.' })),
|
|
30
|
+
}, { additionalProperties: false })
|
|
31
|
+
|
|
32
|
+
export const DiffInput = Type.Object({
|
|
33
|
+
repoRoot: Type.String({ description: 'Absolute path to the main checkout root.' }),
|
|
34
|
+
missionId: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: id).' })),
|
|
35
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId; both are accepted.' })),
|
|
36
|
+
maxBytes: Type.Optional(Type.Integer({ minimum: 1024, description: 'Patch cap in bytes (default 65536).' })),
|
|
37
|
+
}, { additionalProperties: false })
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// tower_plan / tower_mission / tower_status / tower_complete / tower_review /
|
|
2
|
+
// tower_preflight input contracts. Pure TypeBox only; no I/O.
|
|
3
|
+
import {
|
|
4
|
+
MERGE_VERDICTS,
|
|
5
|
+
MISSION_KINDS,
|
|
6
|
+
MISSION_STATUSES,
|
|
7
|
+
PAUSE_CAUSES,
|
|
8
|
+
REVIEW_POLICIES,
|
|
9
|
+
STATUS_FORMATS,
|
|
10
|
+
} from '../../vocabulary.js'
|
|
11
|
+
import { Type, enumOf } from './shared.js'
|
|
12
|
+
|
|
13
|
+
export const PlanInput = Type.Object({
|
|
14
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
15
|
+
missions: Type.Array(
|
|
16
|
+
Type.Object({
|
|
17
|
+
title: Type.String({ description: 'Printable ASCII title; used as the mission label.' }),
|
|
18
|
+
scope: Type.Optional(Type.Array(Type.String(), { description: 'Relative file globs this mission may touch; empty only for survey.' })),
|
|
19
|
+
tasks: Type.Optional(Type.Array(Type.String(), { description: 'Verifiable task items the worker ticks off.' })),
|
|
20
|
+
deps: Type.Optional(Type.Array(Type.String(), { description: 'Mission ids that must merge before this one.' })),
|
|
21
|
+
kind: Type.Optional(enumOf(MISSION_KINDS, { description: 'survey = read-only investigation that reserves no scope.' })),
|
|
22
|
+
context: Type.Optional(Type.String({ description: "The user's own key sentences, verbatim (max 4096 chars)." })),
|
|
23
|
+
references: Type.Optional(Type.Array(Type.String(), { maxItems: 20, description: 'Read-only pointers/files the worker should read (max 20); copied into the briefing.' })),
|
|
24
|
+
reviewPolicy: Type.Optional(enumOf(REVIEW_POLICIES, { description: 'required-clean (mission default) means a clean review is required when review.requireReview is on; none needs reviewExemptionReason, and with the review gate on it also needs gates.allowReviewExemption.' })),
|
|
25
|
+
reviewExemptionReason: Type.Optional(Type.String({ description: 'Non-empty justification when reviewPolicy is "none".' })),
|
|
26
|
+
}, { additionalProperties: false }),
|
|
27
|
+
{ description: 'Missions to create, in intended order.' },
|
|
28
|
+
),
|
|
29
|
+
}, { additionalProperties: false })
|
|
30
|
+
|
|
31
|
+
export const MissionInput = Type.Object({
|
|
32
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
33
|
+
id: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: missionId).' })),
|
|
34
|
+
missionId: Type.Optional(Type.String({ description: 'Alias for id; both are accepted.' })),
|
|
35
|
+
status: Type.Optional(enumOf(MISSION_STATUSES, { description: 'New lifecycle status.' })),
|
|
36
|
+
note: Type.Optional(Type.String({ description: 'Append a decision-log note.' })),
|
|
37
|
+
blocker: Type.Optional(Type.String({ description: 'Report a blocker (also sets status to blocked).' })),
|
|
38
|
+
clear_blockers: Type.Optional(Type.Boolean({ description: 'Clear all recorded blockers.' })),
|
|
39
|
+
clearBlockers: Type.Optional(Type.Boolean({ description: 'Alias for clear_blockers.' })),
|
|
40
|
+
task_done: Type.Optional(Type.String({ description: 'Mark the first open task containing this text as done.' })),
|
|
41
|
+
taskDone: Type.Optional(Type.String({ description: 'Alias for task_done.' })),
|
|
42
|
+
task_drop: Type.Optional(Type.String({ description: 'Mark the first open task containing this text as dropped.' })),
|
|
43
|
+
taskDrop: Type.Optional(Type.String({ description: 'Alias for task_drop.' })),
|
|
44
|
+
task_drop_reason: Type.Optional(Type.String({ description: 'Required with task_drop: why the task is legitimately descoped.' })),
|
|
45
|
+
taskDropReason: Type.Optional(Type.String({ description: 'Alias for task_drop_reason.' })),
|
|
46
|
+
scope: Type.Optional(Type.Array(Type.String(), { description: 'Tower only: replacement scope globs; bumps scopeGeneration.' })),
|
|
47
|
+
pause_cause: Type.Optional(enumOf(PAUSE_CAUSES, { description: 'With status=paused: why the mission was paused (default user).' })),
|
|
48
|
+
authorization: Type.Optional(Type.String({ description: 'Authorization reference recorded when resuming a user-paused mission.' })),
|
|
49
|
+
}, { additionalProperties: false })
|
|
50
|
+
|
|
51
|
+
export const StatusInput = Type.Object({
|
|
52
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
53
|
+
missionId: Type.Optional(Type.String({ description: 'Return full detail for one mission instead of the dashboard list.' })),
|
|
54
|
+
offset: Type.Optional(Type.Integer({ minimum: 0, description: 'Mission list offset (default 0).' })),
|
|
55
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100, description: 'Mission list page size (default 20).' })),
|
|
56
|
+
includeGate: Type.Optional(Type.Boolean({ description: 'Compute tower_preflight-style gate digests for missions on this page.' })),
|
|
57
|
+
format: Type.Optional(enumOf(STATUS_FORMATS, { description: 'markdown returns the MISSIONS.md board text.' })),
|
|
58
|
+
}, { additionalProperties: false })
|
|
59
|
+
|
|
60
|
+
export const CompleteInput = Type.Object({
|
|
61
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
62
|
+
missionId: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: id).' })),
|
|
63
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId; both are accepted.' })),
|
|
64
|
+
report: Type.String({ description: 'Full completion report for the tower and the next review round.' }),
|
|
65
|
+
checks: Type.Optional(
|
|
66
|
+
Type.Array(
|
|
67
|
+
Type.Object({
|
|
68
|
+
name: Type.String({ description: 'What was checked.' }),
|
|
69
|
+
command: Type.Optional(Type.String({ description: 'Command or method used.' })),
|
|
70
|
+
exitCode: Type.Optional(Type.Integer({ description: 'Observed exit code.' })),
|
|
71
|
+
evidence: Type.Optional(Type.String({ description: 'Short evidence line/log reference.' })),
|
|
72
|
+
}, { additionalProperties: false }),
|
|
73
|
+
{ description: 'Optional evidence ledger entries: {name, command?, exitCode?, evidence?}.' },
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
}, { additionalProperties: false })
|
|
77
|
+
|
|
78
|
+
export const ReviewInput = Type.Object({
|
|
79
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
80
|
+
missionId: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: id).' })),
|
|
81
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId; both are accepted.' })),
|
|
82
|
+
status: Type.String({ description: 'Verdict: "clean", or "p1-Nitems" / "p2-Nitems" with the number of findings at that priority.' }),
|
|
83
|
+
merge: enumOf(MERGE_VERDICTS, { description: 'Merge recommendation for the tower.' }),
|
|
84
|
+
findings: Type.String({ description: 'Full findings text (markdown); write "none" when clean.' }),
|
|
85
|
+
checks: Type.Optional(Type.Array(Type.String(), { description: 'Checklist items you verified.' })),
|
|
86
|
+
decision: Type.String({ description: 'The reasoning behind your verdict.' }),
|
|
87
|
+
breakGlassReason: Type.Optional(Type.String({ description: 'Tower-only exceptional self-review: why no independent reviewer was used.' })),
|
|
88
|
+
}, { additionalProperties: false })
|
|
89
|
+
|
|
90
|
+
export const PreflightInput = Type.Object({
|
|
91
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
92
|
+
missionId: Type.Optional(Type.String({ description: 'Mission id, e.g. M1 (canonical; alias: id).' })),
|
|
93
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId; both are accepted.' })),
|
|
94
|
+
}, { additionalProperties: false })
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// tower_init / tower_doctor / tower_recover / tower_test / tower_batch /
|
|
2
|
+
// tower_resource / tower_import_legacy / tower_legacy_map / tower_reload input
|
|
3
|
+
// contracts. Pure TypeBox only; no I/O.
|
|
4
|
+
import {
|
|
5
|
+
BATCH_ACTIONS,
|
|
6
|
+
LEASE_ACTIONS,
|
|
7
|
+
LEGACY_MAP_ACTIONS,
|
|
8
|
+
RECOVERY_ACTIONS,
|
|
9
|
+
} from '../../vocabulary.js'
|
|
10
|
+
import { Type, enumOf, untypedObject } from './shared.js'
|
|
11
|
+
|
|
12
|
+
export const InitInput = Type.Object({
|
|
13
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
14
|
+
base: Type.Optional(Type.String({ description: 'Optional local base branch (defaults to the checked-out branch).' })),
|
|
15
|
+
autoGitInit: Type.Optional(Type.Boolean({ description: 'Run git init (and an empty initial commit) when repoRoot is not a repository yet.' })),
|
|
16
|
+
snapshotDirty: Type.Optional(Type.Boolean({ description: 'Snapshot uncommitted tracked changes into a dangling commit used as the mission baseline (untracked files are not included).' })),
|
|
17
|
+
reanchor: Type.Optional(Type.Boolean({ description: 'Update the recorded base when no mission is active.' })),
|
|
18
|
+
}, { additionalProperties: false })
|
|
19
|
+
|
|
20
|
+
export const DoctorInput = Type.Object({
|
|
21
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
22
|
+
}, { additionalProperties: false })
|
|
23
|
+
|
|
24
|
+
export const RecoverInput = Type.Object({
|
|
25
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
26
|
+
action: enumOf(RECOVERY_ACTIONS, { description: 'Recovery action.' }),
|
|
27
|
+
confirm: Type.Optional(Type.String({ description: 'Required literal CLEAR_STALE_LOCK for clear-stale-lock.' })),
|
|
28
|
+
}, { additionalProperties: false })
|
|
29
|
+
|
|
30
|
+
export const TestInput = Type.Object({
|
|
31
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
32
|
+
missionId: Type.String({ description: 'Mission id, e.g. M1 (alias: id).' }),
|
|
33
|
+
id: Type.Optional(Type.String({ description: 'Alias for missionId.' })),
|
|
34
|
+
}, { additionalProperties: false })
|
|
35
|
+
|
|
36
|
+
export const BatchInput = Type.Object({
|
|
37
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
38
|
+
action: enumOf(BATCH_ACTIONS, { description: 'Batch operation.' }),
|
|
39
|
+
missionIds: Type.Optional(Type.Array(Type.String(), { description: 'Exact mission ids in this batch (create).' })),
|
|
40
|
+
exclusions: Type.Optional(Type.Array(Type.String(), { description: 'Paused legacy mission ids excluded from the batch.' })),
|
|
41
|
+
requiredChecks: Type.Optional(Type.Array(Type.String(), { description: 'Check names every build member must record (default ["check"]).' })),
|
|
42
|
+
receiptPaths: Type.Optional(Type.Array(Type.String(), { description: 'Exact relative receipt files checked at close.' })),
|
|
43
|
+
batchId: Type.Optional(Type.String({ description: 'Batch id (status/close/abandon).' })),
|
|
44
|
+
reason: Type.Optional(Type.String({ description: 'Reason recorded when abandoning a batch.' })),
|
|
45
|
+
checks: Type.Optional(Type.Array(untypedObject(), { description: 'Close-time extra check evidence.' })),
|
|
46
|
+
treeTip: Type.Optional(Type.String({ description: 'Expected reviewed/tested commit for close evidence.' })),
|
|
47
|
+
receiptHashes: Type.Optional(untypedObject('Relative receipt path -> sha256 observed by the caller.')),
|
|
48
|
+
}, { additionalProperties: false })
|
|
49
|
+
|
|
50
|
+
export const ResourceInput = Type.Object({
|
|
51
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
52
|
+
action: enumOf(LEASE_ACTIONS, { description: 'Lease operation.' }),
|
|
53
|
+
name: Type.Optional(Type.String({ description: 'Lease name, e.g. plugin-load or heavy-ci.' })),
|
|
54
|
+
holder: Type.Optional(Type.String({ description: 'Holder label (defaults to the caller).' })),
|
|
55
|
+
token: Type.Optional(Type.String({ description: 'Release token; generated on acquire when omitted.' })),
|
|
56
|
+
ttlMs: Type.Optional(Type.Integer({ minimum: 1000, description: 'Lease lifetime (default leases.ttlMs).' })),
|
|
57
|
+
force: Type.Optional(Type.Boolean({ description: 'Explicitly release an expired lease (token still required).' })),
|
|
58
|
+
}, { additionalProperties: false })
|
|
59
|
+
|
|
60
|
+
export const ImportLegacyInput = Type.Object({
|
|
61
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
62
|
+
sourcePath: Type.String({ description: 'Absolute path to the legacy JSON state file.' }),
|
|
63
|
+
}, { additionalProperties: false })
|
|
64
|
+
|
|
65
|
+
export const LegacyMapInput = Type.Object({
|
|
66
|
+
repoRoot: Type.String({ description: 'Absolute path inside the target Git repository.' }),
|
|
67
|
+
action: enumOf(LEGACY_MAP_ACTIONS, { description: 'Mapping operation.' }),
|
|
68
|
+
sourceHash: Type.Optional(Type.String({ description: 'Legacy snapshot sha256 (map).' })),
|
|
69
|
+
oldId: Type.Optional(Type.String({ description: 'Legacy mission id (map).' })),
|
|
70
|
+
newMissionId: Type.Optional(Type.String({ description: 'Current mission id; optional audit pointer.' })),
|
|
71
|
+
}, { additionalProperties: false })
|
|
72
|
+
|
|
73
|
+
export const ReloadInput = Type.Object({
|
|
74
|
+
repoRoot: Type.Optional(Type.String({ description: 'Optional repository root; when present the caller must be the tower.' })),
|
|
75
|
+
}, { additionalProperties: false, required: [] })
|