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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import type { CliContext } from './types.js'
|
|
4
|
+
import { c } from './prompt.js'
|
|
5
|
+
|
|
6
|
+
const HELP = `
|
|
7
|
+
opencastle agents [subcommand] [options]
|
|
8
|
+
|
|
9
|
+
Manage persistent agent identities.
|
|
10
|
+
|
|
11
|
+
Subcommands:
|
|
12
|
+
list List all agent identities (agent, task count, latest date)
|
|
13
|
+
inspect <agent> Show summaries for a specific agent
|
|
14
|
+
purge <agent> Delete all identities for an agent
|
|
15
|
+
compact --older-than <d> Delete identities older than N days
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--older-than <days> Days threshold for compact subcommand
|
|
19
|
+
--yes, -y Skip confirmation prompt
|
|
20
|
+
--help, -h Show this help
|
|
21
|
+
`
|
|
22
|
+
|
|
23
|
+
interface AgentsOptions {
|
|
24
|
+
subcommand: string | null
|
|
25
|
+
agentName: string | null
|
|
26
|
+
olderThan: number | null
|
|
27
|
+
yes: boolean
|
|
28
|
+
help: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseAgentsArgs(args: string[]): AgentsOptions {
|
|
32
|
+
const opts: AgentsOptions = {
|
|
33
|
+
subcommand: null,
|
|
34
|
+
agentName: null,
|
|
35
|
+
olderThan: null,
|
|
36
|
+
yes: false,
|
|
37
|
+
help: false,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < args.length; i++) {
|
|
41
|
+
const arg = args[i]
|
|
42
|
+
switch (arg) {
|
|
43
|
+
case '--help':
|
|
44
|
+
case '-h':
|
|
45
|
+
opts.help = true
|
|
46
|
+
break
|
|
47
|
+
case '--older-than':
|
|
48
|
+
if (i + 1 >= args.length) { console.error(' \u2717 --older-than requires a number'); process.exit(1) }
|
|
49
|
+
opts.olderThan = parseInt(args[++i], 10)
|
|
50
|
+
if (!Number.isFinite(opts.olderThan) || opts.olderThan < 1) {
|
|
51
|
+
console.error(' \u2717 --older-than must be a positive integer')
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
break
|
|
55
|
+
case '--yes':
|
|
56
|
+
case '-y':
|
|
57
|
+
opts.yes = true
|
|
58
|
+
break
|
|
59
|
+
default:
|
|
60
|
+
if (arg.startsWith('--')) {
|
|
61
|
+
console.error(` \u2717 Unknown option: ${arg}`)
|
|
62
|
+
console.log(HELP)
|
|
63
|
+
process.exit(1)
|
|
64
|
+
}
|
|
65
|
+
if (!opts.subcommand) {
|
|
66
|
+
opts.subcommand = arg
|
|
67
|
+
} else if (!opts.agentName) {
|
|
68
|
+
opts.agentName = arg
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return opts
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default async function agents({ args }: CliContext): Promise<void> {
|
|
77
|
+
const opts = parseAgentsArgs(args)
|
|
78
|
+
|
|
79
|
+
if (opts.help || !opts.subcommand) {
|
|
80
|
+
console.log(HELP)
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const dbPath = resolve(process.cwd(), '.opencastle', 'convoy.db')
|
|
85
|
+
if (!existsSync(dbPath)) {
|
|
86
|
+
console.log(' No convoy database found at .opencastle/convoy.db')
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const { createConvoyStore } = await import('./convoy/store.js')
|
|
91
|
+
const store = createConvoyStore(dbPath)
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
switch (opts.subcommand) {
|
|
95
|
+
case 'list': {
|
|
96
|
+
const summaries = store.listAgentIdentitySummary()
|
|
97
|
+
if (summaries.length === 0) {
|
|
98
|
+
console.log(' No agent identities found.')
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
console.log(`\n Agent Identities (${summaries.length} agents):\n`)
|
|
102
|
+
console.log(` ${'Agent'.padEnd(25)} ${'Tasks'.padEnd(8)} Latest`)
|
|
103
|
+
console.log(` ${'\u2500'.repeat(25)} ${'\u2500'.repeat(8)} ${'\u2500'.repeat(20)}`)
|
|
104
|
+
for (const s of summaries) {
|
|
105
|
+
console.log(` ${s.agent.padEnd(25)} ${String(s.task_count).padEnd(8)} ${s.latest_date}`)
|
|
106
|
+
}
|
|
107
|
+
console.log()
|
|
108
|
+
break
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
case 'inspect': {
|
|
112
|
+
if (!opts.agentName) {
|
|
113
|
+
console.error(' \u2717 inspect requires an agent name: opencastle agents inspect <agent>')
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
const identities = store.getAgentIdentities(opts.agentName, 100)
|
|
117
|
+
if (identities.length === 0) {
|
|
118
|
+
console.log(` No identities found for agent "${opts.agentName}".`)
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
console.log(`\n Agent: ${opts.agentName} (${identities.length} identities)\n`)
|
|
122
|
+
for (const id of identities) {
|
|
123
|
+
console.log(` ${c.dim('\u2500'.repeat(60))}`)
|
|
124
|
+
console.log(` Task: ${id.task_id} | Convoy: ${id.convoy_id}`)
|
|
125
|
+
console.log(` Date: ${id.created_at} | Retention: ${id.retention_days}d`)
|
|
126
|
+
console.log(` Summary:`)
|
|
127
|
+
const lines = id.summary.split('\n')
|
|
128
|
+
for (const line of lines.slice(0, 10)) {
|
|
129
|
+
console.log(` ${line}`)
|
|
130
|
+
}
|
|
131
|
+
if (lines.length > 10) {
|
|
132
|
+
console.log(` ${c.dim(`... ${lines.length - 10} more lines`)}`)
|
|
133
|
+
}
|
|
134
|
+
console.log()
|
|
135
|
+
}
|
|
136
|
+
break
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
case 'purge': {
|
|
140
|
+
if (!opts.agentName) {
|
|
141
|
+
console.error(' \u2717 purge requires an agent name: opencastle agents purge <agent>')
|
|
142
|
+
process.exit(1)
|
|
143
|
+
}
|
|
144
|
+
if (!opts.yes) {
|
|
145
|
+
const existing = store.getAgentIdentities(opts.agentName, 1000)
|
|
146
|
+
if (existing.length === 0) {
|
|
147
|
+
console.log(` No identities found for agent "${opts.agentName}".`)
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
console.log(`\n This will delete ${existing.length} identities for agent "${opts.agentName}".`)
|
|
151
|
+
console.log(` Use --yes or -y to confirm.`)
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
const deleted = store.purgeAgentIdentities(opts.agentName)
|
|
155
|
+
console.log(` \u2713 Purged ${deleted} identities for agent "${opts.agentName}".`)
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case 'compact': {
|
|
160
|
+
if (!opts.olderThan) {
|
|
161
|
+
console.error(' \u2717 compact requires --older-than <days>')
|
|
162
|
+
process.exit(1)
|
|
163
|
+
}
|
|
164
|
+
const deleted = store.deleteAgentIdentitiesOlderThan(opts.olderThan)
|
|
165
|
+
console.log(` \u2713 Deleted ${deleted} identities older than ${opts.olderThan} days.`)
|
|
166
|
+
break
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
default:
|
|
170
|
+
console.error(` \u2717 Unknown subcommand: ${opts.subcommand}`)
|
|
171
|
+
console.log(HELP)
|
|
172
|
+
process.exit(1)
|
|
173
|
+
}
|
|
174
|
+
} finally {
|
|
175
|
+
store.close()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
+
import { join, basename } from 'node:path'
|
|
3
|
+
import type { CliContext } from './types.js'
|
|
4
|
+
import { scanForSecrets } from './convoy/gates.js'
|
|
5
|
+
|
|
6
|
+
const HELP = `
|
|
7
|
+
opencastle baselines <subcommand> [options]
|
|
8
|
+
|
|
9
|
+
Manage visual regression baselines.
|
|
10
|
+
|
|
11
|
+
Subcommands:
|
|
12
|
+
update --slug <name> --from <file> Update a baseline from a PNG file
|
|
13
|
+
list List all baselines
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--slug <name> Baseline name (used as filename)
|
|
17
|
+
--from <file> Source PNG file path
|
|
18
|
+
--dir <path> Baselines directory (default: .opencastle/baselines)
|
|
19
|
+
--help, -h Show this help
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
interface BaselinesOptions {
|
|
23
|
+
subcommand: string | null
|
|
24
|
+
slug: string | null
|
|
25
|
+
from: string | null
|
|
26
|
+
dir: string
|
|
27
|
+
help: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseBaselinesArgs(args: string[]): BaselinesOptions {
|
|
31
|
+
const opts: BaselinesOptions = {
|
|
32
|
+
subcommand: null,
|
|
33
|
+
slug: null,
|
|
34
|
+
from: null,
|
|
35
|
+
dir: '.opencastle/baselines',
|
|
36
|
+
help: false,
|
|
37
|
+
}
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
const arg = args[i]
|
|
40
|
+
switch (arg) {
|
|
41
|
+
case '--help':
|
|
42
|
+
case '-h':
|
|
43
|
+
opts.help = true
|
|
44
|
+
break
|
|
45
|
+
case '--slug':
|
|
46
|
+
if (i + 1 >= args.length) {
|
|
47
|
+
console.error(' \u2717 --slug requires a name')
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
opts.slug = args[++i]
|
|
51
|
+
break
|
|
52
|
+
case '--from':
|
|
53
|
+
if (i + 1 >= args.length) {
|
|
54
|
+
console.error(' \u2717 --from requires a file path')
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
opts.from = args[++i]
|
|
58
|
+
break
|
|
59
|
+
case '--dir':
|
|
60
|
+
if (i + 1 >= args.length) {
|
|
61
|
+
console.error(' \u2717 --dir requires a path')
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
64
|
+
opts.dir = args[++i]
|
|
65
|
+
break
|
|
66
|
+
default:
|
|
67
|
+
if (arg.startsWith('--')) {
|
|
68
|
+
console.error(` \u2717 Unknown option: ${arg}`)
|
|
69
|
+
console.log(HELP)
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
if (!opts.subcommand) {
|
|
73
|
+
opts.subcommand = arg
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return opts
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default async function baselines({ args }: CliContext): Promise<void> {
|
|
81
|
+
const opts = parseBaselinesArgs(args)
|
|
82
|
+
|
|
83
|
+
if (opts.help || !opts.subcommand) {
|
|
84
|
+
console.log(HELP)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
switch (opts.subcommand) {
|
|
89
|
+
case 'update': {
|
|
90
|
+
if (!opts.slug) {
|
|
91
|
+
console.error(' \u2717 update requires --slug <name>')
|
|
92
|
+
process.exit(1)
|
|
93
|
+
}
|
|
94
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(opts.slug)) {
|
|
95
|
+
console.error(' \u2717 Slug must contain only alphanumeric characters, hyphens, and underscores')
|
|
96
|
+
process.exit(1)
|
|
97
|
+
}
|
|
98
|
+
if (!opts.from) {
|
|
99
|
+
console.error(' \u2717 update requires --from <file>')
|
|
100
|
+
process.exit(1)
|
|
101
|
+
}
|
|
102
|
+
if (!existsSync(opts.from)) {
|
|
103
|
+
console.error(` \u2717 Source file not found: ${opts.from}`)
|
|
104
|
+
process.exit(1)
|
|
105
|
+
}
|
|
106
|
+
const data = readFileSync(opts.from)
|
|
107
|
+
const scan = scanForSecrets(data.toString('base64'), opts.from)
|
|
108
|
+
if (!scan.clean) {
|
|
109
|
+
console.error(' \u2717 Source file contains potential secrets \u2014 baseline not updated')
|
|
110
|
+
process.exit(1)
|
|
111
|
+
}
|
|
112
|
+
mkdirSync(opts.dir, { recursive: true })
|
|
113
|
+
const dest = join(opts.dir, `${opts.slug}.png`)
|
|
114
|
+
writeFileSync(dest, data)
|
|
115
|
+
console.log(` \u2713 Baseline updated: ${dest}`)
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
case 'list': {
|
|
120
|
+
if (!existsSync(opts.dir)) {
|
|
121
|
+
console.log(` No baselines directory found at ${opts.dir}`)
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
const files = readdirSync(opts.dir).filter((f) => f.endsWith('.png'))
|
|
125
|
+
if (files.length === 0) {
|
|
126
|
+
console.log(' No baselines found.')
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
console.log(`\n Baselines in ${opts.dir}:\n`)
|
|
130
|
+
for (const file of files) {
|
|
131
|
+
const stats = statSync(join(opts.dir, file))
|
|
132
|
+
console.log(` ${basename(file, '.png').padEnd(30)} ${(stats.size / 1024).toFixed(1)} KB`)
|
|
133
|
+
}
|
|
134
|
+
console.log()
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
default:
|
|
139
|
+
console.error(` \u2717 Unknown subcommand: ${opts.subcommand}`)
|
|
140
|
+
console.log(HELP)
|
|
141
|
+
process.exit(1)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Convoy Telemetry Model
|
|
2
|
+
|
|
3
|
+
How Convoy concepts map to [OpenTelemetry](https://opentelemetry.io/) semantics.
|
|
4
|
+
|
|
5
|
+
## Conceptual Mapping
|
|
6
|
+
|
|
7
|
+
| Convoy Concept | OTel Concept | ID Field | Description |
|
|
8
|
+
|---------------|-------------|----------|-------------|
|
|
9
|
+
| **Convoy** | Trace | `convoy_id` → `trace_id` | A single execution run of a `.convoy.yml` spec |
|
|
10
|
+
| **Task** | Span | `task_id` → `span_id` | One unit of work within a convoy |
|
|
11
|
+
| **TaskStep** | Sub-span | `step_index` | Sequential steps within a multi-step task |
|
|
12
|
+
| **Event** | Log / SpanEvent | `type` | Structured occurrence during execution |
|
|
13
|
+
| **Metrics** | Derived aggregates | — | Computed from events (tokens, cost, duration) |
|
|
14
|
+
|
|
15
|
+
### ID Correlation
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
trace_id = convoy_id (globally unique, set at convoy creation)
|
|
19
|
+
span_id = task_id (unique within convoy, from spec)
|
|
20
|
+
worker_id = worker trace (ephemeral, tied to adapter process)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Every event carries `convoy_id`, `task_id`, and `worker_id` (all nullable) to enable correlation across the trace hierarchy.
|
|
24
|
+
|
|
25
|
+
## Storage
|
|
26
|
+
|
|
27
|
+
- **Primary**: SQLite (`convoy.db`) — durable, queryable, crash-safe
|
|
28
|
+
- **Supplementary**: NDJSON (`convoy-events.ndjson`) — append-only log for streaming/grep
|
|
29
|
+
|
|
30
|
+
SQLite is the source of truth. NDJSON is replayed from SQLite on crash recovery via `recoverNdjson()`.
|
|
31
|
+
|
|
32
|
+
### Write Strategy (v1)
|
|
33
|
+
|
|
34
|
+
NDJSON writes use synchronous `appendFileSync` + `fsyncSync` per event. This ensures crash-safety — every event is durable before the engine proceeds. Trade-offs:
|
|
35
|
+
|
|
36
|
+
- **Latency**: ~1-2ms per event (sync I/O). For convoys with <10,000 events this is negligible.
|
|
37
|
+
- **Throughput**: Not suitable for >10,000 events/second workloads.
|
|
38
|
+
- **Crash-safety**: Every event is fsynced before the engine continues, so a crash never loses the last event.
|
|
39
|
+
|
|
40
|
+
An async buffered writer is deferred as an optimization for Phase 5 if profiling shows sync writes become a bottleneck.
|
|
41
|
+
|
|
42
|
+
## Event Type Reference
|
|
43
|
+
|
|
44
|
+
All 39 canonical event types emitted by the convoy engine.
|
|
45
|
+
|
|
46
|
+
### Convoy Lifecycle
|
|
47
|
+
|
|
48
|
+
| Event Type | Source | Data Fields |
|
|
49
|
+
|-----------|--------|-------------|
|
|
50
|
+
| `convoy_started` | engine.ts | `name?: string` |
|
|
51
|
+
| `convoy_finished` | engine.ts | `status: string` |
|
|
52
|
+
| `convoy_failed` | engine.ts | `status: string; reason?: string` |
|
|
53
|
+
| `convoy_guard` | engine.ts | `checks?: string[]` |
|
|
54
|
+
|
|
55
|
+
### Task Lifecycle
|
|
56
|
+
|
|
57
|
+
| Event Type | Source | Data Fields |
|
|
58
|
+
|-----------|--------|-------------|
|
|
59
|
+
| `task_started` | engine.ts | `worker_id?: string` |
|
|
60
|
+
| `task_done` | engine.ts | `status?: string; retries?: number; worker_id?: string` |
|
|
61
|
+
| `task_failed` | engine.ts | `reason: string; worker_id?: string; gate?: string; hook?: string` |
|
|
62
|
+
| `task_skipped` | engine.ts | `reason: string` |
|
|
63
|
+
| `task_retried` | engine.ts | `previous_status: string` |
|
|
64
|
+
| `task_waiting_input` | engine.ts | `task_id?: string; reason?: string` |
|
|
65
|
+
|
|
66
|
+
### Review & Disputes
|
|
67
|
+
|
|
68
|
+
| Event Type | Source | Data Fields |
|
|
69
|
+
|-----------|--------|-------------|
|
|
70
|
+
| `review_started` | engine.ts | `level: string; task_id?: string; model?: string` |
|
|
71
|
+
| `review_verdict` | engine.ts | `level: string; verdict: string; tokens: number; model?: string; feedback_length?: number; budget_exceeded?: boolean; budget_downgrade?: boolean; budget_skip?: boolean; passes?: number; blocks?: number` |
|
|
72
|
+
| `dispute_opened` | engine.ts | `dispute_id: string; task_id: string; agent?: string; reason?: string` |
|
|
73
|
+
| `dlq_entry_created` | engine.ts | `dlq_id: string; task_id: string; agent?: string; attempts?: number` |
|
|
74
|
+
|
|
75
|
+
### Drift Detection
|
|
76
|
+
|
|
77
|
+
| Event Type | Source | Data Fields |
|
|
78
|
+
|-----------|--------|-------------|
|
|
79
|
+
| `drift_check_result` | engine.ts | `score?: number; threshold?: number; passed?: boolean` |
|
|
80
|
+
| `drift_detected` | engine.ts | `score?: number; files?: string[]` |
|
|
81
|
+
|
|
82
|
+
### Circuit Breaker
|
|
83
|
+
|
|
84
|
+
| Event Type | Source | Data Fields |
|
|
85
|
+
|-----------|--------|-------------|
|
|
86
|
+
| `circuit_breaker_tripped` | engine.ts | `agent?: string; failure_count?: number; threshold?: number` |
|
|
87
|
+
| `circuit_breaker_fallback` | engine.ts | `original_agent?: string; fallback_agent?: string; task_id?: string` |
|
|
88
|
+
| `circuit_breaker_blocked` | engine.ts | `agent?: string; task_id?: string` |
|
|
89
|
+
|
|
90
|
+
### Merge & Worktree
|
|
91
|
+
|
|
92
|
+
| Event Type | Source | Data Fields |
|
|
93
|
+
|-----------|--------|-------------|
|
|
94
|
+
| `merge_conflict_detected` | engine.ts | `task_id?: string; files?: string[]` |
|
|
95
|
+
| `merge_conflict_failed` | engine.ts | `task_id?: string; error?: string` |
|
|
96
|
+
|
|
97
|
+
### Artifacts & Injection
|
|
98
|
+
|
|
99
|
+
| Event Type | Source | Data Fields |
|
|
100
|
+
|-----------|--------|-------------|
|
|
101
|
+
| `file_injection_received` | engine.ts | `task_id?: string; from_task?: string; name?: string` |
|
|
102
|
+
| `artifact_limit_reached` | engine.ts | `task_id?: string; limit?: number; current?: number` |
|
|
103
|
+
|
|
104
|
+
### Agent Intelligence
|
|
105
|
+
|
|
106
|
+
| Event Type | Source | Data Fields |
|
|
107
|
+
|-----------|--------|-------------|
|
|
108
|
+
| `agent_identity_captured` | engine.ts | `agent?: string; task_id?: string` |
|
|
109
|
+
| `agent_identity_rejected` | engine.ts | `agent?: string; task_id?: string; reason?: string` |
|
|
110
|
+
| `weak_area_skipped` | engine.ts | `agent?: string; weak_areas?: string[]; task_files?: string[]` |
|
|
111
|
+
| `swarm_concurrency_update` | engine.ts | `new_concurrency?: number; reason?: string` |
|
|
112
|
+
|
|
113
|
+
### Hooks
|
|
114
|
+
|
|
115
|
+
| Event Type | Source | Data Fields |
|
|
116
|
+
|-----------|--------|-------------|
|
|
117
|
+
| `post_convoy_hook_failed` | engine.ts | `hook?: string; error?: string` |
|
|
118
|
+
|
|
119
|
+
### Observability / Session
|
|
120
|
+
|
|
121
|
+
| Event Type | Source | Data Fields |
|
|
122
|
+
|-----------|--------|-------------|
|
|
123
|
+
| `session` | engine.ts | `agent?: string; model?: string; task?: string; outcome?: string; duration_min?: number` |
|
|
124
|
+
| `delegation` | engine.ts | `agent?: string; model?: string; tier?: string; mechanism?: string; outcome?: string` |
|
|
125
|
+
|
|
126
|
+
### Security & Reliability
|
|
127
|
+
|
|
128
|
+
| Event Type | Source | Data Fields |
|
|
129
|
+
|-----------|--------|-------------|
|
|
130
|
+
| `secret_leak_prevented` | engine.ts, events.ts | `original_type?: string; patterns?: string[]; task_id?: string; findings_count?: number; context?: string` |
|
|
131
|
+
| `ndjson_write_failed` | events.ts | `original_type?: string` |
|
|
132
|
+
|
|
133
|
+
### Built-in Gates
|
|
134
|
+
|
|
135
|
+
| Event Type | Source | Data Fields |
|
|
136
|
+
|-----------|--------|-------------|
|
|
137
|
+
| `built_in_gate_result` | engine.ts | `gate: string; passed: boolean; output?: string; level?: string` |
|
|
138
|
+
|
|
139
|
+
### Watch Mode
|
|
140
|
+
|
|
141
|
+
| Event Type | Source | Data Fields |
|
|
142
|
+
|-----------|--------|-------------|
|
|
143
|
+
| `watch_started` | watch.ts | `trigger_type?: string; pid?: number` |
|
|
144
|
+
| `watch_cycle_start` | watch.ts | `cycle_number?: number; triggered_by?: string` |
|
|
145
|
+
| `watch_cycle_end` | watch.ts | `cycle_number?: number; status?: string` |
|
|
146
|
+
| `watch_stopped` | watch.ts | `reason?: string` |
|
|
147
|
+
|
|
148
|
+
### Worker Health
|
|
149
|
+
|
|
150
|
+
| Event Type | Source | Data Fields |
|
|
151
|
+
|-----------|--------|-------------|
|
|
152
|
+
| `worker_killed` | health.ts | `reason?: string; worker_id?: string; task_id?: string` |
|
|
153
|
+
|
|
154
|
+
### Discovered Issues
|
|
155
|
+
|
|
156
|
+
| Event Type | Source | Data Fields |
|
|
157
|
+
|-----------|--------|-------------|
|
|
158
|
+
| `discovered_issue` | issues.ts | `task_id?: string; title?: string; file?: string; description?: string; severity?: string` |
|
|
159
|
+
|
|
160
|
+
## Derived Metrics
|
|
161
|
+
|
|
162
|
+
These are computed from raw events, not emitted directly.
|
|
163
|
+
|
|
164
|
+
| Metric | Derivation |
|
|
165
|
+
|--------|-----------|
|
|
166
|
+
| Task duration | `task_done.timestamp - task_started.timestamp` |
|
|
167
|
+
| Convoy duration | `convoy_finished.timestamp - convoy_started.timestamp` |
|
|
168
|
+
| Retry rate | `COUNT(task_retried) / COUNT(task_started)` |
|
|
169
|
+
| Gate failure rate | `COUNT(built_in_gate_result WHERE !passed) / COUNT(built_in_gate_result)` |
|
|
170
|
+
| Review pass rate | `COUNT(review_verdict WHERE verdict='pass') / COUNT(review_verdict)` |
|
|
171
|
+
| Token usage | `SUM(review_verdict.tokens)` per convoy |
|
|
172
|
+
| Circuit breaker trips | `COUNT(circuit_breaker_tripped)` per agent |
|
|
173
|
+
|
|
174
|
+
## Runtime Validation
|
|
175
|
+
|
|
176
|
+
- `validateEventType(type)` — checks membership in `KNOWN_EVENT_TYPES` (a `Set<string>` exported from [`types.ts`](types.ts)). Unknown types trigger a `console.warn` but do not throw, preserving extensibility for custom event types.
|
|
177
|
+
- `validateEventData(type, data)` — validates the `data` payload shape for known event types. Defined in [`event-schemas.ts`](event-schemas.ts). Returns `{ valid: boolean; issues?: string[] }`. Invalid payloads trigger a `console.warn` but do not block emission.
|
|
178
|
+
|
|
179
|
+
Both validators are called at emit time in [`events.ts`](events.ts).
|
|
180
|
+
|
|
181
|
+
## Dashboard Build Pipeline
|
|
182
|
+
|
|
183
|
+
To build the dashboard with real convoy data:
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
# 1. Run ETL to extract data from SQLite → JSON
|
|
187
|
+
npm run dashboard:etl
|
|
188
|
+
|
|
189
|
+
# 2. Build the Astro dashboard (reads from public/data/*.json)
|
|
190
|
+
npx astro build --root src/dashboard
|
|
191
|
+
|
|
192
|
+
# 3. Serve locally (optional)
|
|
193
|
+
npx astro preview --root src/dashboard
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
In CI, add these steps after tests pass:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
- run: npm run dashboard:etl
|
|
200
|
+
- run: npx astro build --root src/dashboard
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The ETL script gracefully handles missing databases — it produces empty JSON files so the dashboard renders an empty state instead of crashing.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
export interface DashboardOverallStats {
|
|
2
|
+
total_convoys: number
|
|
3
|
+
running_convoys: number
|
|
4
|
+
successful_convoys: number
|
|
5
|
+
failed_convoys: number
|
|
6
|
+
avg_convoy_duration_sec: number | null
|
|
7
|
+
p95_convoy_duration_sec: number | null
|
|
8
|
+
total_tokens: number
|
|
9
|
+
total_cost_usd: number
|
|
10
|
+
top_agents: Array<{ agent: string; task_count: number; total_tokens: number }>
|
|
11
|
+
top_models: Array<{ model: string; task_count: number; total_tokens: number }>
|
|
12
|
+
retry_queue_count: number
|
|
13
|
+
disputed_tasks: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface DashboardConvoySummary {
|
|
17
|
+
id: string
|
|
18
|
+
name: string
|
|
19
|
+
status: string
|
|
20
|
+
branch: string | null
|
|
21
|
+
created_at: string
|
|
22
|
+
started_at: string | null
|
|
23
|
+
finished_at: string | null
|
|
24
|
+
duration_sec: number | null
|
|
25
|
+
total_tokens: number | null
|
|
26
|
+
total_cost_usd: number | null
|
|
27
|
+
tasks_total: number
|
|
28
|
+
tasks_done: number
|
|
29
|
+
tasks_running: number
|
|
30
|
+
tasks_waiting: number
|
|
31
|
+
tasks_failed: number
|
|
32
|
+
tasks_retrying: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface DashboardTaskSummary {
|
|
36
|
+
id: string
|
|
37
|
+
phase: number
|
|
38
|
+
agent: string
|
|
39
|
+
model: string | null
|
|
40
|
+
status: string
|
|
41
|
+
duration_sec: number | null
|
|
42
|
+
retries: number
|
|
43
|
+
files: string[]
|
|
44
|
+
total_tokens: number | null
|
|
45
|
+
cost_usd: number | null
|
|
46
|
+
review_level: string | null
|
|
47
|
+
review_verdict: string | null
|
|
48
|
+
drift_score: number | null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface DashboardConvoyDetail {
|
|
52
|
+
convoy: {
|
|
53
|
+
id: string
|
|
54
|
+
name: string
|
|
55
|
+
status: string
|
|
56
|
+
created_at: string
|
|
57
|
+
finished_at: string | null
|
|
58
|
+
branch: string | null
|
|
59
|
+
total_tokens: number | null
|
|
60
|
+
total_cost_usd: number | null
|
|
61
|
+
}
|
|
62
|
+
taskSummary: {
|
|
63
|
+
total: number
|
|
64
|
+
done: number
|
|
65
|
+
running: number
|
|
66
|
+
failed: number
|
|
67
|
+
review_blocked: number
|
|
68
|
+
disputed: number
|
|
69
|
+
reviewed: number
|
|
70
|
+
panel_reviewed: number
|
|
71
|
+
tasks_with_drift: number
|
|
72
|
+
max_drift_score: number | null
|
|
73
|
+
drift_retried: number
|
|
74
|
+
}
|
|
75
|
+
quality: {
|
|
76
|
+
reviewed_tasks: number
|
|
77
|
+
review_blocked_tasks: number
|
|
78
|
+
disputed_tasks: number
|
|
79
|
+
panel_reviews: number
|
|
80
|
+
}
|
|
81
|
+
drift: {
|
|
82
|
+
tasks_with_drift: number
|
|
83
|
+
max_drift_score: number | null
|
|
84
|
+
drift_retried_tasks: number
|
|
85
|
+
}
|
|
86
|
+
dlq_count: number
|
|
87
|
+
dlq_entries: Array<{
|
|
88
|
+
id: string
|
|
89
|
+
task_id: string
|
|
90
|
+
agent: string
|
|
91
|
+
failure_type: string
|
|
92
|
+
attempts: number
|
|
93
|
+
resolved: number
|
|
94
|
+
}>
|
|
95
|
+
artifact_count: number
|
|
96
|
+
artifacts: Array<{
|
|
97
|
+
id: string
|
|
98
|
+
name: string
|
|
99
|
+
type: string
|
|
100
|
+
task_id: string
|
|
101
|
+
created_at: string
|
|
102
|
+
}>
|
|
103
|
+
has_more_events: boolean
|
|
104
|
+
events: Array<{
|
|
105
|
+
type: string
|
|
106
|
+
task_id: string | null
|
|
107
|
+
data: unknown
|
|
108
|
+
created_at: string
|
|
109
|
+
}>
|
|
110
|
+
tasks: Array<{
|
|
111
|
+
id: string
|
|
112
|
+
phase: number
|
|
113
|
+
agent: string
|
|
114
|
+
model: string | null
|
|
115
|
+
status: string
|
|
116
|
+
retries: number
|
|
117
|
+
started_at: string | null
|
|
118
|
+
finished_at: string | null
|
|
119
|
+
total_tokens: number | null
|
|
120
|
+
cost_usd: number | null
|
|
121
|
+
review_level: string | null
|
|
122
|
+
review_verdict: string | null
|
|
123
|
+
review_tokens: number | null
|
|
124
|
+
review_model: string | null
|
|
125
|
+
panel_attempts: number | null
|
|
126
|
+
dispute_id: string | null
|
|
127
|
+
drift_score: number | null
|
|
128
|
+
drift_retried: number | null
|
|
129
|
+
files: string[] | null
|
|
130
|
+
}>
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface DashboardTimelineEvent {
|
|
134
|
+
id: number
|
|
135
|
+
timestamp: string
|
|
136
|
+
type: string
|
|
137
|
+
convoy_id: string | null
|
|
138
|
+
task_id: string | null
|
|
139
|
+
worker_id: string | null
|
|
140
|
+
summary: string
|
|
141
|
+
}
|