agentworkshop 0.7.33 → 0.7.34
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/adc365b6-f636-4a2b-9527-79819c1e874b.json +1 -0
- package/.output/server/chunks/_/nitro.mjs +699 -670
- package/.output/server/package.json +1 -1
- package/package.json +1 -1
- package/scripts/_dbg-harness-cancel-repro.mjs +81 -0
- package/scripts/_dbg-opslog-agent-e2e.mjs +5 -2
- package/scripts/production-closed-loop-e2e.mjs +7 -4
- package/server/services/workshop/agents/base-agent.ts +9 -0
- package/server/services/workshop/agents/codex-agent.ts +746 -729
- package/server/services/workshop/plugins/host.mjs +4 -7
- package/server/services/workshop/runtime/agent-runtime.ts +32 -3
- package/.output/public/_nuxt/builds/meta/291ef3e6-fa6a-4dee-8f46-05da1aeab815.json +0 -1
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codex/dsh 任务终态复现:单引擎 × 单任务,保留现场,输出任务状态时间线/消息/事件/
|
|
3
|
+
* 工具面,判定「谁在何时把任务置为 CANCELED」。
|
|
4
|
+
* 用法:NO_PROXY='*' AW_BASE=http://127.0.0.1:3001 AW_E2E_TOKEN=<tok> HARNESS=codex node scripts/_dbg-harness-cancel-repro.mjs
|
|
5
|
+
*/
|
|
6
|
+
const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3001'
|
|
7
|
+
const TOKEN = process.env.AW_E2E_TOKEN ?? ''
|
|
8
|
+
const HARNESS = process.env.HARNESS ?? 'codex'
|
|
9
|
+
const TAG = `cx${Math.random().toString(36).slice(2, 6)}`
|
|
10
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
|
|
11
|
+
|
|
12
|
+
async function api(method, path, body) {
|
|
13
|
+
const res = await fetch(BASE + path, {
|
|
14
|
+
method,
|
|
15
|
+
headers: { authorization: `Bearer ${TOKEN}`, 'content-type': 'application/json' },
|
|
16
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
17
|
+
})
|
|
18
|
+
return res.json()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const tpl = await api('POST', '/api/workshop/agents', { name: `${HARNESS}-repro-${TAG}`, harness: HARNESS })
|
|
22
|
+
const agentId = tpl?.data?.id
|
|
23
|
+
console.log(`template=${agentId} harness=${HARNESS}`)
|
|
24
|
+
const ch = await api('POST', '/api/workshop/channels', { name: `${HARNESS}-repro-${TAG}` })
|
|
25
|
+
const channelId = ch?.data?.channelId ?? ch?.data?.id ?? ch?.data?.channel?.id
|
|
26
|
+
// lead(渠道须有 lead 才能收任务;mock 引擎足够)
|
|
27
|
+
const leadTpl = await api('POST', '/api/workshop/agents', { name: `${HARNESS}-repro-lead-${TAG}`, harness: 'mock' })
|
|
28
|
+
await api('POST', `/api/workshop/channels/${channelId}/agents`, { agentId: leadTpl?.data?.id, role: 'lead' })
|
|
29
|
+
await api('POST', `/api/workshop/channels/${channelId}/agents`, { agentId, role: 'worker' })
|
|
30
|
+
const inst = await api('GET', `/api/workshop/channels/${channelId}/agents`)
|
|
31
|
+
const instId = (inst?.data ?? []).find(a => a.harness === HARNESS)?.id ?? agentId
|
|
32
|
+
console.log(`channel=${channelId} instance=${instId}`)
|
|
33
|
+
|
|
34
|
+
// 工具面:complete_task 是否存在
|
|
35
|
+
const tools = await api('GET', `/api/workshop/agent-tools/list?agentId=${instId}`)
|
|
36
|
+
const names = (tools?.data ?? tools)?.tools?.map(t => t?.name) ?? (tools?.data ?? tools)?.map?.(t => t?.name) ?? []
|
|
37
|
+
console.log('tool surface:', JSON.stringify(names))
|
|
38
|
+
|
|
39
|
+
const t = await api('POST', `/api/workshop/channels/${channelId}/tasks`, {
|
|
40
|
+
title: `repro-${TAG}:读最近日志并回报`,
|
|
41
|
+
description: '用 ops_log 查看最近 3 条日志,然后调用 complete_task 交付一句 LOGCHECK-OK 开头的总结。',
|
|
42
|
+
})
|
|
43
|
+
const taskId = t?.data?.task?.id ?? t?.data?.id ?? t?.data?.taskId
|
|
44
|
+
console.log('task=', taskId, JSON.stringify(t ?? {}).slice(0, 200))
|
|
45
|
+
|
|
46
|
+
// 轮询:每 5s 记录状态,直到终态(最长 8 分钟),记录状态变化时间点
|
|
47
|
+
const timeline = []
|
|
48
|
+
let last = ''
|
|
49
|
+
const t0 = Date.now()
|
|
50
|
+
while (Date.now() - t0 < 8 * 60_000) {
|
|
51
|
+
const me = await api('GET', `/api/workshop/tasks/${taskId}`)
|
|
52
|
+
const st = me?.data?.state
|
|
53
|
+
if (st && st !== last) {
|
|
54
|
+
timeline.push(`t+${Math.round((Date.now() - t0) / 1000)}s ${st}`)
|
|
55
|
+
console.log(` t+${Math.round((Date.now() - t0) / 1000)}s -> ${st}`)
|
|
56
|
+
last = st
|
|
57
|
+
}
|
|
58
|
+
if (['COMPLETED', 'FAILED', 'CANCELED'].includes(st)) break
|
|
59
|
+
await sleep(5000)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 现场:任务详情 / 频道消息 / 事件
|
|
63
|
+
const task = await api('GET', `/api/workshop/tasks/${taskId}`)
|
|
64
|
+
const msgs = await api('GET', `/api/workshop/channels/${channelId}/messages?limit=50`)
|
|
65
|
+
const evs = await api('GET', `/api/workshop/channels/${channelId}/events?limit=120`)
|
|
66
|
+
const fs = await import('node:fs')
|
|
67
|
+
fs.writeFileSync(`.e2e-shots/repro-${HARNESS}-task.json`, JSON.stringify(task?.data ?? task, null, 1))
|
|
68
|
+
fs.writeFileSync(`.e2e-shots/repro-${HARNESS}-msgs.json`, JSON.stringify(msgs?.data ?? msgs, null, 1))
|
|
69
|
+
fs.writeFileSync(`.e2e-shots/repro-${HARNESS}-events.json`, JSON.stringify(evs?.data ?? evs, null, 1))
|
|
70
|
+
console.log('timeline:', timeline.join(' | '))
|
|
71
|
+
const blob = JSON.stringify(task?.data ?? {}) + JSON.stringify(msgs?.data ?? {}) + JSON.stringify(evs?.data ?? {})
|
|
72
|
+
console.log('LOGCHECK-OK in transcript:', blob.includes('LOGCHECK-OK'))
|
|
73
|
+
console.log('implicit-complete marker:', blob.includes('隐式收口'))
|
|
74
|
+
// 消息里的角色/内容前 3 条(找 agent 最后说了什么)
|
|
75
|
+
const list = msgs?.data ?? []
|
|
76
|
+
for (const m of list.slice(-3)) {
|
|
77
|
+
const who = m?.role ?? m?.metadata?.['x-aw-from-label'] ?? '?'
|
|
78
|
+
const text = (m?.parts ?? []).map(p => p?.text ?? '').join(' ').slice(0, 160)
|
|
79
|
+
console.log(` [${who}] ${text}`)
|
|
80
|
+
}
|
|
81
|
+
console.log(`现场已存 .e2e-shots/repro-${HARNESS}-*.json(channel=${channelId} 保留未删)`)
|
|
@@ -140,6 +140,7 @@ async function main() {
|
|
|
140
140
|
ok(Boolean(tasks[e.key]), `[${e.key}] 任务下发`)
|
|
141
141
|
}
|
|
142
142
|
const states = {}
|
|
143
|
+
const lastSeen = {}
|
|
143
144
|
const pollTask = async (id) => {
|
|
144
145
|
for (let i = 0; i < 3; i++) {
|
|
145
146
|
try {
|
|
@@ -151,11 +152,13 @@ async function main() {
|
|
|
151
152
|
return undefined
|
|
152
153
|
}
|
|
153
154
|
const deadline = Date.now() + 20 * 60_000
|
|
155
|
+
const t0 = Date.now()
|
|
154
156
|
while (Date.now() < deadline && Object.keys(states).length < ENGINES.length) {
|
|
155
157
|
for (const e of ENGINES) {
|
|
156
158
|
if (states[e.key]) continue
|
|
157
159
|
const st = await pollTask(tasks[e.key])
|
|
158
|
-
if (st &&
|
|
160
|
+
if (st && st !== lastSeen[e.key]) console.log(` · t+${Math.round((Date.now()-t0)/1000)}s [${e.key}] ${st}`)
|
|
161
|
+
if (st && ['COMPLETED', 'FAILED', 'CANCELED'].includes(st)) { states[e.key] = st; lastSeen[e.key] = st }
|
|
159
162
|
}
|
|
160
163
|
if (Object.keys(states).length < ENGINES.length) await sleep(6000)
|
|
161
164
|
}
|
|
@@ -171,7 +174,7 @@ async function main() {
|
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
// ── 清理 ──
|
|
174
|
-
|
|
177
|
+
if (process.env.KEEP) console.log('KEEP=1 保留现场: ' + JSON.stringify(Object.fromEntries(Object.entries(members).map(([k,v])=>[k,v.channelId]))))
|
|
175
178
|
for (const e of [...ENGINES, { key: 'mock' }]) {
|
|
176
179
|
const list = await api('GET', '/api/workshop/agents', { token })
|
|
177
180
|
const tpl = (list.data ?? []).find(x => x.name === `ops-${e.key}-${TAG}`)
|
|
@@ -178,9 +178,10 @@ let autoRunId = ''
|
|
|
178
178
|
touchReload()
|
|
179
179
|
console.log(' · 已启用 auto_diag(规则:节点 ' + freshNode + ' > 0)并触发热重载')
|
|
180
180
|
|
|
181
|
-
// 等待窗
|
|
182
|
-
//
|
|
183
|
-
|
|
181
|
+
// 等待窗 8 分钟:插件宿主热重载(~20-40s,含全部插件重装载)+ 下一采样拍(采样间隔
|
|
182
|
+
// 最长 60s+)+ 自动上传/发起;实测共享环境下整链路可达 4 分钟以上(48×5s 曾在
|
|
183
|
+
// 触发前 0.4s 超时误报)。mock 引擎可能在窗内就跑完,running/completed 均算命中
|
|
184
|
+
for (let i = 0; i < 96 && !autoRunId; i++) {
|
|
184
185
|
await sleep(5000)
|
|
185
186
|
const runs = await api('GET', '/api/plugins/diag-bridge/runs', { token: userToken })
|
|
186
187
|
const hit = (runs.json?.runs ?? []).find(x => x.source === 'auto' && !preAuto.has(x.runId) && (x.status === 'running' || x.status === 'completed'))
|
|
@@ -291,7 +292,9 @@ console.log('\n── Stage E 知识沉淀验证 ──')
|
|
|
291
292
|
ok(count > 0, '经验库可读', `count=${count}`)
|
|
292
293
|
ok(hasOpt, '经验库含 optimization 类目(优化方案在册)')
|
|
293
294
|
|
|
294
|
-
|
|
295
|
+
// 检索词须与刚沉淀经验的词法重叠(stage1 为 BM25 关键字检索;查询与语料无词法重叠时
|
|
296
|
+
// rag 侧有意跳过全局向量回退,避免低分噪声 → 0 命中)。用标题/标签中的确证词。
|
|
297
|
+
const s3 = await api('GET', `/api/plugins/rag-bridge/search?q=${encodeURIComponent('优化方案 压力设定器 keep production-e2e')}&top_k=3`, { token: userToken })
|
|
295
298
|
ok(s3.json?.success === true && (s3.json?.results?.length ?? 0) > 0, 'two-stage 检索命中闭环优化知识', `results=${s3.json?.results?.length}`)
|
|
296
299
|
}
|
|
297
300
|
|
|
@@ -111,6 +111,15 @@ export abstract class BaseAgentImpl implements AgentInterface {
|
|
|
111
111
|
|
|
112
112
|
// ===== run:消息分流(平台语义,全引擎一致) =====
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* 装配期注入工作区(AgentRuntime 构造时调用):使 host 工具在首个 run() 之前
|
|
116
|
+
* 即可直调 —— 否则 REST agent-tools/invoke 在实例未处理过任何消息时会得到
|
|
117
|
+
* 「workspace 未就绪」(实测生产闭环 Stage D 的 kb_store 偶发因此失败)。
|
|
118
|
+
*/
|
|
119
|
+
attachWorkspace(ws: AgentRunContext['workspace']): void {
|
|
120
|
+
if (ws && !this.workspace) this.workspace = ws
|
|
121
|
+
}
|
|
122
|
+
|
|
114
123
|
async* run(request: AgentRunRequest, ctx: AgentRunContext): AsyncIterable<AgentEvent> {
|
|
115
124
|
if (!this.workspace) this.workspace = ctx.workspace
|
|
116
125
|
const kind = request.message.metadata?.['x-aw-task-kind']
|