@tea-agent/loop-agent 0.42.0-next.4 → 0.42.0-next.6
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 +24 -0
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +1 -0
- package/dist/worker/console/chat/pi-runtime.js +26 -0
- package/dist/worker/console/chat/routes.js +60 -5
- package/dist/worker/console/chat/sdd-data-alignment.js +219 -0
- package/dist/worker/console/chat/session-catalog.js +2 -0
- package/dist/worker/console/chat/session-store.js +26 -0
- package/dist/worker/console/chat/shortcuts.js +18 -2
- package/dist/worker/console/server.js +1 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CeznJ4Iw.js → abnfDiagram-N423BO3Z-BJpsGgAv.js} +1 -1
- package/dist/worker/console/static/assets/{arc-B4u9u7SX.js → arc-DjMFPsEd.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-oghpT81p.js → architectureDiagram-T3A2C74G-DqdZlr1O.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BPJ5Kvcj.js → blockDiagram-VBNYF7ZC-BmZnfFEO.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-CoIu69pd.js → c4Diagram-5PPSVZJV-3cKberpL.js} +1 -1
- package/dist/worker/console/static/assets/channel-DWYwciiv.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DtgiAsSc.js → chunk-2GRJ4B5K-q-0yjQ1h.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-YSu_EVSD.js → chunk-2Q5K7J3B-DnONBaDW.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DaRO-NB6.js → chunk-5RXB4S5H-v3a6PCOS.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-Dh0ZKiGA.js → chunk-5VM5RSS4-D1kLrlU_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DOrvbR-H.js → chunk-6Q2QTUOP-BPelzihi.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C3b22LN2.js → chunk-GF5L2VYU-DauNE69H.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CXlqO8l6.js → chunk-JWPE2WC7-RIg5_bFJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BblTBrDu.js → chunk-KBJHAD2P-BuILuoxs.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DswqPALe.js → chunk-RYQCIY6F-VHORV5oW.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CfTwVOlc.js → chunk-XXDRQBXY-BJYr4xHC.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D6jOf_l4.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D6jOf_l4.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-bLD8jVFo.js → cose-bilkent-JH36ORCC-BS6nuqQn.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BvoG1fh5.js → cynefin-VYW2F7L2-BBhF0NAT.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DEPAdJu3.js → cynefinDiagram-MW4NZA55-D1135Ifg.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-DmrNvBLy.js → dagre-VZM6K2ZE-vAOYPUWJ.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CdwzIwvs.js → diagram-7IWD3JNH-DuaSPkfa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CJ3hj-md.js → diagram-B4RE2ZJO-mMvXvGU6.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-C6YJT2a4.js → diagram-LBJQPF4R-CDIEJtlL.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C_LPfjhX.js → diagram-Q27KOJAE-Bi_6JvCW.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-xvsSb5Ie.js → diagram-UB23O5K3-DAqj57yN.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Rxp2tKcD.js → ebnfDiagram-BXEA7PRR-DfWaxNEo.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DRyLYmZh.js → erDiagram-JOGREHBK-Bt7QqkNG.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-TLSU_D2B.js → flowDiagram-UKHOOZJN-CRRhQwT2.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DGqeTvHC.js → ganttDiagram-PKOTCBZU-DAV1O5B6.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BZxyH2wh.js → gitGraphDiagram-DS77QQ5N-0X2Qo4Lp.js} +1 -1
- package/dist/worker/console/static/assets/index-CVdddQsA.js +451 -0
- package/dist/worker/console/static/assets/index-DDQc5a50.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BsYtzx01.js → infoDiagram-6WML65LV-C5W5eILC.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-Qr-ezGv-.js → ishikawaDiagram-WSZJBQD7-5SfqU-Tb.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DYbB3ibo.js → journeyDiagram-NVQOT4AX-CKDRcaKf.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BHJHewZO.js → kanban-definition-27J2QSJJ-Cc7_TuSb.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CmPIPmCG.js → linear-BcQYYmeT.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-F--C1uV0.js → mermaid.core-CwLZPtQo.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-YTXfL1st.js → mindmap-definition-FAOFIHXS-D29M025z.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DdDMYXP9.js → pegDiagram-VL7TDLO6-CV-ZAWYe.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-C3nH4KVd.js → pieDiagram-7S7Q4E2Y-C6iPS_o5.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-kXyxjG7Y.js → quadrantDiagram-CIZ2JOQS-BFCD1sTg.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-KNzbl-7w.js → railroadDiagram-AXF67PYL-CztNj4O9.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CSwI6dG0.js → requirementDiagram-LRYGKXZP-BWIELSNd.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bl1AyuXy.js → sankeyDiagram-W5VNT64P-CPtxrjJQ.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-2y4EmXQa.js → sequenceDiagram-SI44F4Z6-BEWVSFsu.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-E8v8YrMg.js → sizeCapture-X5ZJPWSS-DyhzoX_J.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-eGGlR856.js → stateDiagram-OKZ733FA-xLox0nu7.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DS5ZRvJG.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-B7g7xc07.js → swimlanes-SLNWSIFB-CLC_ow9A.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Dvpxn-bf.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DR3VCHBG.js → timeline-definition-Z64GVDOM-DmJZ4uyY.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-LUvapZIy.js → vennDiagram-T6HMQDX7-DD7GGi9Z.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-C7GDcaXq.js → wardleyDiagram-T6FBY63Y-Bi8PQoGt.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CbNAqoGK.js → xychartDiagram-ELKLHX3M-uKdAY8Dp.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +225 -21
- package/dist/worker/console/static-src/operator-chat/useComposer.js +5 -1
- package/dist/worker/console/workspace-context.js +4 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +58 -15
- package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +16 -5
- package/dist/workflows/dag/backend-test-writer-completeness.js +42 -6
- package/dist/workflows/dag/init-hybrid.js +62 -22
- package/dist/workflows/dag/prompt.js +36 -2
- package/docs/templates/backend-test-dag.json +5 -5
- package/package.json +1 -1
- package/dist/worker/console/static/assets/channel-DmIXqJ6B.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-MFV5Qxb5.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-MFV5Qxb5.js +0 -1
- package/dist/worker/console/static/assets/index-C8V8K9b4.js +0 -451
- package/dist/worker/console/static/assets/index-Cnf_KxTQ.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8e4J3at.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Do88I1Nb.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
- 修复 backend-test correspondence 把前面函数的 `pytest.param(..., id="TP-...")` 叠进后续 Case `parameterIds` 的问题:decorator 区域遇到上一 `def`/`class` 或非 decorator 续行即停,不再把整组打成 `TEST_POINT_BINDING_EXTRA`。eligibility 门禁不放宽。
|
|
6
|
+
- 修复 backend-test scenario facts 把内部 max/min/oversize helper 哨兵 `-1/-2/-3` 写成 `observedLength`、导致 N10 在 schema `min(0)` 上 `invalid-output` 的问题:这些 helper 仍按 Markdown bound MATCH,持久化长度只保留非负值。
|
|
7
|
+
- 修复 backend-test 把 GET `params=`/`query=` 过滤字段、Python `True`/`False` 布尔字面量和混合大小写 `pytest.param` 列表漏出 correspondence/payload/scenario 观察的问题:这些 variant 现在可以按 item 授权,而不再把整 Case 打成 `PAYLOAD_SHAPE_MISMATCH` / `VARIANT_PARAMETER_MISSING`。eligibility 门禁不放宽。
|
|
8
|
+
- 修复 backend-test 计划协议标题被翻译成中文后 N3 直接 `missing-module-index`、以及 Markdown writer 漏写精确 Scenario Partition slot 只能等到 N6 才失败的问题:N3 现在把 `## 模块索引/覆盖范围/覆盖矩阵/场景分区` 确定性改写成英文 canonical heading 并最多写回一次 run-owned `plan.md`,同时展开 slot 清单写入 `contracts/backend-test-scenario-partition-slots.json` 并注入 map child `requiredVariantSlots`。Markdown shard completeness 对缺失精确 `TP-SP-...` variant 报 `incomplete-partition-slot-set` 并允许一次有界补写。覆盖语义按方案 A:Case 数量可以少于枚举数量,但每个 slot 仍须独立 variant Test Point;`SINGLE`/`MULTIPLE` 不计覆盖。N6 fail-closed 不放宽。
|
|
9
|
+
- 修复 backend-test strict Markdown map writer 的 upstream artifact pointer 泄漏:当长上游输出的 `stdout.md` 不在当前节点 `readSet` 时,不再向 Pi 注入不可读取的 artifact 路径,避免模型触发 `read-outside-read-set` 后进入 pending;保留有界 preview,并新增回归覆盖严格 writer 边界。
|
|
10
|
+
- 修复 backend-test Manifest 把模型生成的 Module Stem 展示单元格当成模块身份真源、导致 low-tier 输出(缺 `test_` 前缀或整段 Markdown 链接)在 N3 `module-markdown-path-mismatch` 反复失败的问题:N3 现在先解析唯一合法 Markdown Path 与 `<stem>.md` 相对链接作为 canonical 身份,再校验业务 ownership/split 与路径安全;Stem 单元格只作冗余展示并能确定性重写为 canonical 文件名 stem。strict `backendTest.moduleLayout` 改为先按唯一 required Markdown Path 定位模块,再恢复冻结 Markdown/Pytest path pair,避免错误 stem 提前丢弃整行,同时保留 Plan-only pytest path 修复。Markdown Path 缺失/多候选/越界或与 strict path pair 冲突、以及多行 canonicalize 后同 identity(`duplicate-canonical-module-stem`)继续 fail-closed,不再静默去重。同步加强 N2 prompt(Stem 单元格仅允许纯文件名 stem)。
|
|
5
11
|
- 修复 Next Release 的 io-heavy 测试并发不稳定:发布 workflow 将该池固定为单 worker,避免全部测试已通过后触发 Vitest `onTaskUpdate` RPC settlement timeout,并消除 controller identity/package fingerprint 测试之间的共享状态竞态;不跳过或删除任何测试。
|
|
6
12
|
- 修复 backend-test 唯一 pytest collection repair 已产生受控非空修改、却因模型遗漏 `IMPLEMENTATION_OUTCOME` 而阻断整条执行链的问题:仅 N11 exact repair writer 可把完全缺失的 outcome 确定性恢复为候选 changed,随后仍必须由 N12 重新通过 scenario、correspondence、payload、collection、fixture 和 readiness 门禁;unknown/conflicting/blocked、空 diff、越界写入及 provider/context failure 继续 fail-closed。
|
|
13
|
+
- 新建和删除对话现在会立即在侧栏中反馈:新建时先显示“正在创建”,删除时平滑淡出;网络失败会自动恢复原会话,不再需要等待整张会话列表刷新后才看到变化。
|
|
14
|
+
- 新建对话会优先复用当前工作区中真正的空白会话,避免重复创建;有历史消息、工具调用、待发送内容或正在运行的会话不会被复用。
|
|
15
|
+
- 会话列表刷新现在会复用未变化的行和列表引用,标题、运行状态或队列变化时只更新对应会话,侧栏切换和刷新更平滑。
|
|
16
|
+
- Operator Chat 新增仅对具备 Aimax 凭证的用户可见的 `/sdd-bingo` 数据对齐命令;`wizard-local` 模型请求会自动携带与上报一致的 OpenCode 风格会话身份、当前模型和最新 API key,请求头与资格文件变更均在下一次操作自动生效。
|
|
7
17
|
- 修复前端任务产物派生续跑无法识别当前节点链的问题;从实现节点继续时,设计约束、写入准入、验证和收尾步骤会按现行流程安全重放。
|
|
8
18
|
- 统一工作区、Todo 和其他滚动区域的滚动条样式,默认更轻巧,鼠标悬浮时也更容易辨认。
|
|
9
19
|
- HTML 文件预览现在支持页面内的标签切换等交互;较大的自包含 HTML 可以在新窗口中直接完整加载,不再受源码摘要长度影响。
|
|
@@ -230,6 +240,20 @@
|
|
|
230
240
|
- 右侧辅助栏统一承载文件、上下文、调用记录、运行活动和当前 DAG;点击消息流中的运行状态即可在同一侧栏查看运行、门禁与产物,并可自由调整侧栏宽度。
|
|
231
241
|
- 新增任务清单与结构化提问卡片,让长任务的进度和需要用户选择的内容更容易理解。
|
|
232
242
|
- 「对话 / 操作 / 观测」使用统一导航;操作和观测页面可以切换工作区查看对应数据。
|
|
243
|
+
## [0.41.1] - 2026-09-02
|
|
244
|
+
|
|
245
|
+
### 修复
|
|
246
|
+
|
|
247
|
+
- 修复 Next Release 发布 workflow 的 io-heavy 测试并发不稳定:将该测试池固定为单 worker,避免全部测试通过后因过高并发触发 Vitest `onTaskUpdate` RPC settlement timeout,并消除 controller identity / package fingerprint 测试之间的共享状态竞态。
|
|
248
|
+
- 修复 Operator Chat 旧会话继续使用过期模型认证的问题:模型配置变化后,下一轮发送或手动重新加载会自动刷新模型目录和当前模型认证。
|
|
249
|
+
- 修复 Operator Chat 长工具链的消息错序:模型过程文字、思考和工具卡片现在按服务端事件顺序稳定交替展示,刷新历史会话后也保持相同顺序。
|
|
250
|
+
- 统一工作区、Todo 和其他滚动区域的滚动条样式,默认更轻巧,鼠标悬浮时也更容易辨认。
|
|
251
|
+
- HTML 文件预览现在支持页面内的标签切换等交互;较大的自包含 HTML 可以在新窗口中直接完整加载,不再受源码摘要长度限制。
|
|
252
|
+
|
|
253
|
+
### 测试
|
|
254
|
+
|
|
255
|
+
- `harness.json` Pi MED 档测试不再 pin 具体模型 id,改为校验 tier 形状,避免每次 dogfood 档位切换都造成测试失败。
|
|
256
|
+
|
|
233
257
|
## [0.41.0] - 2026-08-31
|
|
234
258
|
|
|
235
259
|
### 新增
|
package/dist/build-stamp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.42.0-next.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-09-
|
|
3
|
+
"version": "0.42.0-next.6",
|
|
4
|
+
"gitSha": "6919b06fcb268f3a61890e85b6bc4dcbf11b62e1",
|
|
5
|
+
"builtAt": "2026-09-02T06:01:45.078Z"
|
|
6
6
|
}
|
|
@@ -4500,6 +4500,7 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
4500
4500
|
? "pytest-generate"
|
|
4501
4501
|
: "md-generate",
|
|
4502
4502
|
writeSet: input.task.writeSet ?? [],
|
|
4503
|
+
runDir: meta.runDir,
|
|
4503
4504
|
})
|
|
4504
4505
|
: isBackendTestMdPlanTask(input.task)
|
|
4505
4506
|
? await assessBackendTestMdPlanCompleteness(input.cwd, backendLayout)
|
|
@@ -43,6 +43,7 @@ import { filterActiveInterviewTools } from "../interview/tools.js";
|
|
|
43
43
|
import { RUNTIME_CONTEXT_TEXT_MAX, redactRuntimeText, } from "./runtime-context.js";
|
|
44
44
|
import { buildTodoContinuationContext, } from "./todos.js";
|
|
45
45
|
import { buildGoalContinuationContext, } from "./goals.js";
|
|
46
|
+
import { injectWizardLocalHeaders, } from "./sdd-data-alignment.js";
|
|
46
47
|
/**
|
|
47
48
|
* Base system prompt fragment every General Operator Chat session receives.
|
|
48
49
|
*
|
|
@@ -298,6 +299,24 @@ export function createGoalCompactionContextExtension(options) {
|
|
|
298
299
|
},
|
|
299
300
|
};
|
|
300
301
|
}
|
|
302
|
+
export function createWizardLocalHeadersExtension(options) {
|
|
303
|
+
return {
|
|
304
|
+
name: "operator-chat-wizard-local-headers",
|
|
305
|
+
hidden: true,
|
|
306
|
+
factory: (api) => {
|
|
307
|
+
api.on("before_provider_headers", async (rawEvent, context) => {
|
|
308
|
+
if (!context || !rawEvent.headers || typeof rawEvent.headers !== "object")
|
|
309
|
+
return;
|
|
310
|
+
await injectWizardLocalHeaders({
|
|
311
|
+
headers: rawEvent.headers,
|
|
312
|
+
context,
|
|
313
|
+
sessionId: options.sessionId,
|
|
314
|
+
service: options.service,
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
301
320
|
/**
|
|
302
321
|
* Build Pi custom ToolDefinition objects for every whitelisted operator action.
|
|
303
322
|
* Each tool's execute() routes into the operator action dispatcher (Gate 3).
|
|
@@ -1061,10 +1080,17 @@ export class ConsolePiRuntime {
|
|
|
1061
1080
|
registry: this.options.goalRegistry,
|
|
1062
1081
|
})
|
|
1063
1082
|
: undefined;
|
|
1083
|
+
const wizardLocalHeadersExtension = sessionId && this.options.sddDataAlignment
|
|
1084
|
+
? createWizardLocalHeadersExtension({
|
|
1085
|
+
sessionId,
|
|
1086
|
+
service: this.options.sddDataAlignment,
|
|
1087
|
+
})
|
|
1088
|
+
: undefined;
|
|
1064
1089
|
const extraFactories = [
|
|
1065
1090
|
todoContextExtension,
|
|
1066
1091
|
goalContextExtension,
|
|
1067
1092
|
pressureExtension,
|
|
1093
|
+
wizardLocalHeadersExtension,
|
|
1068
1094
|
].filter((item) => Boolean(item));
|
|
1069
1095
|
return {
|
|
1070
1096
|
...base,
|
|
@@ -16,7 +16,7 @@ import { randomUUID } from "node:crypto";
|
|
|
16
16
|
import { parseAssistantContent } from "./assistant-content.js";
|
|
17
17
|
import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
|
|
18
18
|
import { buildModelCallableToolSchemas } from "./tool-adapter.js";
|
|
19
|
-
import { cleanChatTitle, acceptedOperationIdFromResult } from "./session-store.js";
|
|
19
|
+
import { cleanChatTitle, acceptedOperationIdFromResult, isBlankChatSession } from "./session-store.js";
|
|
20
20
|
import { hashChatTurnPayload, projectChatToolEventPayload, projectCompactSnapshot, } from "./chat-event-store.js";
|
|
21
21
|
import { projectDeferredTurnSummary, projectDeferredTurnSummaries, } from "./deferred-turn.js";
|
|
22
22
|
import { THINKING_LEVELS, RuntimeSnapshotUnavailableError, } from "./pi-runtime.js";
|
|
@@ -42,6 +42,12 @@ import { buildTodoCompactionInstructions, projectCurrentTodos } from "./todos.js
|
|
|
42
42
|
import { buildGoalCompactionInstructions, buildGoalContinuationContext, } from "./goals.js";
|
|
43
43
|
import { classifyGoalRoundAdmission, } from "./goal-round-driver.js";
|
|
44
44
|
import { isTerminalOperationState } from "../../../infrastructure/console/operation-store.js";
|
|
45
|
+
import { SddDataAlignmentError, } from "./sdd-data-alignment.js";
|
|
46
|
+
function projectBrowserChatSession(record) {
|
|
47
|
+
const { telemetrySessionId, ...projected } = record;
|
|
48
|
+
void telemetrySessionId;
|
|
49
|
+
return projected;
|
|
50
|
+
}
|
|
45
51
|
/**
|
|
46
52
|
* Reattach the durable event-ring order to session-store rows. The session
|
|
47
53
|
* store remains the content authority; the ring only supplies chronology.
|
|
@@ -616,7 +622,7 @@ workspaceId) {
|
|
|
616
622
|
sessionId: handle.sessionId,
|
|
617
623
|
sessionFile: handle.sessionFile,
|
|
618
624
|
model: handle.model,
|
|
619
|
-
session,
|
|
625
|
+
session: { ...session, isBlank: isBlankChatSession(session) },
|
|
620
626
|
activeTools: handle.activeTools,
|
|
621
627
|
hasBash: true,
|
|
622
628
|
...(handle.initializing ? { initializing: true } : {}),
|
|
@@ -820,7 +826,7 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
|
|
|
820
826
|
ok: true,
|
|
821
827
|
eventEpoch: deps.events.epoch(sessionId),
|
|
822
828
|
session: {
|
|
823
|
-
...record,
|
|
829
|
+
...projectBrowserChatSession(record),
|
|
824
830
|
messages: projectStoredMessagesWithEventOrder(record.messages, snapshot),
|
|
825
831
|
composerDraft,
|
|
826
832
|
activeTurnId: activeTurn?.turnId,
|
|
@@ -1406,6 +1412,7 @@ export async function listChatSessionSummaries(deps, ownerWorkspaceId) {
|
|
|
1406
1412
|
: "idle",
|
|
1407
1413
|
deferredTurn: projectDeferredTurnSummary(session.deferredTurns?.[0]),
|
|
1408
1414
|
deferredTurns: projectDeferredTurnSummaries(session.deferredTurns ?? []),
|
|
1415
|
+
isBlank: isBlankChatSession(session) && !active,
|
|
1409
1416
|
};
|
|
1410
1417
|
});
|
|
1411
1418
|
}
|
|
@@ -1436,7 +1443,7 @@ export async function handlePatchChatSession(req, res, deps, sessionId) {
|
|
|
1436
1443
|
});
|
|
1437
1444
|
return;
|
|
1438
1445
|
}
|
|
1439
|
-
sendJson(res, 200, { ok: true, session: record });
|
|
1446
|
+
sendJson(res, 200, { ok: true, session: projectBrowserChatSession(record) });
|
|
1440
1447
|
}
|
|
1441
1448
|
catch (error) {
|
|
1442
1449
|
sendJson(res, 404, {
|
|
@@ -2776,6 +2783,9 @@ export async function handleChatCommands(res, deps, sessionId) {
|
|
|
2776
2783
|
res.end(JSON.stringify({
|
|
2777
2784
|
ok: true,
|
|
2778
2785
|
commands,
|
|
2786
|
+
capabilities: {
|
|
2787
|
+
sddBingo: Boolean(await deps.sddDataAlignment?.isEligible()),
|
|
2788
|
+
},
|
|
2779
2789
|
...(warning ? { warning } : {}),
|
|
2780
2790
|
}));
|
|
2781
2791
|
}
|
|
@@ -2786,10 +2796,46 @@ export async function handleChatCommands(res, deps, sessionId) {
|
|
|
2786
2796
|
res.end(JSON.stringify({
|
|
2787
2797
|
ok: true,
|
|
2788
2798
|
commands: [],
|
|
2799
|
+
capabilities: { sddBingo: false },
|
|
2789
2800
|
warning: error instanceof Error ? error.message : "commands unavailable",
|
|
2790
2801
|
}));
|
|
2791
2802
|
}
|
|
2792
2803
|
}
|
|
2804
|
+
async function handleSddBingo(req, res, deps, sessionId) {
|
|
2805
|
+
const gate = gateMutation(req, deps);
|
|
2806
|
+
if (!gate.ok) {
|
|
2807
|
+
sendJson(res, gate.status, { ok: false, error: { code: gate.code, message: gate.message } });
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
if (!deps.sddDataAlignment) {
|
|
2811
|
+
sendJson(res, 403, {
|
|
2812
|
+
ok: false,
|
|
2813
|
+
error: { code: "SDD_NOT_ELIGIBLE", message: "当前用户不可使用 SDD 数据对齐" },
|
|
2814
|
+
});
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
try {
|
|
2818
|
+
const body = await readJsonBody(req);
|
|
2819
|
+
if (Object.keys(body).some((key) => key !== "skillName")) {
|
|
2820
|
+
throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "请求体只允许 skillName 字段", 400);
|
|
2821
|
+
}
|
|
2822
|
+
await deps.sddDataAlignment.report(sessionId, body.skillName);
|
|
2823
|
+
sendJson(res, 200, { ok: true });
|
|
2824
|
+
}
|
|
2825
|
+
catch (error) {
|
|
2826
|
+
if (error instanceof SddDataAlignmentError) {
|
|
2827
|
+
sendJson(res, error.status, {
|
|
2828
|
+
ok: false,
|
|
2829
|
+
error: { code: error.code, message: error.message },
|
|
2830
|
+
});
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
sendJson(res, 502, {
|
|
2834
|
+
ok: false,
|
|
2835
|
+
error: { code: "SDD_UPSTREAM_FAILED", message: "SDD 数据对齐上报失败" },
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2793
2839
|
export async function handleRuntimeContext(res, deps, sessionId) {
|
|
2794
2840
|
const ensured = await ensureActiveSession(deps, sessionId);
|
|
2795
2841
|
if (!ensured.ok) {
|
|
@@ -2921,7 +2967,11 @@ export async function handleModels(req, res, deps, sessionId) {
|
|
|
2921
2967
|
// folded into a 409 MODEL_SWITCH_FAILED (the frontend refresh path
|
|
2922
2968
|
// re-reads the snapshot to converge on the applied selection).
|
|
2923
2969
|
const snapshot = await deps.runtime.getRuntimeSnapshot(sessionId);
|
|
2924
|
-
sendJson(res, 200, {
|
|
2970
|
+
sendJson(res, 200, {
|
|
2971
|
+
ok: true,
|
|
2972
|
+
session: projectBrowserChatSession(updated),
|
|
2973
|
+
snapshot,
|
|
2974
|
+
});
|
|
2925
2975
|
}
|
|
2926
2976
|
catch (error) {
|
|
2927
2977
|
if (error instanceof RuntimeSnapshotUnavailableError) {
|
|
@@ -4188,6 +4238,11 @@ export async function handleChatRequest(req, res, deps, pathname, workspaceScope
|
|
|
4188
4238
|
await handleSkillAutoInvocation(req, res, deps, decodeURIComponent(skillAutoInvocation[1]), decodeURIComponent(skillAutoInvocation[2]));
|
|
4189
4239
|
return true;
|
|
4190
4240
|
}
|
|
4241
|
+
const sddBingo = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/sdd-bingo$/);
|
|
4242
|
+
if (method === "POST" && sddBingo) {
|
|
4243
|
+
await handleSddBingo(req, res, deps, decodeURIComponent(sddBingo[1]));
|
|
4244
|
+
return true;
|
|
4245
|
+
}
|
|
4191
4246
|
const m4Route = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/(draft|files|runtime-context|runtime-snapshot|models|model|reload|commands)$/);
|
|
4192
4247
|
if (m4Route) {
|
|
4193
4248
|
const id = decodeURIComponent(m4Route[1]);
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
export const SDD_DEFAULT_SKILL_NAME = "SDD-implementation-test-review";
|
|
6
|
+
export const SDD_TELEMETRY_URL = "https://wizard-ai.paic.com.cn/code_pilot/api/skill-usage-log";
|
|
7
|
+
export const SDD_TELEMETRY_SIGN = "52aad0c09e6b46818a11ee702d6fe0ba";
|
|
8
|
+
export const WIZARD_LOCAL_DEFAULT_USER_AGENT = "Builder/OpenCode ai-sdk/provider-utils/4.0.21 runtime/bun/1.3.11";
|
|
9
|
+
function sameAuthFileVersion(left, right) {
|
|
10
|
+
if (!left.exists || !right.exists)
|
|
11
|
+
return left.exists === right.exists;
|
|
12
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size &&
|
|
13
|
+
left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
14
|
+
}
|
|
15
|
+
/** Reads ~/.aima-go/auth.json with metadata-based parse caching and fail-closed semantics. */
|
|
16
|
+
export class AimaxAuthReader {
|
|
17
|
+
authFile;
|
|
18
|
+
cached;
|
|
19
|
+
constructor(authFile = path.join(os.homedir(), ".aima-go", "auth.json")) {
|
|
20
|
+
this.authFile = authFile;
|
|
21
|
+
}
|
|
22
|
+
async getToken() {
|
|
23
|
+
let version;
|
|
24
|
+
try {
|
|
25
|
+
const info = await stat(this.authFile, { bigint: true });
|
|
26
|
+
version = { exists: true, dev: info.dev, ino: info.ino, size: info.size,
|
|
27
|
+
mtimeNs: info.mtimeNs, ctimeNs: info.ctimeNs };
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
this.cached = { version: { exists: false } };
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
if (this.cached && sameAuthFileVersion(this.cached.version, version))
|
|
34
|
+
return this.cached.token;
|
|
35
|
+
let token;
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(await readFile(this.authFile, "utf8"));
|
|
38
|
+
const candidate = parsed["aimax-auth-token"];
|
|
39
|
+
if (typeof candidate === "string" && candidate.trim())
|
|
40
|
+
token = candidate;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Malformed or unreadable files are intentionally ineligible.
|
|
44
|
+
}
|
|
45
|
+
this.cached = { version, ...(token ? { token } : {}) };
|
|
46
|
+
return token;
|
|
47
|
+
}
|
|
48
|
+
async isEligible() {
|
|
49
|
+
return Boolean(await this.getToken());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const OPENCODE_RANDOM_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
53
|
+
const OPENCODE_TIME_MASK = (1n << 48n) - 1n;
|
|
54
|
+
let lastOpenCodeMillisecond = -1;
|
|
55
|
+
let openCodeMillisecondCounter = 0;
|
|
56
|
+
/** Generate the OpenCode-compatible `ses_` identifier without an OpenCode dependency. */
|
|
57
|
+
export function createOpenCodeSessionId(options = {}) {
|
|
58
|
+
const now = Math.floor((options.now ?? Date.now)());
|
|
59
|
+
if (now === lastOpenCodeMillisecond)
|
|
60
|
+
openCodeMillisecondCounter = (openCodeMillisecondCounter + 1) & 0xfff;
|
|
61
|
+
else {
|
|
62
|
+
lastOpenCodeMillisecond = now;
|
|
63
|
+
openCodeMillisecondCounter = 0;
|
|
64
|
+
}
|
|
65
|
+
const ordered = BigInt(now) * 0x1000n + BigInt(openCodeMillisecondCounter);
|
|
66
|
+
const timePart = ((~ordered) & OPENCODE_TIME_MASK).toString(16).padStart(12, "0");
|
|
67
|
+
const bytes = (options.random ?? randomBytes)(14);
|
|
68
|
+
let randomPart = "";
|
|
69
|
+
for (const byte of bytes)
|
|
70
|
+
randomPart += OPENCODE_RANDOM_ALPHABET[byte % OPENCODE_RANDOM_ALPHABET.length];
|
|
71
|
+
return `ses_${timePart}${randomPart}`;
|
|
72
|
+
}
|
|
73
|
+
export class SddDataAlignmentError extends Error {
|
|
74
|
+
code;
|
|
75
|
+
status;
|
|
76
|
+
constructor(code, message, status) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.code = code;
|
|
79
|
+
this.status = status;
|
|
80
|
+
this.name = "SddDataAlignmentError";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export function normalizeSddSkillName(value) {
|
|
84
|
+
if (value !== undefined && typeof value !== "string") {
|
|
85
|
+
throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 必须是字符串", 400);
|
|
86
|
+
}
|
|
87
|
+
const raw = value ?? "";
|
|
88
|
+
const normalized = raw.trim() || SDD_DEFAULT_SKILL_NAME;
|
|
89
|
+
if ([...normalized].length > 256 || /[\u0000-\u001f\u007f]/u.test(raw)) {
|
|
90
|
+
throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 不能超过 256 个字符或包含控制字符", 400);
|
|
91
|
+
}
|
|
92
|
+
return normalized;
|
|
93
|
+
}
|
|
94
|
+
export class SddDataAlignmentService {
|
|
95
|
+
store;
|
|
96
|
+
auth;
|
|
97
|
+
fetchImpl;
|
|
98
|
+
constructor(store, auth = new AimaxAuthReader(), fetchImpl = fetch) {
|
|
99
|
+
this.store = store;
|
|
100
|
+
this.auth = auth;
|
|
101
|
+
this.fetchImpl = fetchImpl;
|
|
102
|
+
}
|
|
103
|
+
async isEligible() {
|
|
104
|
+
return this.auth.isEligible();
|
|
105
|
+
}
|
|
106
|
+
async ensureTelemetrySessionId(sessionId) {
|
|
107
|
+
const value = await this.store.ensureTelemetrySessionId(sessionId, createOpenCodeSessionId);
|
|
108
|
+
if (!value)
|
|
109
|
+
throw new SddDataAlignmentError("SDD_SESSION_NOT_FOUND", "当前对话不存在", 404);
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
async report(sessionId, rawSkillName) {
|
|
113
|
+
const token = await this.auth.getToken();
|
|
114
|
+
if (!token) {
|
|
115
|
+
throw new SddDataAlignmentError("SDD_NOT_ELIGIBLE", "当前用户不可使用 SDD 数据对齐", 403);
|
|
116
|
+
}
|
|
117
|
+
const skillName = normalizeSddSkillName(rawSkillName);
|
|
118
|
+
const telemetrySessionId = await this.ensureTelemetrySessionId(sessionId);
|
|
119
|
+
const controller = new AbortController();
|
|
120
|
+
const timeout = setTimeout(() => controller.abort(), 30_000);
|
|
121
|
+
let response;
|
|
122
|
+
try {
|
|
123
|
+
response = await this.fetchImpl(SDD_TELEMETRY_URL, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: {
|
|
126
|
+
"Content-Type": "application/json",
|
|
127
|
+
"ws-auth": token,
|
|
128
|
+
"ws-sign": SDD_TELEMETRY_SIGN,
|
|
129
|
+
Connection: "keep-alive",
|
|
130
|
+
"User-Agent": "opencode/1.3.6",
|
|
131
|
+
Accept: "*/*",
|
|
132
|
+
Host: "wizard-ai.paic.com.cn",
|
|
133
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
134
|
+
},
|
|
135
|
+
body: JSON.stringify({ sessionId: telemetrySessionId, builderId: "opencode", skillName }),
|
|
136
|
+
signal: controller.signal,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
if (controller.signal.aborted) {
|
|
141
|
+
throw new SddDataAlignmentError("SDD_UPSTREAM_TIMEOUT", "SDD 数据对齐上报超时", 504);
|
|
142
|
+
}
|
|
143
|
+
throw new SddDataAlignmentError("SDD_UPSTREAM_FAILED", "SDD 数据对齐上报失败", 502);
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
clearTimeout(timeout);
|
|
147
|
+
}
|
|
148
|
+
const body = await readBoundedTelemetryJson(response);
|
|
149
|
+
if (!response.ok || body?.ret !== 0 || body.succ !== true) {
|
|
150
|
+
throw new SddDataAlignmentError("SDD_UPSTREAM_FAILED", "SDD 数据对齐上报失败", 502);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function readBoundedTelemetryJson(response) {
|
|
155
|
+
const reader = response.body?.getReader();
|
|
156
|
+
if (!reader)
|
|
157
|
+
return undefined;
|
|
158
|
+
const chunks = [];
|
|
159
|
+
let total = 0;
|
|
160
|
+
try {
|
|
161
|
+
for (;;) {
|
|
162
|
+
const item = await reader.read();
|
|
163
|
+
if (item.done)
|
|
164
|
+
break;
|
|
165
|
+
total += item.value.byteLength;
|
|
166
|
+
if (total > 64 * 1024) {
|
|
167
|
+
await reader.cancel();
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
chunks.push(item.value);
|
|
171
|
+
}
|
|
172
|
+
const merged = new Uint8Array(total);
|
|
173
|
+
let offset = 0;
|
|
174
|
+
for (const chunk of chunks) {
|
|
175
|
+
merged.set(chunk, offset);
|
|
176
|
+
offset += chunk.byteLength;
|
|
177
|
+
}
|
|
178
|
+
return JSON.parse(new TextDecoder().decode(merged));
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
reader.releaseLock();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function deleteHeaderCaseInsensitive(headers, name) {
|
|
188
|
+
for (const key of Object.keys(headers))
|
|
189
|
+
if (key.toLowerCase() === name.toLowerCase())
|
|
190
|
+
delete headers[key];
|
|
191
|
+
}
|
|
192
|
+
function configuredHeader(headers, name) {
|
|
193
|
+
const key = Object.keys(headers ?? {}).find((entry) => entry.toLowerCase() === name.toLowerCase());
|
|
194
|
+
return key ? headers?.[key] : undefined;
|
|
195
|
+
}
|
|
196
|
+
function setStaticHeader(headers, configured, name, defaultValue) {
|
|
197
|
+
deleteHeaderCaseInsensitive(headers, name);
|
|
198
|
+
headers[name] = configuredHeader(configured, name) ?? defaultValue;
|
|
199
|
+
}
|
|
200
|
+
export async function injectWizardLocalHeaders(options) {
|
|
201
|
+
const model = options.context.model;
|
|
202
|
+
if (model?.provider !== "wizard-local" || !model.id)
|
|
203
|
+
return;
|
|
204
|
+
if (!(await options.service.isEligible()))
|
|
205
|
+
return;
|
|
206
|
+
const getAuth = options.context.modelRegistry?.getApiKeyAndHeaders;
|
|
207
|
+
if (!getAuth)
|
|
208
|
+
return;
|
|
209
|
+
const auth = await getAuth(model);
|
|
210
|
+
if (!auth.ok || typeof auth.apiKey !== "string" || !auth.apiKey)
|
|
211
|
+
return;
|
|
212
|
+
setStaticHeader(options.headers, auth.headers, "client-code", "OpenCode");
|
|
213
|
+
setStaticHeader(options.headers, auth.headers, "User-Agent", WIZARD_LOCAL_DEFAULT_USER_AGENT);
|
|
214
|
+
for (const name of ["x-api-key", "X-Session-Id", "X-Model-Id"])
|
|
215
|
+
deleteHeaderCaseInsensitive(options.headers, name);
|
|
216
|
+
options.headers["x-api-key"] = auth.apiKey;
|
|
217
|
+
options.headers["X-Session-Id"] = await options.service.ensureTelemetrySessionId(options.sessionId);
|
|
218
|
+
options.headers["X-Model-Id"] = model.id;
|
|
219
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isBlankChatSession } from "./session-store.js";
|
|
1
2
|
import { projectDeferredTurnSummaries, projectDeferredTurnSummary, } from "./deferred-turn.js";
|
|
2
3
|
/**
|
|
3
4
|
* Process-wide Session baseline, mirroring DSH's independent session.list.
|
|
@@ -33,6 +34,7 @@ export class ChatSessionCatalog {
|
|
|
33
34
|
: {}),
|
|
34
35
|
deferredTurn: projectDeferredTurnSummary(record.deferredTurns?.[0]),
|
|
35
36
|
deferredTurns: projectDeferredTurnSummaries(record.deferredTurns ?? []),
|
|
37
|
+
isBlank: isBlankChatSession(record),
|
|
36
38
|
...(workspaceId ? { workspaceId } : {}),
|
|
37
39
|
});
|
|
38
40
|
}
|
|
@@ -18,6 +18,15 @@ import { authorizeOperatorChatTool } from "./tools.js";
|
|
|
18
18
|
import { projectOperationForChat } from "./chat-event-store.js";
|
|
19
19
|
import { scrubSecrets } from "./explore-tools.js";
|
|
20
20
|
import { hashDeferredTurnPayload, parseDeferredTurns, } from "./deferred-turn.js";
|
|
21
|
+
/** True only for a durable session that has never received user work. */
|
|
22
|
+
export function isBlankChatSession(record) {
|
|
23
|
+
return (record.state === "active" &&
|
|
24
|
+
!record.title &&
|
|
25
|
+
!record.displayTitle &&
|
|
26
|
+
record.messages.length === 0 &&
|
|
27
|
+
record.toolInvocations.length === 0 &&
|
|
28
|
+
(record.deferredTurns?.length ?? 0) === 0);
|
|
29
|
+
}
|
|
21
30
|
const CHAT_TITLE_MAX_CHARS = 80;
|
|
22
31
|
/** Safe, one-line bounded title used for placeholders and model output. */
|
|
23
32
|
export function cleanChatTitle(value) {
|
|
@@ -81,6 +90,9 @@ export class ChatSessionStore {
|
|
|
81
90
|
...(typeof raw.modelProvider === "string" ? { modelProvider: raw.modelProvider } : {}),
|
|
82
91
|
...(typeof raw.modelId === "string" ? { modelId: raw.modelId } : {}),
|
|
83
92
|
...(typeof raw.thinkingLevel === "string" ? { thinkingLevel: raw.thinkingLevel } : {}),
|
|
93
|
+
...(typeof raw.telemetrySessionId === "string" && raw.telemetrySessionId
|
|
94
|
+
? { telemetrySessionId: raw.telemetrySessionId }
|
|
95
|
+
: {}),
|
|
84
96
|
...(typeof raw.workspaceId === "string" && raw.workspaceId
|
|
85
97
|
? { workspaceId: raw.workspaceId }
|
|
86
98
|
: {}),
|
|
@@ -165,6 +177,7 @@ export class ChatSessionStore {
|
|
|
165
177
|
const now = new Date().toISOString();
|
|
166
178
|
const record = {
|
|
167
179
|
...source,
|
|
180
|
+
telemetrySessionId: undefined,
|
|
168
181
|
sessionId: input.targetSessionId,
|
|
169
182
|
state: "active",
|
|
170
183
|
...(input.targetSessionFile ? { sessionFile: input.targetSessionFile } : { sessionFile: undefined }),
|
|
@@ -179,6 +192,19 @@ export class ChatSessionStore {
|
|
|
179
192
|
return record;
|
|
180
193
|
});
|
|
181
194
|
}
|
|
195
|
+
/** Atomically backfill the per-conversation simulated identity on first use. */
|
|
196
|
+
async ensureTelemetrySessionId(sessionId, createId) {
|
|
197
|
+
return this.serialize(sessionId, async () => {
|
|
198
|
+
const record = await this.get(sessionId);
|
|
199
|
+
if (!record)
|
|
200
|
+
return undefined;
|
|
201
|
+
if (record.telemetrySessionId)
|
|
202
|
+
return record.telemetrySessionId;
|
|
203
|
+
record.telemetrySessionId = createId();
|
|
204
|
+
await this.persist(record);
|
|
205
|
+
return record.telemetrySessionId;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
182
208
|
async rename(sessionId, title) {
|
|
183
209
|
return this.serialize(sessionId, async () => {
|
|
184
210
|
const record = await this.get(sessionId);
|
|
@@ -42,6 +42,13 @@ export const SHORTCUTS = [
|
|
|
42
42
|
source: "operator",
|
|
43
43
|
},
|
|
44
44
|
];
|
|
45
|
+
export const SDD_BINGO_SHORTCUT = {
|
|
46
|
+
command: "/sdd-bingo",
|
|
47
|
+
label: "sdd-bingo",
|
|
48
|
+
description: "SDD数据对齐",
|
|
49
|
+
action: "sdd-bingo",
|
|
50
|
+
source: "operator",
|
|
51
|
+
};
|
|
45
52
|
/** Pi Web commands with real browser-equivalent behavior (UI-11). */
|
|
46
53
|
export const WEB_SLASH_COMMANDS = [
|
|
47
54
|
{
|
|
@@ -98,7 +105,7 @@ export function resolveShortcut(input) {
|
|
|
98
105
|
const token = input.trim().split(/\s+/, 1)[0]?.toLowerCase();
|
|
99
106
|
if (!token?.startsWith("/"))
|
|
100
107
|
return undefined;
|
|
101
|
-
const local = [...SHORTCUTS, ...WEB_SLASH_COMMANDS].find((shortcut) => shortcut.command === token);
|
|
108
|
+
const local = [...SHORTCUTS, ...WEB_SLASH_COMMANDS, SDD_BINGO_SHORTCUT].find((shortcut) => shortcut.command === token);
|
|
102
109
|
if (local)
|
|
103
110
|
return { command: local.command, action: local.action };
|
|
104
111
|
return { command: token, action: "unknown" };
|
|
@@ -116,7 +123,7 @@ export function workspaceForShortcut(action) {
|
|
|
116
123
|
* Merge Operator, Web, and dynamic Session commands with conflict dedupe.
|
|
117
124
|
* Priority: operator/web > extension > skill > prompt. /reload is single-card.
|
|
118
125
|
*/
|
|
119
|
-
export function mergeSlashCommands(dynamic = []) {
|
|
126
|
+
export function mergeSlashCommands(dynamic = [], capabilities = {}) {
|
|
120
127
|
const byName = new Map();
|
|
121
128
|
const upsert = (item) => {
|
|
122
129
|
const key = normalizeCommandName(item.command);
|
|
@@ -149,6 +156,15 @@ export function mergeSlashCommands(dynamic = []) {
|
|
|
149
156
|
action: item.action,
|
|
150
157
|
});
|
|
151
158
|
}
|
|
159
|
+
if (capabilities.sddBingo) {
|
|
160
|
+
upsert({
|
|
161
|
+
command: SDD_BINGO_SHORTCUT.command,
|
|
162
|
+
label: SDD_BINGO_SHORTCUT.label,
|
|
163
|
+
description: SDD_BINGO_SHORTCUT.description,
|
|
164
|
+
source: SDD_BINGO_SHORTCUT.source,
|
|
165
|
+
action: SDD_BINGO_SHORTCUT.action,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
152
168
|
for (const item of dynamic) {
|
|
153
169
|
const command = normalizeCommandName(item.command);
|
|
154
170
|
if (!command)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-BPelzihi.js";import{p as f}from"./chunk-JWPE2WC7-RIg5_bFJ.js";import{_ as n,l as o}from"./mermaid.core-CwLZPtQo.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BBhF0NAT.js";import"./index-CVdddQsA.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-
|
|
1
|
+
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-CwLZPtQo.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|