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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentworkshop-prod",
3
- "version": "0.7.33",
3
+ "version": "0.7.34",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentworkshop",
3
- "version": "0.7.33",
3
+ "version": "0.7.34",
4
4
  "type": "module",
5
5
  "description": "AgentWorkShop 软件开发系统 - 基于 Nuxt 4 的配置驱动运行范式",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",
@@ -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 && ['COMPLETED', 'FAILED', 'CANCELED'].includes(st)) states[e.key] = 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
- for (const m of Object.values(members)) await api('DELETE', `/api/workshop/channels/${m.channelId}?purge=1`, { token }).catch(() => {})
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
- // 等待窗 4 分钟:重装载(~20s)+ 下一采样拍 + 上传/启动;mock 引擎可能在窗内就跑完,
182
- // 所以 running/completed 均算命中
183
- for (let i = 0; i < 48 && !autoRunId; i++) {
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
- const s3 = await api('GET', `/api/plugins/rag-bridge/search?q=${encodeURIComponent('闭环优化 设定 判定')}&top_k=3`, { token: userToken })
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']