opencastle 0.27.0 → 0.27.2
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/bin/cli.mjs +6 -0
- package/dist/cli/agents.d.ts +3 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +161 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/baselines.d.ts +3 -0
- package/dist/cli/baselines.d.ts.map +1 -0
- package/dist/cli/baselines.js +128 -0
- package/dist/cli/baselines.js.map +1 -0
- package/dist/cli/convoy/dashboard-types.d.ts +146 -0
- package/dist/cli/convoy/dashboard-types.d.ts.map +1 -0
- package/dist/cli/convoy/dashboard-types.js +2 -0
- package/dist/cli/convoy/dashboard-types.js.map +1 -0
- package/dist/cli/convoy/engine.d.ts +67 -2
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +2036 -28
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/engine.test.js +1659 -70
- package/dist/cli/convoy/engine.test.js.map +1 -1
- package/dist/cli/convoy/event-schemas.d.ts +9 -0
- package/dist/cli/convoy/event-schemas.d.ts.map +1 -0
- package/dist/cli/convoy/event-schemas.js +185 -0
- package/dist/cli/convoy/event-schemas.js.map +1 -0
- package/dist/cli/convoy/events.d.ts +12 -1
- package/dist/cli/convoy/events.d.ts.map +1 -1
- package/dist/cli/convoy/events.js +186 -13
- package/dist/cli/convoy/events.js.map +1 -1
- package/dist/cli/convoy/events.test.js +325 -28
- package/dist/cli/convoy/events.test.js.map +1 -1
- package/dist/cli/convoy/expertise.d.ts +16 -0
- package/dist/cli/convoy/expertise.d.ts.map +1 -0
- package/dist/cli/convoy/expertise.js +121 -0
- package/dist/cli/convoy/expertise.js.map +1 -0
- package/dist/cli/convoy/expertise.test.d.ts +2 -0
- package/dist/cli/convoy/expertise.test.d.ts.map +1 -0
- package/dist/cli/convoy/expertise.test.js +96 -0
- package/dist/cli/convoy/expertise.test.js.map +1 -0
- package/dist/cli/convoy/export.test.js +1 -0
- package/dist/cli/convoy/export.test.js.map +1 -1
- package/dist/cli/convoy/formula.d.ts +19 -0
- package/dist/cli/convoy/formula.d.ts.map +1 -0
- package/dist/cli/convoy/formula.js +142 -0
- package/dist/cli/convoy/formula.js.map +1 -0
- package/dist/cli/convoy/formula.test.d.ts +2 -0
- package/dist/cli/convoy/formula.test.d.ts.map +1 -0
- package/dist/cli/convoy/formula.test.js +342 -0
- package/dist/cli/convoy/formula.test.js.map +1 -0
- package/dist/cli/convoy/gates.d.ts +128 -0
- package/dist/cli/convoy/gates.d.ts.map +1 -0
- package/dist/cli/convoy/gates.js +606 -0
- package/dist/cli/convoy/gates.js.map +1 -0
- package/dist/cli/convoy/gates.test.d.ts +2 -0
- package/dist/cli/convoy/gates.test.d.ts.map +1 -0
- package/dist/cli/convoy/gates.test.js +976 -0
- package/dist/cli/convoy/gates.test.js.map +1 -0
- package/dist/cli/convoy/health.d.ts +11 -0
- package/dist/cli/convoy/health.d.ts.map +1 -1
- package/dist/cli/convoy/health.js +54 -0
- package/dist/cli/convoy/health.js.map +1 -1
- package/dist/cli/convoy/health.test.js +56 -1
- package/dist/cli/convoy/health.test.js.map +1 -1
- package/dist/cli/convoy/issues.d.ts +8 -0
- package/dist/cli/convoy/issues.d.ts.map +1 -0
- package/dist/cli/convoy/issues.js +98 -0
- package/dist/cli/convoy/issues.js.map +1 -0
- package/dist/cli/convoy/issues.test.d.ts +2 -0
- package/dist/cli/convoy/issues.test.d.ts.map +1 -0
- package/dist/cli/convoy/issues.test.js +107 -0
- package/dist/cli/convoy/issues.test.js.map +1 -0
- package/dist/cli/convoy/knowledge.d.ts +5 -0
- package/dist/cli/convoy/knowledge.d.ts.map +1 -0
- package/dist/cli/convoy/knowledge.js +116 -0
- package/dist/cli/convoy/knowledge.js.map +1 -0
- package/dist/cli/convoy/knowledge.test.d.ts +2 -0
- package/dist/cli/convoy/knowledge.test.d.ts.map +1 -0
- package/dist/cli/convoy/knowledge.test.js +87 -0
- package/dist/cli/convoy/knowledge.test.js.map +1 -0
- package/dist/cli/convoy/lessons.d.ts +17 -0
- package/dist/cli/convoy/lessons.d.ts.map +1 -0
- package/dist/cli/convoy/lessons.js +149 -0
- package/dist/cli/convoy/lessons.js.map +1 -0
- package/dist/cli/convoy/lessons.test.d.ts +2 -0
- package/dist/cli/convoy/lessons.test.d.ts.map +1 -0
- package/dist/cli/convoy/lessons.test.js +135 -0
- package/dist/cli/convoy/lessons.test.js.map +1 -0
- package/dist/cli/convoy/lock.d.ts +13 -0
- package/dist/cli/convoy/lock.d.ts.map +1 -0
- package/dist/cli/convoy/lock.js +88 -0
- package/dist/cli/convoy/lock.js.map +1 -0
- package/dist/cli/convoy/lock.test.d.ts +2 -0
- package/dist/cli/convoy/lock.test.d.ts.map +1 -0
- package/dist/cli/convoy/lock.test.js +136 -0
- package/dist/cli/convoy/lock.test.js.map +1 -0
- package/dist/cli/convoy/log-merge.test.d.ts +2 -0
- package/dist/cli/convoy/log-merge.test.d.ts.map +1 -0
- package/dist/cli/convoy/log-merge.test.js +147 -0
- package/dist/cli/convoy/log-merge.test.js.map +1 -0
- package/dist/cli/convoy/merge.d.ts +4 -0
- package/dist/cli/convoy/merge.d.ts.map +1 -1
- package/dist/cli/convoy/merge.js +18 -1
- package/dist/cli/convoy/merge.js.map +1 -1
- package/dist/cli/convoy/merge.test.js +6 -7
- package/dist/cli/convoy/merge.test.js.map +1 -1
- package/dist/cli/convoy/partition.d.ts +51 -0
- package/dist/cli/convoy/partition.d.ts.map +1 -0
- package/dist/cli/convoy/partition.js +186 -0
- package/dist/cli/convoy/partition.js.map +1 -0
- package/dist/cli/convoy/partition.test.d.ts +2 -0
- package/dist/cli/convoy/partition.test.d.ts.map +1 -0
- package/dist/cli/convoy/partition.test.js +315 -0
- package/dist/cli/convoy/partition.test.js.map +1 -0
- package/dist/cli/convoy/pipeline.test.js +6 -0
- package/dist/cli/convoy/pipeline.test.js.map +1 -1
- package/dist/cli/convoy/store.d.ts +99 -7
- package/dist/cli/convoy/store.d.ts.map +1 -1
- package/dist/cli/convoy/store.js +764 -31
- package/dist/cli/convoy/store.js.map +1 -1
- package/dist/cli/convoy/store.test.js +1810 -18
- package/dist/cli/convoy/store.test.js.map +1 -1
- package/dist/cli/convoy/types.d.ts +427 -5
- package/dist/cli/convoy/types.d.ts.map +1 -1
- package/dist/cli/convoy/types.js +42 -1
- package/dist/cli/convoy/types.js.map +1 -1
- package/dist/cli/log.d.ts +11 -0
- package/dist/cli/log.d.ts.map +1 -1
- package/dist/cli/log.js +114 -2
- package/dist/cli/log.js.map +1 -1
- package/dist/cli/run/adapters/claude.d.ts +2 -0
- package/dist/cli/run/adapters/claude.d.ts.map +1 -1
- package/dist/cli/run/adapters/claude.js +89 -49
- package/dist/cli/run/adapters/claude.js.map +1 -1
- package/dist/cli/run/adapters/claude.test.d.ts +2 -0
- package/dist/cli/run/adapters/claude.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/claude.test.js +205 -0
- package/dist/cli/run/adapters/claude.test.js.map +1 -0
- package/dist/cli/run/adapters/copilot.d.ts +1 -0
- package/dist/cli/run/adapters/copilot.d.ts.map +1 -1
- package/dist/cli/run/adapters/copilot.js +84 -46
- package/dist/cli/run/adapters/copilot.js.map +1 -1
- package/dist/cli/run/adapters/copilot.test.d.ts +2 -0
- package/dist/cli/run/adapters/copilot.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/copilot.test.js +195 -0
- package/dist/cli/run/adapters/copilot.test.js.map +1 -0
- package/dist/cli/run/adapters/cursor.d.ts +1 -0
- package/dist/cli/run/adapters/cursor.d.ts.map +1 -1
- package/dist/cli/run/adapters/cursor.js +83 -47
- package/dist/cli/run/adapters/cursor.js.map +1 -1
- package/dist/cli/run/adapters/cursor.test.d.ts +2 -0
- package/dist/cli/run/adapters/cursor.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/cursor.test.js +129 -0
- package/dist/cli/run/adapters/cursor.test.js.map +1 -0
- package/dist/cli/run/adapters/opencode.d.ts +1 -0
- package/dist/cli/run/adapters/opencode.d.ts.map +1 -1
- package/dist/cli/run/adapters/opencode.js +81 -47
- package/dist/cli/run/adapters/opencode.js.map +1 -1
- package/dist/cli/run/adapters/opencode.test.d.ts +2 -0
- package/dist/cli/run/adapters/opencode.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/opencode.test.js +119 -0
- package/dist/cli/run/adapters/opencode.test.js.map +1 -0
- package/dist/cli/run/executor.js +1 -1
- package/dist/cli/run/executor.js.map +1 -1
- package/dist/cli/run/schema.d.ts.map +1 -1
- package/dist/cli/run/schema.js +245 -4
- package/dist/cli/run/schema.js.map +1 -1
- package/dist/cli/run/schema.test.js +669 -0
- package/dist/cli/run/schema.test.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +362 -22
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/types.d.ts +85 -2
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/cli/watch.d.ts +15 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +279 -0
- package/dist/cli/watch.js.map +1 -0
- package/package.json +5 -1
- package/src/cli/agents.ts +177 -0
- package/src/cli/baselines.ts +143 -0
- package/src/cli/convoy/TELEMETRY.md +203 -0
- package/src/cli/convoy/dashboard-types.ts +141 -0
- package/src/cli/convoy/engine.test.ts +1937 -70
- package/src/cli/convoy/engine.ts +2350 -40
- package/src/cli/convoy/event-schemas.ts +195 -0
- package/src/cli/convoy/events.test.ts +384 -39
- package/src/cli/convoy/events.ts +202 -16
- package/src/cli/convoy/expertise.test.ts +128 -0
- package/src/cli/convoy/expertise.ts +163 -0
- package/src/cli/convoy/export.test.ts +1 -0
- package/src/cli/convoy/formula.test.ts +405 -0
- package/src/cli/convoy/formula.ts +174 -0
- package/src/cli/convoy/gates.test.ts +1169 -0
- package/src/cli/convoy/gates.ts +774 -0
- package/src/cli/convoy/health.test.ts +64 -2
- package/src/cli/convoy/health.ts +80 -2
- package/src/cli/convoy/issues.test.ts +143 -0
- package/src/cli/convoy/issues.ts +136 -0
- package/src/cli/convoy/knowledge.test.ts +101 -0
- package/src/cli/convoy/knowledge.ts +132 -0
- package/src/cli/convoy/lessons.test.ts +188 -0
- package/src/cli/convoy/lessons.ts +164 -0
- package/src/cli/convoy/lock.test.ts +181 -0
- package/src/cli/convoy/lock.ts +103 -0
- package/src/cli/convoy/log-merge.test.ts +179 -0
- package/src/cli/convoy/merge.test.ts +6 -7
- package/src/cli/convoy/merge.ts +19 -1
- package/src/cli/convoy/partition.test.ts +423 -0
- package/src/cli/convoy/partition.ts +232 -0
- package/src/cli/convoy/pipeline.test.ts +6 -0
- package/src/cli/convoy/store.test.ts +2041 -20
- package/src/cli/convoy/store.ts +945 -46
- package/src/cli/convoy/types.ts +278 -4
- package/src/cli/log.ts +120 -2
- package/src/cli/run/adapters/claude.test.ts +234 -0
- package/src/cli/run/adapters/claude.ts +45 -5
- package/src/cli/run/adapters/copilot.test.ts +224 -0
- package/src/cli/run/adapters/copilot.ts +34 -4
- package/src/cli/run/adapters/cursor.test.ts +144 -0
- package/src/cli/run/adapters/cursor.ts +33 -2
- package/src/cli/run/adapters/opencode.test.ts +135 -0
- package/src/cli/run/adapters/opencode.ts +30 -2
- package/src/cli/run/executor.ts +1 -1
- package/src/cli/run/schema.test.ts +758 -0
- package/src/cli/run/schema.ts +300 -25
- package/src/cli/run.ts +341 -21
- package/src/cli/types.ts +86 -1
- package/src/cli/watch.ts +298 -0
- package/src/dashboard/dist/_astro/{index.DtnyD8a5.css → index.6L3_HsPT.css} +1 -1
- package/src/dashboard/dist/data/.gitkeep +0 -0
- package/src/dashboard/dist/data/convoy-list.json +1 -0
- package/src/dashboard/dist/data/overall-stats.json +24 -0
- package/src/dashboard/dist/index.html +701 -3
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/public/data/.gitkeep +0 -0
- package/src/dashboard/public/data/convoy-list.json +1 -0
- package/src/dashboard/public/data/overall-stats.json +24 -0
- package/src/dashboard/scripts/etl.test.ts +210 -0
- package/src/dashboard/scripts/etl.ts +108 -0
- package/src/dashboard/scripts/integration-test.ts +504 -0
- package/src/dashboard/src/pages/index.astro +854 -15
- package/src/dashboard/src/styles/dashboard.css +557 -1
- package/src/orchestrator/prompts/generate-convoy.prompt.md +212 -13
package/src/cli/convoy/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ConvoyStatus = 'pending' | 'running' | 'done' | 'failed' | 'gate-failed'
|
|
1
|
+
export type ConvoyStatus = 'pending' | 'running' | 'done' | 'failed' | 'gate-failed' | 'hook-failed'
|
|
2
2
|
|
|
3
3
|
export type ConvoyTaskStatus =
|
|
4
4
|
| 'pending'
|
|
@@ -6,8 +6,13 @@ export type ConvoyTaskStatus =
|
|
|
6
6
|
| 'running'
|
|
7
7
|
| 'done'
|
|
8
8
|
| 'failed'
|
|
9
|
+
| 'gate-failed'
|
|
10
|
+
| 'review-blocked'
|
|
9
11
|
| 'timed-out'
|
|
10
12
|
| 'skipped'
|
|
13
|
+
| 'hook-failed'
|
|
14
|
+
| 'disputed'
|
|
15
|
+
| 'wait-for-input'
|
|
11
16
|
|
|
12
17
|
export type WorkerStatus = 'spawned' | 'running' | 'done' | 'failed' | 'killed'
|
|
13
18
|
|
|
@@ -24,8 +29,11 @@ export interface ConvoyRecord {
|
|
|
24
29
|
finished_at: string | null
|
|
25
30
|
spec_yaml: string
|
|
26
31
|
total_tokens: number | null
|
|
27
|
-
total_cost_usd:
|
|
32
|
+
total_cost_usd: number | null
|
|
28
33
|
pipeline_id: string | null
|
|
34
|
+
circuit_state: string | null
|
|
35
|
+
review_tokens_total: number | null
|
|
36
|
+
review_budget: number | null
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
export interface TaskRecord {
|
|
@@ -51,7 +59,25 @@ export interface TaskRecord {
|
|
|
51
59
|
prompt_tokens: number | null
|
|
52
60
|
completion_tokens: number | null
|
|
53
61
|
total_tokens: number | null
|
|
54
|
-
cost_usd:
|
|
62
|
+
cost_usd: number | null
|
|
63
|
+
gates: string | null
|
|
64
|
+
on_exhausted: 'dlq' | 'skip' | 'stop'
|
|
65
|
+
injected: number
|
|
66
|
+
provenance: string | null
|
|
67
|
+
idempotency_key: string | null
|
|
68
|
+
current_step: number | null
|
|
69
|
+
total_steps: number | null
|
|
70
|
+
review_level: string | null
|
|
71
|
+
review_verdict: string | null
|
|
72
|
+
review_tokens: number | null
|
|
73
|
+
review_model: string | null
|
|
74
|
+
panel_attempts: number
|
|
75
|
+
dispute_id: string | null
|
|
76
|
+
drift_score: number | null
|
|
77
|
+
drift_retried: number
|
|
78
|
+
outputs?: string | null // JSON array of TaskOutput
|
|
79
|
+
inputs?: string | null // JSON array of TaskInput
|
|
80
|
+
discovered_issues?: string | null // JSON array
|
|
55
81
|
}
|
|
56
82
|
|
|
57
83
|
export interface WorkerRecord {
|
|
@@ -88,5 +114,253 @@ export interface PipelineRecord {
|
|
|
88
114
|
started_at: string | null
|
|
89
115
|
finished_at: string | null
|
|
90
116
|
total_tokens: number | null
|
|
91
|
-
total_cost_usd:
|
|
117
|
+
total_cost_usd: number | null
|
|
92
118
|
}
|
|
119
|
+
|
|
120
|
+
export interface BuiltInGatesConfig {
|
|
121
|
+
secret_scan?: boolean
|
|
122
|
+
blast_radius?: boolean
|
|
123
|
+
dependency_audit?: 'auto' | boolean
|
|
124
|
+
regression_test?: 'auto' | boolean
|
|
125
|
+
browser_test?: 'auto' | boolean
|
|
126
|
+
gate_timeout?: number
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
export interface BrowserTestConfig {
|
|
131
|
+
urls: string[]
|
|
132
|
+
check_console_errors?: boolean
|
|
133
|
+
visual_diff_threshold?: number
|
|
134
|
+
a11y?: boolean
|
|
135
|
+
severity_threshold?: 'critical' | 'serious' | 'moderate' | 'minor'
|
|
136
|
+
baselines_dir?: string
|
|
137
|
+
}
|
|
138
|
+
export interface GuardConfig {
|
|
139
|
+
enabled?: boolean // default: true
|
|
140
|
+
agent?: string // optional agent name (e.g. 'session-guard')
|
|
141
|
+
checks?: string[] // e.g. ['observability', 'cleanup', 'cost-report']
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface DlqRecord {
|
|
145
|
+
id: string
|
|
146
|
+
convoy_id: string
|
|
147
|
+
task_id: string
|
|
148
|
+
agent: string
|
|
149
|
+
failure_type: string
|
|
150
|
+
error_output: string | null
|
|
151
|
+
attempts: number
|
|
152
|
+
tokens_spent: number | null
|
|
153
|
+
escalation_task_id: string | null
|
|
154
|
+
resolved: number
|
|
155
|
+
resolution: string | null
|
|
156
|
+
created_at: string
|
|
157
|
+
resolved_at: string | null
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface CircuitBreakerConfig {
|
|
161
|
+
threshold?: number // failures before Open (default: 3)
|
|
162
|
+
cooldown_ms?: number // ms in Open before Half-Open (default: 300000 = 5min)
|
|
163
|
+
fallback_agent?: string // reassign pending tasks when circuit opens
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface TaskOutput {
|
|
167
|
+
name: string
|
|
168
|
+
type: 'file' | 'summary' | 'json'
|
|
169
|
+
description?: string
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface TaskInput {
|
|
173
|
+
from: string
|
|
174
|
+
name: string
|
|
175
|
+
as?: string
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface ArtifactRecord {
|
|
179
|
+
id: string
|
|
180
|
+
convoy_id: string
|
|
181
|
+
task_id: string
|
|
182
|
+
name: string
|
|
183
|
+
type: 'file' | 'summary' | 'json'
|
|
184
|
+
content: string
|
|
185
|
+
created_at: string
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface AgentIdentityRecord {
|
|
189
|
+
id: string
|
|
190
|
+
agent: string
|
|
191
|
+
convoy_id: string
|
|
192
|
+
task_id: string
|
|
193
|
+
summary: string
|
|
194
|
+
created_at: string
|
|
195
|
+
retention_days: number
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface StepCondition {
|
|
199
|
+
step: string // reference previous step by id
|
|
200
|
+
exitCode?: { eq?: number; ne?: number; gt?: number; lt?: number }
|
|
201
|
+
fileExists?: { path: string }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface TaskStep {
|
|
205
|
+
id?: string
|
|
206
|
+
prompt: string
|
|
207
|
+
gates?: string[]
|
|
208
|
+
max_retries?: number // inherits from task if omitted
|
|
209
|
+
if?: StepCondition
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface Hook {
|
|
213
|
+
type: 'review' | 'guard' | 'agent' | 'command' | 'validate'
|
|
214
|
+
name?: string
|
|
215
|
+
prompt?: string // for agent hooks
|
|
216
|
+
command?: string // for command hooks
|
|
217
|
+
on?: 'pre_task' | 'post_task' | 'post_convoy'
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface TaskStepRecord {
|
|
221
|
+
id: number
|
|
222
|
+
task_id: string
|
|
223
|
+
step_index: number
|
|
224
|
+
prompt: string
|
|
225
|
+
gates: string | null
|
|
226
|
+
status: string
|
|
227
|
+
exit_code: number | null
|
|
228
|
+
output: string | null
|
|
229
|
+
started_at: string | null
|
|
230
|
+
finished_at: string | null
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface WatchTrigger {
|
|
234
|
+
type: 'file-change' | 'cron' | 'git-push'
|
|
235
|
+
glob?: string // for file-change: glob pattern to watch
|
|
236
|
+
schedule?: string // for cron: 5-field cron expression
|
|
237
|
+
branch?: string // for git-push: branch name pattern
|
|
238
|
+
debounce_ms?: number // file-change debounce (default: 500ms)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface WatchConfig {
|
|
242
|
+
triggers: WatchTrigger[]
|
|
243
|
+
clear_scratchpad?: boolean // clear scratchpad on watch start
|
|
244
|
+
scratchpad_retention_days?: number // auto-clear scratchpad entries older than N days
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface ScratchpadRecord {
|
|
248
|
+
key: string
|
|
249
|
+
value: string
|
|
250
|
+
updated_at: string
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface MCPServerConfig {
|
|
254
|
+
name: string
|
|
255
|
+
type: string
|
|
256
|
+
local?: boolean
|
|
257
|
+
command?: string
|
|
258
|
+
args?: string[]
|
|
259
|
+
url?: string
|
|
260
|
+
config?: Record<string, unknown>
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// Discriminated union covering every canonical convoy event type.
|
|
265
|
+
// Each variant constrains the `data` shape that callers may pass to emit().
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
export type ConvoyEventType =
|
|
268
|
+
| { type: 'convoy_started'; data?: { name?: string } }
|
|
269
|
+
| { type: 'convoy_finished'; data?: { status: string } }
|
|
270
|
+
| { type: 'convoy_failed'; data?: { status: string; reason?: string } }
|
|
271
|
+
| { type: 'convoy_guard'; data?: { checks?: string[]; [key: string]: unknown } }
|
|
272
|
+
| { type: 'task_started'; data?: { worker_id?: string } }
|
|
273
|
+
| { type: 'task_done'; data?: { status?: string; retries?: number; worker_id?: string } }
|
|
274
|
+
| { type: 'task_failed'; data?: { reason: string; worker_id?: string; gate?: string; hook?: string } }
|
|
275
|
+
| { type: 'task_skipped'; data?: { reason: string } }
|
|
276
|
+
| { type: 'task_retried'; data?: { previous_status: string } }
|
|
277
|
+
| { type: 'task_waiting_input'; data?: { task_id?: string; reason?: string } }
|
|
278
|
+
| { type: 'review_started'; data?: { level: string; task_id?: string; model?: string } }
|
|
279
|
+
| {
|
|
280
|
+
type: 'review_verdict'
|
|
281
|
+
data?: {
|
|
282
|
+
level: string
|
|
283
|
+
verdict: string
|
|
284
|
+
tokens: number
|
|
285
|
+
model?: string
|
|
286
|
+
feedback_length?: number
|
|
287
|
+
budget_exceeded?: boolean
|
|
288
|
+
budget_downgrade?: boolean
|
|
289
|
+
budget_skip?: boolean
|
|
290
|
+
passes?: number
|
|
291
|
+
blocks?: number
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
| { type: 'dispute_opened'; data?: { dispute_id: string; task_id: string; agent?: string; reason?: string } }
|
|
295
|
+
| { type: 'dlq_entry_created'; data?: { dlq_id: string; task_id: string; agent?: string; attempts?: number } }
|
|
296
|
+
| { type: 'drift_check_result'; data?: { score?: number; threshold?: number; passed?: boolean } }
|
|
297
|
+
| { type: 'drift_detected'; data?: { score?: number; files?: string[] } }
|
|
298
|
+
| { type: 'circuit_breaker_tripped'; data?: { agent?: string; failure_count?: number; threshold?: number } }
|
|
299
|
+
| { type: 'circuit_breaker_fallback'; data?: { original_agent?: string; fallback_agent?: string; task_id?: string } }
|
|
300
|
+
| { type: 'circuit_breaker_blocked'; data?: { agent?: string; task_id?: string } }
|
|
301
|
+
| { type: 'merge_conflict_detected'; data?: { task_id?: string; files?: string[] } }
|
|
302
|
+
| { type: 'merge_conflict_failed'; data?: { task_id?: string; error?: string } }
|
|
303
|
+
| { type: 'file_injection_received'; data?: { task_id?: string; from_task?: string; name?: string } }
|
|
304
|
+
| { type: 'artifact_limit_reached'; data?: { task_id?: string; limit?: number; current?: number } }
|
|
305
|
+
| { type: 'agent_identity_captured'; data?: { agent?: string; task_id?: string } }
|
|
306
|
+
| { type: 'agent_identity_rejected'; data?: { agent?: string; task_id?: string; reason?: string } }
|
|
307
|
+
| { type: 'weak_area_skipped'; data?: { agent?: string; weak_areas?: string[]; task_files?: string[] } }
|
|
308
|
+
| { type: 'swarm_concurrency_update'; data?: { new_concurrency?: number; reason?: string } }
|
|
309
|
+
| { type: 'post_convoy_hook_failed'; data?: { hook?: string; error?: string } }
|
|
310
|
+
| { type: 'session'; data?: { agent?: string; model?: string; task?: string; outcome?: string; duration_min?: number } }
|
|
311
|
+
| { type: 'delegation'; data?: { agent?: string; model?: string; tier?: string; mechanism?: string; outcome?: string } }
|
|
312
|
+
| {
|
|
313
|
+
type: 'secret_leak_prevented'
|
|
314
|
+
data?: { original_type?: string; patterns?: string[]; task_id?: string; findings_count?: number; context?: string }
|
|
315
|
+
}
|
|
316
|
+
| { type: 'ndjson_write_failed'; data?: { original_type?: string } }
|
|
317
|
+
| { type: 'built_in_gate_result'; data?: { gate: string; passed: boolean; output?: string; level?: string } }
|
|
318
|
+
| { type: 'watch_started'; data?: { trigger_type?: string; pid?: number } }
|
|
319
|
+
| { type: 'watch_cycle_start'; data?: { cycle_number?: number; triggered_by?: string } }
|
|
320
|
+
| { type: 'watch_cycle_end'; data?: { cycle_number?: number; status?: string } }
|
|
321
|
+
| { type: 'watch_stopped'; data?: { reason?: string } }
|
|
322
|
+
| { type: 'worker_killed'; data?: { reason?: string; worker_id?: string; task_id?: string } }
|
|
323
|
+
| { type: 'discovered_issue'; data?: { task_id?: string; title?: string; file?: string; description?: string; severity?: string } }
|
|
324
|
+
|
|
325
|
+
/** All canonical convoy event type strings. Used for runtime validation. */
|
|
326
|
+
export const KNOWN_EVENT_TYPES: Set<string> = new Set<ConvoyEventType['type']>([
|
|
327
|
+
'convoy_started',
|
|
328
|
+
'convoy_finished',
|
|
329
|
+
'convoy_failed',
|
|
330
|
+
'convoy_guard',
|
|
331
|
+
'task_started',
|
|
332
|
+
'task_done',
|
|
333
|
+
'task_failed',
|
|
334
|
+
'task_skipped',
|
|
335
|
+
'task_retried',
|
|
336
|
+
'task_waiting_input',
|
|
337
|
+
'review_started',
|
|
338
|
+
'review_verdict',
|
|
339
|
+
'dispute_opened',
|
|
340
|
+
'dlq_entry_created',
|
|
341
|
+
'drift_check_result',
|
|
342
|
+
'drift_detected',
|
|
343
|
+
'circuit_breaker_tripped',
|
|
344
|
+
'circuit_breaker_fallback',
|
|
345
|
+
'circuit_breaker_blocked',
|
|
346
|
+
'merge_conflict_detected',
|
|
347
|
+
'merge_conflict_failed',
|
|
348
|
+
'file_injection_received',
|
|
349
|
+
'artifact_limit_reached',
|
|
350
|
+
'agent_identity_captured',
|
|
351
|
+
'agent_identity_rejected',
|
|
352
|
+
'weak_area_skipped',
|
|
353
|
+
'swarm_concurrency_update',
|
|
354
|
+
'post_convoy_hook_failed',
|
|
355
|
+
'session',
|
|
356
|
+
'delegation',
|
|
357
|
+
'secret_leak_prevented',
|
|
358
|
+
'ndjson_write_failed',
|
|
359
|
+
'built_in_gate_result',
|
|
360
|
+
'watch_started',
|
|
361
|
+
'watch_cycle_start',
|
|
362
|
+
'watch_cycle_end',
|
|
363
|
+
'watch_stopped',
|
|
364
|
+
'worker_killed',
|
|
365
|
+
'discovered_issue',
|
|
366
|
+
])
|
package/src/cli/log.ts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { mkdir, appendFile, stat } from 'node:fs/promises'
|
|
2
|
+
import { readdirSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
2
3
|
import { join, dirname } from 'node:path'
|
|
3
4
|
import type { CliContext } from './types.js'
|
|
4
5
|
|
|
5
6
|
const HELP = `
|
|
6
7
|
opencastle log [options]
|
|
8
|
+
opencastle log merge [--since <ISO-date>] [--until <ISO-date>] [--output <path>]
|
|
7
9
|
|
|
8
|
-
Append a structured event to the observability log (events.ndjson)
|
|
10
|
+
Append a structured event to the observability log (events.ndjson),
|
|
11
|
+
or merge per-convoy NDJSON files into a single file.
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
Subcommands:
|
|
14
|
+
merge Merge all .opencastle/logs/convoys/*.ndjson into convoy-events.ndjson
|
|
15
|
+
|
|
16
|
+
Options (log append):
|
|
11
17
|
--type <type> Event type (required): session|delegation|review|panel|dispute
|
|
12
18
|
--<field> <value> Any field from the event schema (see documentation)
|
|
13
19
|
--logs-dir <path> Override the logs directory path
|
|
14
20
|
--help, -h Show this help
|
|
15
21
|
|
|
22
|
+
Options (merge):
|
|
23
|
+
--since <ISO-date> Only include records at or after this date
|
|
24
|
+
--until <ISO-date> Only include records at or before this date
|
|
25
|
+
--output <path> Output path (default: .opencastle/logs/convoy-events.ndjson)
|
|
26
|
+
|
|
16
27
|
Array fields (comma-separated): file_partition, lessons_added, discoveries, reviewing_agents
|
|
17
28
|
Boolean fields: escalated, weighted
|
|
18
29
|
Numeric fields: auto-detected from value
|
|
@@ -21,6 +32,8 @@ const HELP = `
|
|
|
21
32
|
opencastle log --type session --agent Developer --model claude-sonnet-4-6 --task "Fix bug" --outcome success
|
|
22
33
|
opencastle log --type delegation --session_id feat/prj-1 --agent Developer --tier fast --mechanism sub-agent --outcome success
|
|
23
34
|
opencastle log --type panel --panel_key auth-review --verdict pass --pass_count 3 --block_count 0
|
|
35
|
+
opencastle log merge --since 2026-01-01 --output /tmp/merged.ndjson
|
|
36
|
+
opencastle log merge
|
|
24
37
|
`
|
|
25
38
|
|
|
26
39
|
const VALID_TYPES = ['session', 'delegation', 'review', 'panel', 'dispute']
|
|
@@ -59,6 +72,94 @@ export async function resolveLogsDir(override?: string | null): Promise<string>
|
|
|
59
72
|
return join(process.cwd(), '.opencastle', 'logs')
|
|
60
73
|
}
|
|
61
74
|
|
|
75
|
+
/** Merge per-convoy NDJSON files into a single deduplicated, sorted file. */
|
|
76
|
+
export async function mergeConvoyLogs(options: {
|
|
77
|
+
since?: string
|
|
78
|
+
until?: string
|
|
79
|
+
output?: string
|
|
80
|
+
basePath?: string
|
|
81
|
+
}): Promise<{ merged: number; deduplicated: number; written: number }> {
|
|
82
|
+
const base = options.basePath ?? process.cwd()
|
|
83
|
+
const convoysDir = join(base, '.opencastle', 'logs', 'convoys')
|
|
84
|
+
|
|
85
|
+
let files: string[] = []
|
|
86
|
+
try {
|
|
87
|
+
files = readdirSync(convoysDir)
|
|
88
|
+
.filter(f => f.endsWith('.ndjson'))
|
|
89
|
+
.map(f => join(convoysDir, f))
|
|
90
|
+
} catch {
|
|
91
|
+
return { merged: 0, deduplicated: 0, written: 0 }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (files.length === 0) {
|
|
95
|
+
return { merged: 0, deduplicated: 0, written: 0 }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const allRecords: Array<Record<string, unknown>> = []
|
|
99
|
+
let totalRead = 0
|
|
100
|
+
|
|
101
|
+
for (const file of files) {
|
|
102
|
+
const content = readFileSync(file, 'utf8')
|
|
103
|
+
const lines = content.split('\n').filter(l => l.trim())
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
try {
|
|
106
|
+
allRecords.push(JSON.parse(line) as Record<string, unknown>)
|
|
107
|
+
totalRead++
|
|
108
|
+
} catch {
|
|
109
|
+
// skip malformed lines
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Deduplicate by _event_id — keep first occurrence
|
|
115
|
+
const seen = new Set<unknown>()
|
|
116
|
+
const unique: Array<Record<string, unknown>> = []
|
|
117
|
+
for (const record of allRecords) {
|
|
118
|
+
const id = record['_event_id']
|
|
119
|
+
if (id !== undefined) {
|
|
120
|
+
if (seen.has(id)) continue
|
|
121
|
+
seen.add(id)
|
|
122
|
+
}
|
|
123
|
+
unique.push(record)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const deduplicatedCount = totalRead - unique.length
|
|
127
|
+
|
|
128
|
+
// Filter by since/until
|
|
129
|
+
let filtered = unique
|
|
130
|
+
if (options.since) {
|
|
131
|
+
const since = options.since
|
|
132
|
+
filtered = filtered.filter(r => {
|
|
133
|
+
const ts = r['timestamp'] as string | undefined
|
|
134
|
+
return ts !== undefined && ts >= since
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
if (options.until) {
|
|
138
|
+
const until = options.until
|
|
139
|
+
filtered = filtered.filter(r => {
|
|
140
|
+
const ts = r['timestamp'] as string | undefined
|
|
141
|
+
return ts !== undefined && ts <= until
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Sort by timestamp ascending
|
|
146
|
+
filtered.sort((a, b) => {
|
|
147
|
+
const ta = (a['timestamp'] as string) ?? ''
|
|
148
|
+
const tb = (b['timestamp'] as string) ?? ''
|
|
149
|
+
return ta < tb ? -1 : ta > tb ? 1 : 0
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
if (filtered.length === 0) {
|
|
153
|
+
return { merged: totalRead, deduplicated: deduplicatedCount, written: 0 }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const outputPath = options.output ?? join(base, '.opencastle', 'logs', 'convoy-events.ndjson')
|
|
157
|
+
mkdirSync(dirname(outputPath), { recursive: true })
|
|
158
|
+
writeFileSync(outputPath, filtered.map(r => JSON.stringify(r)).join('\n') + '\n', 'utf8')
|
|
159
|
+
|
|
160
|
+
return { merged: totalRead, deduplicated: deduplicatedCount, written: filtered.length }
|
|
161
|
+
}
|
|
162
|
+
|
|
62
163
|
/** Append a structured event record to events.ndjson. */
|
|
63
164
|
export async function appendEvent(
|
|
64
165
|
record: Record<string, unknown>,
|
|
@@ -77,6 +178,23 @@ export default async function log({ args }: CliContext): Promise<void> {
|
|
|
77
178
|
return
|
|
78
179
|
}
|
|
79
180
|
|
|
181
|
+
// merge subcommand
|
|
182
|
+
if (args[0] === 'merge') {
|
|
183
|
+
const mergeArgs = args.slice(1)
|
|
184
|
+
let since: string | undefined
|
|
185
|
+
let until: string | undefined
|
|
186
|
+
let output: string | undefined
|
|
187
|
+
for (let i = 0; i < mergeArgs.length; i++) {
|
|
188
|
+
const a = mergeArgs[i]
|
|
189
|
+
if (a === '--since' && i + 1 < mergeArgs.length) { since = mergeArgs[++i]; continue }
|
|
190
|
+
if (a === '--until' && i + 1 < mergeArgs.length) { until = mergeArgs[++i]; continue }
|
|
191
|
+
if (a === '--output' && i + 1 < mergeArgs.length) { output = mergeArgs[++i]; continue }
|
|
192
|
+
}
|
|
193
|
+
const result = await mergeConvoyLogs({ since, until, output })
|
|
194
|
+
console.log(` Merged: ${result.merged} records, Deduplicated: ${result.deduplicated}, Written: ${result.written}`)
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
80
198
|
let type: string | null = null
|
|
81
199
|
let logsDir: string | null = null
|
|
82
200
|
const fields: Record<string, unknown> = {}
|