dsh-tower 2.0.4 → 2.1.0
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 +152 -0
- package/COMPATIBILITY.md +39 -0
- package/CONTRIBUTING.md +157 -2
- package/README.en.md +46 -3
- package/README.md +44 -3
- package/SECURITY.md +27 -0
- package/adapters/default.js +12 -0
- package/adapters/store-fs.js +6 -0
- package/adapters/vcs-git.js +4 -0
- package/app/archive.js +190 -0
- package/app/batch.js +79 -0
- package/app/board.js +27 -0
- package/app/checkCompletion.js +89 -0
- package/app/enrichedMissions.js +26 -0
- package/app/findings.js +78 -0
- package/app/gate.js +126 -0
- package/app/init.js +291 -0
- package/app/merge.js +573 -0
- package/app/messages.js +66 -0
- package/app/mission.js +37 -0
- package/app/missionContext.js +14 -0
- package/app/plan.js +50 -0
- package/app/provision.js +193 -0
- package/app/read.js +113 -0
- package/app/rebase.js +302 -0
- package/app/recording.js +72 -0
- package/app/review.js +147 -0
- package/app/roster.js +123 -0
- package/app/spawn.js +125 -0
- package/app/teardown.js +205 -0
- package/app/tower.js +552 -0
- package/cordis.patch.yml +61 -53
- package/docs/README.md +3 -1
- package/docs/agents/domain.md +38 -0
- package/docs/agents/issue-tracker.md +46 -0
- package/docs/agents/triage-labels.md +15 -0
- package/docs/architecture/overview.md +114 -13
- package/docs/board.md +72 -0
- package/docs/guide/hot-reload.md +1 -1
- package/docs/guide/install-upgrade-migration.md +25 -1
- package/docs/guide/release.md +125 -0
- package/docs/operations/security.md +47 -2
- package/docs/reference/blockers.md +1 -1
- package/docs/reference/config.generated.md +70 -0
- package/docs/reference/config.md +8 -1
- package/docs/reference/doctor.md +58 -8
- package/docs/reference/errors.generated.md +41 -0
- package/docs/reference/errors.md +7 -3
- package/docs/reference/tools.generated.md +33 -0
- package/docs/reference/tools.md +7 -6
- package/docs/zh/README.md +3 -2
- package/docs/zh/guide/hot-reload.md +1 -1
- package/docs/zh/guide/install-upgrade-migration.md +18 -1
- package/docs/zh/operations/security.md +30 -1
- package/docs/zh/reference/blockers.md +1 -1
- package/docs/zh/reference/config.md +8 -1
- package/docs/zh/reference/doctor.md +35 -2
- package/docs/zh/reference/errors.md +6 -2
- package/docs/zh/reference/tools.md +3 -3
- 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 +427 -0
- package/domain/contracts/outputs.js +305 -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 +95 -0
- package/domain/contracts/tool-inputs/ops.js +75 -0
- package/domain/contracts/tool-inputs/shared.js +25 -0
- package/domain/errors.js +98 -0
- package/domain/gate.js +125 -0
- package/domain/integration/gate.js +190 -0
- package/domain/integration/init.js +261 -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 +167 -0
- package/domain/missions/findings.js +201 -0
- package/domain/missions/lifecycle.js +186 -0
- package/domain/missions/plan.js +149 -0
- package/domain/missions/review.js +179 -0
- package/domain/planning.js +121 -0
- package/domain/read/enrichedMissions.js +58 -0
- package/domain/read/missionContext.js +53 -0
- package/domain/read/views.js +141 -0
- package/domain/report.js +67 -0
- package/domain/roster.js +40 -0
- package/domain/scope.js +177 -0
- package/domain/state/archive.js +257 -0
- package/domain/state/migrations.js +145 -0
- package/domain/state/schema.js +239 -0
- package/domain/state/semantics.js +117 -0
- package/domain/state.js +183 -0
- package/domain/summary.js +116 -0
- package/domain/vocabulary.js +179 -0
- package/index.js +63 -20
- package/lib/archive.js +279 -0
- package/lib/briefing.js +176 -0
- package/lib/caseSensitivity.js +66 -0
- package/lib/compatibility.js +108 -0
- package/lib/config.js +33 -375
- package/lib/doctor.js +275 -4
- package/lib/git.js +60 -16
- package/lib/hmr.js +4 -3
- package/lib/leases.js +17 -7
- package/lib/legacy.js +39 -14
- package/lib/modeState.js +39 -0
- package/lib/modeStore.js +11 -43
- package/lib/persistence.js +356 -0
- package/lib/presets.js +35 -0
- package/lib/spawn-prechecks.js +168 -0
- package/lib/spawn-support.js +79 -0
- package/lib/stateFile.js +99 -0
- package/lib/store.js +67 -135
- package/lib/tests.js +17 -12
- package/lib/tool-registry.js +544 -0
- package/lib/workspaceLock.js +2 -2
- package/lib/worktreeOwnership.js +22 -10
- 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 +212 -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 +45 -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 +102 -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 +373 -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
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// Public type contract for the Tower bundle. This is a declaration-only file:
|
|
2
|
+
// the package remains zero-build and no .js is emitted.
|
|
3
|
+
//
|
|
4
|
+
// This is the single vocabulary shared by the published API and the pure
|
|
5
|
+
// domain modules under domain/, which are machine-checked against it by
|
|
6
|
+
// `tsc --noEmit` (checkJs). Keep this file free of value imports so consumers
|
|
7
|
+
// can load it without `allowJs`.
|
|
8
|
+
|
|
9
|
+
export type MissionStatus = 'planned' | 'active' | 'completed' | 'blocked' | 'paused' | 'merged' | 'abandoned'
|
|
10
|
+
export type MissionKind = 'build' | 'survey'
|
|
11
|
+
export type ReviewStatus = 'clean' | 'p1-1items' | 'p1-2items' | 'p1-3items' | 'p1-4items' | 'p1-5items' | 'p2-1items' | 'p2-2items' | 'p2-3items' | 'p2-4items' | 'p2-5items'
|
|
12
|
+
export type MergeVerdict = 'merge' | 'fix-then-merge' | 'hold'
|
|
13
|
+
export type ReviewPolicy = 'required-clean' | 'none'
|
|
14
|
+
export type FindingType = 'bug' | 'improve' | 'vuln' | 'idea'
|
|
15
|
+
export type FindingSeverity = 'low' | 'medium' | 'high' | 'critical'
|
|
16
|
+
export type FindingStatus = 'open' | 'triaged' | 'fixed' | 'wontfix'
|
|
17
|
+
export type Role = 'worker' | 'reviewer'
|
|
18
|
+
|
|
19
|
+
export type ErrorCode =
|
|
20
|
+
| 'invalid-argument'
|
|
21
|
+
| 'repo-invalid'
|
|
22
|
+
| 'not-initialized'
|
|
23
|
+
| 'mission-not-found'
|
|
24
|
+
| 'finding-not-found'
|
|
25
|
+
| 'invalid-transition'
|
|
26
|
+
| 'state-locked'
|
|
27
|
+
| 'state-corrupt'
|
|
28
|
+
| 'config-invalid'
|
|
29
|
+
| 'config-unknown-key'
|
|
30
|
+
| 'git-timeout'
|
|
31
|
+
| 'merge-conflict'
|
|
32
|
+
| 'merge-hook-failed'
|
|
33
|
+
| 'merge-intent-pending'
|
|
34
|
+
| 'base-dirty'
|
|
35
|
+
| 'stale-base'
|
|
36
|
+
| 'tip-moved'
|
|
37
|
+
| 'out-of-scope'
|
|
38
|
+
| 'worker-busy'
|
|
39
|
+
| 'worker-stale'
|
|
40
|
+
| 'spawn-unavailable'
|
|
41
|
+
| 'provider-missing'
|
|
42
|
+
| 'provider-cooldown'
|
|
43
|
+
| 'preset-missing'
|
|
44
|
+
| 'reviewer-prerequisite'
|
|
45
|
+
| 'worktree-dirty'
|
|
46
|
+
| 'workspace-busy'
|
|
47
|
+
| 'worker-limit'
|
|
48
|
+
| 'rate-limited'
|
|
49
|
+
| 'takeover-refused'
|
|
50
|
+
| 'reload-unavailable'
|
|
51
|
+
| 'reload-failed'
|
|
52
|
+
| 'tests-failed'
|
|
53
|
+
| 'tests-timeout'
|
|
54
|
+
| 'lease-busy'
|
|
55
|
+
| 'lease-token'
|
|
56
|
+
| 'batch-invalid'
|
|
57
|
+
| 'legacy-invalid'
|
|
58
|
+
| 'tool-internal'
|
|
59
|
+
|
|
60
|
+
/** One machine-readable gate/completion blocker: a stable code plus a detail. */
|
|
61
|
+
export interface Blocker {
|
|
62
|
+
code: string
|
|
63
|
+
detail: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Snapshot inputs for the merge/preflight review, roster and test gates. */
|
|
67
|
+
export interface MergeGateOptions {
|
|
68
|
+
mission: Mission
|
|
69
|
+
roster?: RosterEntry[]
|
|
70
|
+
branch?: string | null
|
|
71
|
+
branchTip?: string | null
|
|
72
|
+
review?: Review | null
|
|
73
|
+
config?: TowerConfig
|
|
74
|
+
activity?: Record<string, string | undefined>
|
|
75
|
+
parentStatus?: Record<string, string | null> | null
|
|
76
|
+
inactiveStates?: Iterable<string>
|
|
77
|
+
rebaseWaived?: boolean
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface MissionTask {
|
|
81
|
+
text: string
|
|
82
|
+
status: 'open' | 'done' | 'dropped'
|
|
83
|
+
note?: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface MissionWorktree {
|
|
87
|
+
path: string
|
|
88
|
+
branch: string
|
|
89
|
+
baseCommit: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface CompletionCheck {
|
|
93
|
+
name?: string
|
|
94
|
+
command?: string
|
|
95
|
+
exitCode?: number
|
|
96
|
+
evidence?: string
|
|
97
|
+
[key: string]: unknown
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface CompletionReport {
|
|
101
|
+
report?: string
|
|
102
|
+
/** UTF-8 byte length of `report`; absent on documents written before #89. */
|
|
103
|
+
reportBytes?: number
|
|
104
|
+
by?: string
|
|
105
|
+
at?: number
|
|
106
|
+
checks?: CompletionCheck[]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** One recorded test-discovery receipt (lib/tests.js). */
|
|
110
|
+
export interface TestRun {
|
|
111
|
+
version?: number
|
|
112
|
+
ok?: boolean
|
|
113
|
+
treeTip?: string | null
|
|
114
|
+
treeTipAfter?: string | null
|
|
115
|
+
[key: string]: unknown
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Record of the last successful rebase of a mission branch. */
|
|
119
|
+
export interface RebaseRecord {
|
|
120
|
+
at: number
|
|
121
|
+
oldTip: string
|
|
122
|
+
newTip?: string
|
|
123
|
+
baseTip?: string
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface Mission {
|
|
127
|
+
id: string
|
|
128
|
+
title: string
|
|
129
|
+
status: MissionStatus
|
|
130
|
+
kind: MissionKind
|
|
131
|
+
scope: string[]
|
|
132
|
+
deps: string[]
|
|
133
|
+
/**
|
|
134
|
+
* Optional because the pure domain helpers tolerate partial/legacy mission
|
|
135
|
+
* snapshots (`mission.tasks ?? []`); a mission loaded through validateState
|
|
136
|
+
* always has an array.
|
|
137
|
+
*/
|
|
138
|
+
tasks?: MissionTask[]
|
|
139
|
+
blockers?: string[]
|
|
140
|
+
reviewPolicy?: ReviewPolicy
|
|
141
|
+
reviewExemptionReason?: string | null
|
|
142
|
+
worktree?: MissionWorktree | null
|
|
143
|
+
owner?: string | null
|
|
144
|
+
notes?: string[]
|
|
145
|
+
completionReport?: CompletionReport | null
|
|
146
|
+
testRuns?: TestRun[]
|
|
147
|
+
rebase?: RebaseRecord | null
|
|
148
|
+
mergedCommit?: string | null
|
|
149
|
+
mergedAt?: number | null
|
|
150
|
+
scopeGeneration?: number
|
|
151
|
+
ownerGeneration?: number
|
|
152
|
+
pauseCause?: string | null
|
|
153
|
+
updatedAt?: number
|
|
154
|
+
references?: string[]
|
|
155
|
+
authorizationRef?: string | null
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Compact archived-mission entry (`archive/index.json`). It keeps enough of the
|
|
160
|
+
* mission for ordering, dependency lookups and id allocation without loading the
|
|
161
|
+
* full year file, and it is what `tower_status includeArchived` pages from (#73).
|
|
162
|
+
*/
|
|
163
|
+
export interface ArchiveIndexEntry {
|
|
164
|
+
id: string
|
|
165
|
+
title: string
|
|
166
|
+
status: MissionStatus
|
|
167
|
+
kind?: MissionKind
|
|
168
|
+
scope?: string[]
|
|
169
|
+
deps?: string[]
|
|
170
|
+
owner?: string | null
|
|
171
|
+
branch?: string | null
|
|
172
|
+
mergedCommit?: string | null
|
|
173
|
+
mergedAt?: number | null
|
|
174
|
+
updatedAt?: number | null
|
|
175
|
+
archivedAt: number
|
|
176
|
+
archivedYear: number
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Full archived record (`archive/<year>.json`): mission + linked history. */
|
|
180
|
+
export interface ArchivedMissionRecord extends ArchiveIndexEntry {
|
|
181
|
+
mission: Mission
|
|
182
|
+
reviews: Review[]
|
|
183
|
+
findings: Finding[]
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface Review {
|
|
187
|
+
id?: string
|
|
188
|
+
seq?: number
|
|
189
|
+
missionId: string
|
|
190
|
+
branch: string
|
|
191
|
+
round: number
|
|
192
|
+
status: string
|
|
193
|
+
mergeVerdict: MergeVerdict
|
|
194
|
+
reviewedCommit: string
|
|
195
|
+
at: number
|
|
196
|
+
findings?: string
|
|
197
|
+
checks?: string[]
|
|
198
|
+
decision?: string
|
|
199
|
+
reviewer?: string
|
|
200
|
+
reviewerRole?: string
|
|
201
|
+
breakGlassReason?: string
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface Finding {
|
|
205
|
+
id: string
|
|
206
|
+
type: FindingType
|
|
207
|
+
title: string
|
|
208
|
+
seq?: number | null
|
|
209
|
+
severity?: FindingSeverity | null
|
|
210
|
+
status?: FindingStatus | null
|
|
211
|
+
missionId?: string | null
|
|
212
|
+
branch?: string | null
|
|
213
|
+
reportedBy?: string | null
|
|
214
|
+
at?: number | null
|
|
215
|
+
updatedAt?: number | null
|
|
216
|
+
closedAt?: number | null
|
|
217
|
+
summary?: string
|
|
218
|
+
details?: string
|
|
219
|
+
suggestedFix?: string
|
|
220
|
+
location?: string
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface RosterEntry {
|
|
224
|
+
name: string
|
|
225
|
+
role: Role
|
|
226
|
+
state: string
|
|
227
|
+
/** Reserved rows may exist before an agent id is known. */
|
|
228
|
+
agentId?: string | null
|
|
229
|
+
missionId?: string | null
|
|
230
|
+
parentSessionId?: string | null
|
|
231
|
+
spawnTip?: string | null
|
|
232
|
+
stale?: boolean
|
|
233
|
+
[key: string]: unknown
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Public state contract. This stays hand-written rather than derived from
|
|
238
|
+
* `Static<typeof StateSchema>` because the validator schema is deliberately
|
|
239
|
+
* tolerant of legacy rows (`additionalProperties`, optional collections) and
|
|
240
|
+
* checks status vocabularies/worktree records semantically after the shape
|
|
241
|
+
* pass. `tests/type-contracts.ts` pins the schema/public-type overlap.
|
|
242
|
+
*/
|
|
243
|
+
export interface TowerState {
|
|
244
|
+
schemaVersion: number
|
|
245
|
+
revision: number
|
|
246
|
+
base: string
|
|
247
|
+
missions: Mission[]
|
|
248
|
+
repoRoot?: string
|
|
249
|
+
commonDir?: string
|
|
250
|
+
createdAt?: number
|
|
251
|
+
updatedAt?: number
|
|
252
|
+
dirtyBase?: boolean
|
|
253
|
+
/** Detected once at tower_init; null means "not detected yet" (#90). */
|
|
254
|
+
caseInsensitive?: boolean | null
|
|
255
|
+
towerAgentId?: string | null
|
|
256
|
+
snapshotCommit?: string | null
|
|
257
|
+
snapshotRef?: string | null
|
|
258
|
+
roster?: RosterEntry[]
|
|
259
|
+
reviews?: Review[]
|
|
260
|
+
findings?: Finding[]
|
|
261
|
+
messages?: Array<Record<string, unknown>>
|
|
262
|
+
batches?: Array<Record<string, unknown>>
|
|
263
|
+
legacySnapshots?: Array<Record<string, unknown>>
|
|
264
|
+
legacyMaps?: Array<Record<string, unknown>>
|
|
265
|
+
deliveries?: Array<Record<string, unknown>>
|
|
266
|
+
mergeIntent?: Record<string, unknown> | null
|
|
267
|
+
rebaseIntent?: Record<string, unknown> | null
|
|
268
|
+
messageSeq?: number
|
|
269
|
+
reviewSeq?: number
|
|
270
|
+
findingSeq?: number
|
|
271
|
+
batchSeq?: number
|
|
272
|
+
deliverySeq?: number
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export type TowerConfig = import('@deepseek-ai/schemastery').TypeS<typeof import('../domain/config/schema.js').Config>
|
|
276
|
+
|
|
277
|
+
/** Counts rendered by mission summaries and the board. */
|
|
278
|
+
export interface TaskCounts {
|
|
279
|
+
total: number
|
|
280
|
+
done: number
|
|
281
|
+
open: number
|
|
282
|
+
dropped: number
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Compact mission view returned by tower_plan/tower_status. */
|
|
286
|
+
export interface MissionSummary {
|
|
287
|
+
id: string
|
|
288
|
+
title: string
|
|
289
|
+
kind: MissionKind
|
|
290
|
+
status: MissionStatus
|
|
291
|
+
scope: string[]
|
|
292
|
+
deps: string[]
|
|
293
|
+
owner: string | null
|
|
294
|
+
worktree: MissionWorktree | null
|
|
295
|
+
blockers: string[]
|
|
296
|
+
tasks: TaskCounts
|
|
297
|
+
scopeGeneration: number
|
|
298
|
+
ownerGeneration: number
|
|
299
|
+
pauseCause: string | null
|
|
300
|
+
reviewPolicy: ReviewPolicy
|
|
301
|
+
mergedCommit: string | null
|
|
302
|
+
mergedAt: number | null
|
|
303
|
+
/** Size of the completion report without loading its body; null when absent. */
|
|
304
|
+
reportBytes: number | null
|
|
305
|
+
updatedAt?: number
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Full mission view returned by mission-scoped use cases. */
|
|
309
|
+
export interface MissionDetail extends Omit<MissionSummary, 'tasks'> {
|
|
310
|
+
tasks: MissionTask[]
|
|
311
|
+
notes: string[]
|
|
312
|
+
completionReport: CompletionReport | null
|
|
313
|
+
authorizationRef: string | null
|
|
314
|
+
references: string[]
|
|
315
|
+
reviewExemptionReason: string
|
|
316
|
+
testRuns: TestRun[]
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface FindingSummary {
|
|
320
|
+
id: string
|
|
321
|
+
seq: number | null
|
|
322
|
+
type: FindingType
|
|
323
|
+
title: string
|
|
324
|
+
severity: FindingSeverity | null
|
|
325
|
+
status: FindingStatus
|
|
326
|
+
missionId: string | null
|
|
327
|
+
branch: string | null
|
|
328
|
+
reportedBy: string | null
|
|
329
|
+
at: number | null
|
|
330
|
+
updatedAt: number | null
|
|
331
|
+
closedAt: number | null
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export interface ToolDefinition {
|
|
335
|
+
name: string
|
|
336
|
+
description: string
|
|
337
|
+
parameters: { type: 'object'; properties: Record<string, unknown>; required?: string[] }
|
|
338
|
+
execute(args: Record<string, unknown>, exec: { agent?: { id?: string } }): Promise<unknown>
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Owned Tower mode state exposed as the `tower` Cordis service. */
|
|
342
|
+
export interface ModeStateService {
|
|
343
|
+
has(key: string): boolean
|
|
344
|
+
get(key: string): unknown
|
|
345
|
+
set(key: string, value: unknown): this
|
|
346
|
+
delete(key: string): boolean
|
|
347
|
+
values(): IterableIterator<unknown>
|
|
348
|
+
entries(): IterableIterator<[string, unknown]>
|
|
349
|
+
replace(entries?: Iterable<[string, unknown]>): void
|
|
350
|
+
clear(): void
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
declare module '@deepseek-ai/cordis' {
|
|
354
|
+
interface Context {
|
|
355
|
+
tower?: ModeStateService
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
import type { Static, TSchema } from '@sinclair/typebox'
|
|
360
|
+
|
|
361
|
+
export type DefineTool = <S extends TSchema>(
|
|
362
|
+
ctx: any,
|
|
363
|
+
definition: {
|
|
364
|
+
name: string
|
|
365
|
+
description: string
|
|
366
|
+
parameters: S
|
|
367
|
+
execute(args: Static<S>, exec: any): Promise<unknown>
|
|
368
|
+
aliases?: any
|
|
369
|
+
requiresMissionId?: any
|
|
370
|
+
board?: any
|
|
371
|
+
output?: any
|
|
372
|
+
},
|
|
373
|
+
) => void
|
package/lib/errors.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// Stable tool error codes.
|
|
2
|
-
//
|
|
3
|
-
// Everything a Tower tool throws reaches the model as
|
|
4
|
-
// [tower:<code>] <original message>
|
|
5
|
-
// so callers (and tests) can branch on the code instead of matching prose.
|
|
6
|
-
// The catalog is documented in docs/reference/errors.md; scripts/check-docs.mjs
|
|
7
|
-
// keeps the two in sync.
|
|
8
|
-
import { GitError } from './git.js'
|
|
9
|
-
|
|
10
|
-
export const ERROR_CODES = {
|
|
11
|
-
'invalid-argument': 'A tool argument is missing, malformed, or not allowed.',
|
|
12
|
-
'repo-invalid': 'repoRoot is missing, relative, or not inside a Git repository.',
|
|
13
|
-
'not-initialized': 'Tower is not initialized for this repository; run tower_init.',
|
|
14
|
-
'mission-not-found': 'No mission with that id exists.',
|
|
15
|
-
'finding-not-found': 'No finding with that id exists.',
|
|
16
|
-
'invalid-transition': 'The requested state transition is not allowed.',
|
|
17
|
-
'state-locked': 'Another Tower writer holds the state lock.',
|
|
18
|
-
'state-corrupt': 'The Tower state file is unreadable or invalid.',
|
|
19
|
-
'config-invalid': 'Tower configuration is invalid.',
|
|
20
|
-
'config-unknown-key': 'Tower configuration has an unknown top-level key.',
|
|
21
|
-
'git-failed': 'A git command failed.',
|
|
22
|
-
'git-timeout': 'A git command timed out or exceeded its output buffer.',
|
|
23
|
-
'worktree-dirty': 'The mission worktree has uncommitted changes and cannot complete.',
|
|
24
|
-
'spawn-unavailable': 'The subagents service is unavailable.',
|
|
25
|
-
'provider-missing': 'No subagent provider is registered.',
|
|
26
|
-
'preset-missing': 'The configured agent preset is not registered.',
|
|
27
|
-
'reviewer-prerequisite': 'The reviewer spawn prerequisite is not met.',
|
|
28
|
-
'not-authorized': 'The calling agent role may not perform this action.',
|
|
29
|
-
'workspace-busy': 'Another Tower session owns this repository workspace lock.',
|
|
30
|
-
'worker-limit': 'The repository reached spawn.maxLiveWorkers live workers.',
|
|
31
|
-
'rate-limited': 'The tower_send per-minute rate limit was reached.',
|
|
32
|
-
'takeover-refused': 'The mission already has a live worker that was not stopped first.',
|
|
33
|
-
'reload-unavailable': 'The DSH HMR service is not available in this profile.',
|
|
34
|
-
'reload-failed': 'The HMR reload failed; check the DSH log and restart if needed.',
|
|
35
|
-
'tests-failed': 'Declared test discovery failed or did not meet its admission requirements.',
|
|
36
|
-
'tests-timeout': 'Test discovery exceeded its time or output budget.',
|
|
37
|
-
'lease-busy': 'Another holder owns the requested advisory lease.',
|
|
38
|
-
'lease-token': 'The lease release token does not match the current holder.',
|
|
39
|
-
'batch-invalid': 'The batch request or its close evidence is invalid.',
|
|
40
|
-
'legacy-invalid': 'The legacy snapshot or mapping is invalid.',
|
|
41
|
-
'tool-internal': 'An unexpected failure; read the message and the activity log.',
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class TowerToolError extends Error {
|
|
45
|
-
constructor(code, message, options = {}) {
|
|
46
|
-
super(`[tower:${code}] ${message}`, options.cause ? { cause: options.cause } : undefined)
|
|
47
|
-
this.name = 'TowerToolError'
|
|
48
|
-
this.code = code
|
|
49
|
-
this.retryable = options.retryable === true
|
|
50
|
-
this.nextAction = options.nextAction ?? null
|
|
51
|
-
this.details = options.details ?? null
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Ordered rules; the first match wins. Keep the specific patterns above the
|
|
56
|
-
// generic argument fallback so `no open task matching` never becomes internal.
|
|
57
|
-
const RULES = [
|
|
58
|
-
['not-initialized', /tower is not initialized/i],
|
|
59
|
-
['mission-not-found', /^unknown mission "/i],
|
|
60
|
-
['finding-not-found', /^unknown finding "/i],
|
|
61
|
-
['state-locked', /locked by another writer/i],
|
|
62
|
-
[
|
|
63
|
-
'state-corrupt',
|
|
64
|
-
/corrupt tower state|^tower state |^unsupported tower state|^invalid (?:mission|roster|message|finding|review) entry|^mission entry without id|^duplicate (?:mission|roster|message|finding|review)|^mission .* (?:has unknown status|has no tasks array|has no deps array)|^message .* has no readBy array|^finding .* has unknown status|^review .* has unknown (?:status|merge verdict)/i,
|
|
65
|
-
],
|
|
66
|
-
['config-unknown-key', /unknown key|unknown top-level key/i],
|
|
67
|
-
['config-invalid', /config (?:is invalid|must be an object)|repo config/i],
|
|
68
|
-
['lease-busy', /lease .* (?:held|owned)|another holder owns/i],
|
|
69
|
-
['lease-token', /lease .*token|token does not match/i],
|
|
70
|
-
['tests-timeout', /test discovery .*(?:timed out|exceeded|too large)/i],
|
|
71
|
-
['tests-failed', /test discovery (?:failed|did not)|tests? (?:failed|missing|did not pass)/i],
|
|
72
|
-
['batch-invalid', /batch (?:is invalid|close|member|receipt)/i],
|
|
73
|
-
['legacy-invalid', /legacy (?:snapshot|map|source)|unsupported legacy/i],
|
|
74
|
-
['spawn-unavailable', /subagents service is unavailable/i],
|
|
75
|
-
['provider-missing', /no subagent provider/i],
|
|
76
|
-
['preset-missing', /agent preset .*is not registered/i],
|
|
77
|
-
['reviewer-prerequisite', /reviewer needs an existing|reviewer requires a completed/i],
|
|
78
|
-
['repo-invalid', /repoRoot is required|repoRoot must be an absolute path/i],
|
|
79
|
-
['workspace-busy', /workspace .*lock|another tower session owns/i],
|
|
80
|
-
['worker-limit', /live worker limit|too many live workers|worker limit reached/i],
|
|
81
|
-
['rate-limited', /rate limit|too many tower messages/i],
|
|
82
|
-
['takeover-refused', /takeover refused|cannot take over/i],
|
|
83
|
-
['not-authorized', /only the tower|only an assigned reviewer|only the tower or a worker|does not own mission|not authorized|may not perform this action/i],
|
|
84
|
-
[
|
|
85
|
-
'invalid-transition',
|
|
86
|
-
/cannot move from|cannot be completed from that state|is already completed|merged is recorded only|can only be widened|only a completed mission|reviewers may list and read|still has \d+ open task|has unresolved blockers|has no worktree|has no commits since|cannot complete with \d+ unread inbox|not assigned to mission|is a [a-z]+, not a worker|cannot reanchor base|worktree is missing|worktree .* is not a Git worktree|belongs to a different repository|is on .* expected|has \d+ live worker|already has a live worker|cannot reopen a terminal mission|stopped worker cannot report completion|can only be spawned|violates? (?:the )?mission (?:state|transition)/i,
|
|
87
|
-
],
|
|
88
|
-
[
|
|
89
|
-
'invalid-argument',
|
|
90
|
-
/requires |is required|must be (?:a|an|one|an integer|non-empty|printable)|must not be|must be one of|no open task matching|unknown mission status|unknown finding severity|unknown finding type|not an array|is not an array|scope must|title must|checks must|role must|timeoutMs must|repoRoot is required|only a list filter|snapshotDirty was requested|refusing to send a message to yourself|mission has \d+ live worker|roster name already used|is already in the roster|depends on unknown mission/i,
|
|
91
|
-
],
|
|
92
|
-
]
|
|
93
|
-
|
|
94
|
-
function originalMessage(error) {
|
|
95
|
-
return String(error?.message ?? error)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Wrap any thrown value into a TowerToolError with a stable code. */
|
|
99
|
-
export function classifyError(error) {
|
|
100
|
-
if (error instanceof TowerToolError) return error
|
|
101
|
-
if (error && typeof error.code === 'string' && ERROR_CODES[error.code]) {
|
|
102
|
-
return new TowerToolError(error.code, originalMessage(error), {
|
|
103
|
-
cause: error,
|
|
104
|
-
retryable: error.retryable === true,
|
|
105
|
-
nextAction: error.nextAction ?? null,
|
|
106
|
-
details: error.details ?? null,
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
if (error instanceof GitError || error?.name === 'GitError') {
|
|
110
|
-
const message = originalMessage(error)
|
|
111
|
-
if (/not a git repository/i.test(message)) return new TowerToolError('repo-invalid', message, { cause: error })
|
|
112
|
-
const code = error?.timedOut === true || error?.bufferOverflow === true ? 'git-timeout' : 'git-failed'
|
|
113
|
-
return new TowerToolError(code, message, { cause: error })
|
|
114
|
-
}
|
|
115
|
-
const message = originalMessage(error)
|
|
116
|
-
const RETRYABLE = new Set(['state-locked', 'rate-limited', 'git-timeout', 'workspace-busy', 'merge-intent-pending'])
|
|
117
|
-
for (const [code, pattern] of RULES) {
|
|
118
|
-
if (pattern.test(message)) {
|
|
119
|
-
return new TowerToolError(code, message, { cause: error, retryable: RETRYABLE.has(code) })
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return new TowerToolError('tool-internal', message, { cause: error })
|
|
123
|
-
}
|