dominds 1.13.2 → 1.15.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/dist/bootstrap/global-dialog-event-broadcaster.d.ts +18 -0
- package/dist/bootstrap/global-dialog-event-broadcaster.js +81 -0
- package/dist/dialog-fork.js +13 -12
- package/dist/dialog.d.ts +61 -50
- package/dist/dialog.js +284 -78
- package/dist/docs/dialog-system.md +12 -0
- package/dist/docs/dialog-system.zh.md +12 -0
- package/dist/docs/dominds-terminology.md +17 -0
- package/dist/docs/issues/global-dialog-event-broadcaster-missing.md +128 -0
- package/dist/docs/llm-provider-isolation.md +35 -0
- package/dist/docs/llm-provider-isolation.zh.md +35 -0
- package/dist/llm/client.d.ts +2 -1
- package/dist/llm/defaults.yaml +118 -4
- package/dist/llm/gen/anthropic.js +2 -4
- package/dist/llm/gen/codex.d.ts +11 -0
- package/dist/llm/gen/codex.js +41 -31
- package/dist/llm/gen/failure-classifier.js +17 -0
- package/dist/llm/gen/mock.js +45 -21
- package/dist/llm/gen/openai-compatible.d.ts +2 -0
- package/dist/llm/gen/openai-compatible.js +43 -38
- package/dist/llm/gen/openai.d.ts +3 -1
- package/dist/llm/gen/openai.js +888 -71
- package/dist/llm/gen/tool-call-context.d.ts +7 -2
- package/dist/llm/gen/tool-call-context.js +55 -13
- package/dist/llm/gen.d.ts +60 -3
- package/dist/llm/kernel-driver/context.js +1 -1
- package/dist/llm/kernel-driver/drive.js +374 -348
- package/dist/llm/kernel-driver/flow.js +3 -3
- package/dist/llm/kernel-driver/guardrails.d.ts +1 -1
- package/dist/llm/kernel-driver/guardrails.js +4 -4
- package/dist/llm/kernel-driver/runtime.js +11 -29
- package/dist/llm/kernel-driver/subdialog.js +56 -5
- package/dist/llm/kernel-driver/tellask-special.d.ts +38 -12
- package/dist/llm/kernel-driver/tellask-special.js +489 -180
- package/dist/llm/kernel-driver/types.d.ts +1 -1
- package/dist/persistence.d.ts +30 -62
- package/dist/persistence.js +978 -986
- package/dist/priming.js +398 -365
- package/dist/recovery/reply-special.js +3 -3
- package/dist/runtime/inter-dialog-format.d.ts +1 -1
- package/dist/runtime/inter-dialog-format.js +1 -1
- package/dist/runtime/reply-prompt-copy.js +4 -4
- package/dist/server/setup-routes.js +26 -5
- package/dist/server/snippets-routes.d.ts +1 -0
- package/dist/server/snippets-routes.js +20 -9
- package/dist/server/websocket-handler.js +58 -25
- package/dist/shared/utils/fbr.js +12 -8
- package/dist/shared/utils/inter-dialog-format.js +6 -4
- package/dist/team.d.ts +24 -13
- package/dist/team.js +123 -32
- package/dist/tool.d.ts +26 -0
- package/dist/tool.js +97 -0
- package/dist/tools/team_mgmt.js +18 -0
- package/package.json +2 -2
- package/webapp/dist/assets/{_basePickBy-CBOtd63g.js → _basePickBy-DsirmCgI.js} +3 -3
- package/webapp/dist/assets/_basePickBy-DsirmCgI.js.map +1 -0
- package/webapp/dist/assets/{_baseUniq-mfoKz4Wm.js → _baseUniq-tR6G8loB.js} +2 -2
- package/webapp/dist/assets/_baseUniq-tR6G8loB.js.map +1 -0
- package/webapp/dist/assets/{arc-Dq0WZLyu.js → arc-CzxpASkZ.js} +2 -2
- package/webapp/dist/assets/arc-CzxpASkZ.js.map +1 -0
- package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-CNmygmp3.js → architectureDiagram-2XIMDMQ5-BSH7H5oI.js} +26 -8
- package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-BSH7H5oI.js.map +1 -0
- package/webapp/dist/assets/{blockDiagram-VD42YOAC-DvE0lybt.js → blockDiagram-WCTKOSBZ-DpLIr7yO.js} +187 -170
- package/webapp/dist/assets/blockDiagram-WCTKOSBZ-DpLIr7yO.js.map +1 -0
- package/webapp/dist/assets/{c4Diagram-YG6GDRKO-CR7zJ2_u.js → c4Diagram-IC4MRINW-WuYKgWfY.js} +4 -4
- package/webapp/dist/assets/c4Diagram-IC4MRINW-WuYKgWfY.js.map +1 -0
- package/webapp/dist/assets/{channel-DrTrnYx4.js → channel-B-v9dqLN.js} +2 -2
- package/webapp/dist/assets/channel-B-v9dqLN.js.map +1 -0
- package/webapp/dist/assets/{chunk-4BX2VUAB-CVuJEIeN.js → chunk-4BX2VUAB-MtFUfKZy.js} +2 -2
- package/webapp/dist/assets/chunk-4BX2VUAB-MtFUfKZy.js.map +1 -0
- package/webapp/dist/assets/{chunk-55IACEB6-BxUoXApB.js → chunk-55IACEB6-rY9AJdzj.js} +2 -2
- package/webapp/dist/assets/chunk-55IACEB6-rY9AJdzj.js.map +1 -0
- package/webapp/dist/assets/{chunk-FMBD7UC4-TX-LVAaV.js → chunk-FMBD7UC4-B-RtOs7e.js} +2 -2
- package/webapp/dist/assets/chunk-FMBD7UC4-B-RtOs7e.js.map +1 -0
- package/webapp/dist/assets/{chunk-TZMSLE5B-Cw689yRl.js → chunk-JSJVCQXG-Da1d3uS4.js} +14 -6
- package/webapp/dist/assets/chunk-JSJVCQXG-Da1d3uS4.js.map +1 -0
- package/webapp/dist/assets/{chunk-QN33PNHL-D1uiKlOO.js → chunk-KX2RTZJC-DH9UrpuG.js} +2 -2
- package/webapp/dist/assets/chunk-KX2RTZJC-DH9UrpuG.js.map +1 -0
- package/webapp/dist/assets/{chunk-DI55MBZ5-SAhxUTqQ.js → chunk-NQ4KR5QH-CK365lrr.js} +9 -7
- package/webapp/dist/assets/chunk-NQ4KR5QH-CK365lrr.js.map +1 -0
- package/webapp/dist/assets/{chunk-QZHKN3VN-BxuV0Oba.js → chunk-QZHKN3VN-BCaWPGDm.js} +2 -2
- package/webapp/dist/assets/chunk-QZHKN3VN-BCaWPGDm.js.map +1 -0
- package/webapp/dist/assets/{chunk-B4BG7PRW-DpMa3-9L.js → chunk-WL4C6EOR-DDCnEwft.js} +171 -121
- package/webapp/dist/assets/chunk-WL4C6EOR-DDCnEwft.js.map +1 -0
- package/webapp/dist/assets/{classDiagram-2ON5EDUG-BTTGianr.js → classDiagram-VBA2DB6C-CvMBU4WA.js} +7 -6
- package/webapp/dist/assets/classDiagram-VBA2DB6C-CvMBU4WA.js.map +1 -0
- package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-BTTGianr.js → classDiagram-v2-RAHNMMFH-CvMBU4WA.js} +7 -6
- package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-CvMBU4WA.js.map +1 -0
- package/webapp/dist/assets/{clone-Dk8cAI3I.js → clone-r98jR0MC.js} +2 -2
- package/webapp/dist/assets/clone-r98jR0MC.js.map +1 -0
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-BjJnzB2N.js → cose-bilkent-S5V4N54A-t6J60Ogk.js} +2 -2
- package/webapp/dist/assets/cose-bilkent-S5V4N54A-t6J60Ogk.js.map +1 -0
- package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
- package/webapp/dist/assets/{dagre-6UL2VRFP-VF-xGhAf.js → dagre-KLK3FWXG-BlqmY2DV.js} +7 -7
- package/webapp/dist/assets/dagre-KLK3FWXG-BlqmY2DV.js.map +1 -0
- package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
- package/webapp/dist/assets/{diagram-PSM6KHXK-Ba5U0oRY.js → diagram-E7M64L7V-FwCHeIUD.js} +10 -10
- package/webapp/dist/assets/diagram-E7M64L7V-FwCHeIUD.js.map +1 -0
- package/webapp/dist/assets/{diagram-QEK2KX5R-DoYCnEw_.js → diagram-IFDJBPK2-NhtmkuZG.js} +9 -8
- package/webapp/dist/assets/diagram-IFDJBPK2-NhtmkuZG.js.map +1 -0
- package/webapp/dist/assets/{diagram-S2PKOQOG-CkK4SRyE.js → diagram-P4PSJMXO-B9FcmokX.js} +8 -8
- package/webapp/dist/assets/diagram-P4PSJMXO-B9FcmokX.js.map +1 -0
- package/webapp/dist/assets/{erDiagram-Q2GNP2WA-DkI5eYww.js → erDiagram-INFDFZHY-DHKmWvtB.js} +96 -75
- package/webapp/dist/assets/erDiagram-INFDFZHY-DHKmWvtB.js.map +1 -0
- package/webapp/dist/assets/{flowDiagram-NV44I4VS-wOdPUQ7Y.js → flowDiagram-PKNHOUZH-C7Zi8I7T.js} +98 -81
- package/webapp/dist/assets/flowDiagram-PKNHOUZH-C7Zi8I7T.js.map +1 -0
- package/webapp/dist/assets/{ganttDiagram-JELNMOA3-BtRWgkUH.js → ganttDiagram-A5KZAMGK-Cv2T8tz_.js} +28 -3
- package/webapp/dist/assets/ganttDiagram-A5KZAMGK-Cv2T8tz_.js.map +1 -0
- package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js → gitGraphDiagram-K3NZZRJ6-DztaipJU.js} +38 -46
- package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-DztaipJU.js.map +1 -0
- package/webapp/dist/assets/graph-C5yf62Vs.js +782 -0
- package/webapp/dist/assets/graph-C5yf62Vs.js.map +1 -0
- package/webapp/dist/assets/{index-xvYYeHuy.css → index-YaxF76or.css} +1 -1
- package/webapp/dist/assets/{index-rYmIohM_.js → index-hve5MWPs.js} +1603 -1415
- package/webapp/dist/assets/index-hve5MWPs.js.map +1 -0
- package/webapp/dist/assets/{infoDiagram-HS3SLOUP-BMaxCvH5.js → infoDiagram-LFFYTUFH-VgsbBPZP.js} +7 -7
- package/webapp/dist/assets/infoDiagram-LFFYTUFH-VgsbBPZP.js.map +1 -0
- package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
- package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js +966 -0
- package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js.map +1 -0
- package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-ejyerzmG.js → journeyDiagram-4ABVD52K-OO8sev-Y.js} +5 -5
- package/webapp/dist/assets/journeyDiagram-4ABVD52K-OO8sev-Y.js.map +1 -0
- package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-CYj35TEs.js → kanban-definition-K7BYSVSG-DiYCC1Ig.js} +5 -3
- package/webapp/dist/assets/kanban-definition-K7BYSVSG-DiYCC1Ig.js.map +1 -0
- package/webapp/dist/assets/{layout-7Ql4zmuL.js → layout-DdZSgGdu.js} +5 -5
- package/webapp/dist/assets/layout-DdZSgGdu.js.map +1 -0
- package/webapp/dist/assets/{linear-CVmgVPuZ.js → linear-7-aHtaFi.js} +2 -2
- package/webapp/dist/assets/linear-7-aHtaFi.js.map +1 -0
- package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-DOpxjGVo.js → mindmap-definition-YRQLILUH-IG3I-RdD.js} +7 -5
- package/webapp/dist/assets/mindmap-definition-YRQLILUH-IG3I-RdD.js.map +1 -0
- package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
- package/webapp/dist/assets/{pieDiagram-ADFJNKIX-CLQjpmAG.js → pieDiagram-SKSYHLDU-z68KJT5r.js} +8 -8
- package/webapp/dist/assets/pieDiagram-SKSYHLDU-z68KJT5r.js.map +1 -0
- package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-ClD_bz7z.js → quadrantDiagram-337W2JSQ-DaENWdO6.js} +3 -3
- package/webapp/dist/assets/quadrantDiagram-337W2JSQ-DaENWdO6.js.map +1 -0
- package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-DOpb-TWH.js → requirementDiagram-Z7DCOOCP-ROTFv4sa.js} +16 -6
- package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-ROTFv4sa.js.map +1 -0
- package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-D8Hsj3yx.js → sankeyDiagram-WA2Y5GQK-CK7qtpzw.js} +2 -2
- package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CK7qtpzw.js.map +1 -0
- package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-CFMNjBER.js → sequenceDiagram-2WXFIKYE-R5lDySeI.js} +601 -201
- package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-R5lDySeI.js.map +1 -0
- package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-BQeDlw0P.js → stateDiagram-RAJIS63D-sr7msF5U.js} +9 -9
- package/webapp/dist/assets/stateDiagram-RAJIS63D-sr7msF5U.js.map +1 -0
- package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-DscX61Rs.js → stateDiagram-v2-FVOUBMTO-X663liwS.js} +5 -5
- package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-X663liwS.js.map +1 -0
- package/webapp/dist/assets/{timeline-definition-IT6M3QCI-BcXPSTiw.js → timeline-definition-YZTLITO2-Bw0TdG26.js} +3 -3
- package/webapp/dist/assets/timeline-definition-YZTLITO2-Bw0TdG26.js.map +1 -0
- package/webapp/dist/assets/{treemap-GDKQZRPO-BBr4UV0Z.js → treemap-KZPCXAKY-D_sjKwI7.js} +37 -24
- package/webapp/dist/assets/treemap-KZPCXAKY-D_sjKwI7.js.map +1 -0
- package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js +2487 -0
- package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js.map +1 -0
- package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-CS5RAtQE.js → xychartDiagram-JWTSCODW-C65ESjTc.js} +4 -4
- package/webapp/dist/assets/xychartDiagram-JWTSCODW-C65ESjTc.js.map +1 -0
- package/webapp/dist/index.html +2 -2
- package/webapp/dist/assets/_basePickBy-CBOtd63g.js.map +0 -1
- package/webapp/dist/assets/_baseUniq-mfoKz4Wm.js.map +0 -1
- package/webapp/dist/assets/arc-Dq0WZLyu.js.map +0 -1
- package/webapp/dist/assets/architectureDiagram-VXUJARFQ-CNmygmp3.js.map +0 -1
- package/webapp/dist/assets/blockDiagram-VD42YOAC-DvE0lybt.js.map +0 -1
- package/webapp/dist/assets/c4Diagram-YG6GDRKO-CR7zJ2_u.js.map +0 -1
- package/webapp/dist/assets/channel-DrTrnYx4.js.map +0 -1
- package/webapp/dist/assets/chunk-4BX2VUAB-CVuJEIeN.js.map +0 -1
- package/webapp/dist/assets/chunk-55IACEB6-BxUoXApB.js.map +0 -1
- package/webapp/dist/assets/chunk-B4BG7PRW-DpMa3-9L.js.map +0 -1
- package/webapp/dist/assets/chunk-DI55MBZ5-SAhxUTqQ.js.map +0 -1
- package/webapp/dist/assets/chunk-FMBD7UC4-TX-LVAaV.js.map +0 -1
- package/webapp/dist/assets/chunk-QN33PNHL-D1uiKlOO.js.map +0 -1
- package/webapp/dist/assets/chunk-QZHKN3VN-BxuV0Oba.js.map +0 -1
- package/webapp/dist/assets/chunk-TZMSLE5B-Cw689yRl.js.map +0 -1
- package/webapp/dist/assets/classDiagram-2ON5EDUG-BTTGianr.js.map +0 -1
- package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-BTTGianr.js.map +0 -1
- package/webapp/dist/assets/clone-Dk8cAI3I.js.map +0 -1
- package/webapp/dist/assets/cose-bilkent-S5V4N54A-BjJnzB2N.js.map +0 -1
- package/webapp/dist/assets/dagre-6UL2VRFP-VF-xGhAf.js.map +0 -1
- package/webapp/dist/assets/diagram-PSM6KHXK-Ba5U0oRY.js.map +0 -1
- package/webapp/dist/assets/diagram-QEK2KX5R-DoYCnEw_.js.map +0 -1
- package/webapp/dist/assets/diagram-S2PKOQOG-CkK4SRyE.js.map +0 -1
- package/webapp/dist/assets/erDiagram-Q2GNP2WA-DkI5eYww.js.map +0 -1
- package/webapp/dist/assets/flowDiagram-NV44I4VS-wOdPUQ7Y.js.map +0 -1
- package/webapp/dist/assets/ganttDiagram-JELNMOA3-BtRWgkUH.js.map +0 -1
- package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js.map +0 -1
- package/webapp/dist/assets/graph-DAMkuTbn.js +0 -425
- package/webapp/dist/assets/graph-DAMkuTbn.js.map +0 -1
- package/webapp/dist/assets/index-rYmIohM_.js.map +0 -1
- package/webapp/dist/assets/infoDiagram-HS3SLOUP-BMaxCvH5.js.map +0 -1
- package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-ejyerzmG.js.map +0 -1
- package/webapp/dist/assets/kanban-definition-3W4ZIXB7-CYj35TEs.js.map +0 -1
- package/webapp/dist/assets/layout-7Ql4zmuL.js.map +0 -1
- package/webapp/dist/assets/linear-CVmgVPuZ.js.map +0 -1
- package/webapp/dist/assets/mindmap-definition-VGOIOE7T-DOpxjGVo.js.map +0 -1
- package/webapp/dist/assets/pieDiagram-ADFJNKIX-CLQjpmAG.js.map +0 -1
- package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-ClD_bz7z.js.map +0 -1
- package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-DOpb-TWH.js.map +0 -1
- package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-D8Hsj3yx.js.map +0 -1
- package/webapp/dist/assets/sequenceDiagram-WL72ISMW-CFMNjBER.js.map +0 -1
- package/webapp/dist/assets/stateDiagram-FKZM4ZOC-BQeDlw0P.js.map +0 -1
- package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-DscX61Rs.js.map +0 -1
- package/webapp/dist/assets/timeline-definition-IT6M3QCI-BcXPSTiw.js.map +0 -1
- package/webapp/dist/assets/treemap-GDKQZRPO-BBr4UV0Z.js.map +0 -1
- package/webapp/dist/assets/xychartDiagram-PRI3JC2R-CS5RAtQE.js.map +0 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Issue: `Global dialog event broadcaster missing`
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
在没有初始化全局 dialog event broadcaster 的运行环境里,发布 `new_q4h_asked` / `q4h_answered` / `subdialog_created_evt` / `dlg_touched_evt` 会直接抛错:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Global dialog event broadcaster missing: cannot publish new_q4h_asked for dialog=<id>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
这不只是噪音日志。对于 `askHuman` / Q4H 链路,这个异常会被上层当作“Q4H 注册失败”处理,导致:
|
|
12
|
+
|
|
13
|
+
- Q4H 状态文件其实已经写入
|
|
14
|
+
- 但 runtime 仍然走失败分支
|
|
15
|
+
- 追加 `stream_error_evt`
|
|
16
|
+
- 向当前对话注入失败型 tellask result
|
|
17
|
+
- 造成“持久化成功、广播缺失、业务语义却被当作失败”的错位
|
|
18
|
+
|
|
19
|
+
## Observed In
|
|
20
|
+
|
|
21
|
+
- `tests/recovery/reply-special-after-restart.ts`
|
|
22
|
+
- 其它不启动完整 websocket server、但会触发 Q4H runtime 的脚本型 / test 型运行环境
|
|
23
|
+
|
|
24
|
+
## Current Call Chain
|
|
25
|
+
|
|
26
|
+
1. `main/llm/kernel-driver/tellask-special.ts`
|
|
27
|
+
`executeTellaskCall()` 在 Q4H 分支先调用 `DialogPersistence.appendQuestion4HumanState()` 落盘
|
|
28
|
+
2. 同一分支随后调用 `postDialogEvent(dlg, { type: 'new_q4h_asked', ... })`
|
|
29
|
+
3. `main/evt-registry.ts`
|
|
30
|
+
`dispatchGloballyIfNeeded()` 发现 `new_q4h_asked` 属于 global-only event
|
|
31
|
+
4. 若 `globalDialogEventBroadcaster === null`,则直接 `throw`
|
|
32
|
+
5. 异常被 `tellask-special.ts` 的 Q4H `try/catch` 捕获
|
|
33
|
+
6. 上层误判为“Q4H register invariant violation”,转入失败补偿路径
|
|
34
|
+
|
|
35
|
+
## Correct Contract
|
|
36
|
+
|
|
37
|
+
这里应明确:**global dialog event broadcaster 不是可选增强,而是 runtime 必要基础设施**。
|
|
38
|
+
|
|
39
|
+
也就是说:
|
|
40
|
+
|
|
41
|
+
- 任何支持 dialog runtime 的运行环境,在进入对话驱动 / Q4H / 子对话逻辑前,都必须先完成 broadcaster bootstrap
|
|
42
|
+
- WebUI server 只是其中一种 runtime;将来其它 runtime 也必须在位
|
|
43
|
+
- 测试运行环境同样应按 runtime bootstrap 的方式安装 broadcaster,而不是在业务 helper 里零散补丁
|
|
44
|
+
|
|
45
|
+
因此,这个问题的根因不是“event layer 应该 graceful degrade”,而是**存在未完成 broadcaster bootstrap 的 runtime**,并且这个缺陷直到业务路径中途才暴露。
|
|
46
|
+
|
|
47
|
+
## Why It Happened
|
|
48
|
+
|
|
49
|
+
此前全局 broadcaster 只在 `main/server/websocket-handler.ts` 的 websocket server 初始化阶段通过 `setGlobalDialogEventBroadcaster(...)` 安装。
|
|
50
|
+
|
|
51
|
+
但脚本 / 测试 / recovery 运行环境也会直接触发同一批 global-only event。于是形成了:
|
|
52
|
+
|
|
53
|
+
- dialog-scoped event registry 已可用
|
|
54
|
+
- global broadcaster 尚未安装
|
|
55
|
+
- `postDialogEvent()` 处理 global-only event 时立刻抛错
|
|
56
|
+
- 上层业务把该异常误判成 Q4H 注册失败 / 业务失败
|
|
57
|
+
|
|
58
|
+
## Impact
|
|
59
|
+
|
|
60
|
+
### User-visible / behavior impact
|
|
61
|
+
|
|
62
|
+
- `askHuman` 在未完成 runtime bootstrap 的环境下会被错误地当作失败处理
|
|
63
|
+
- 当前对话会收到失败型 tellask result,而不是保持正常 pending Q4H 语义
|
|
64
|
+
|
|
65
|
+
### Persistence / state consistency impact
|
|
66
|
+
|
|
67
|
+
- Q4H state 已经写入,但 runtime 业务语义被标成失败
|
|
68
|
+
- 形成“状态已存在、当前轮回答却说失败”的不一致
|
|
69
|
+
|
|
70
|
+
### Test / diagnostics impact
|
|
71
|
+
|
|
72
|
+
- 用例可能通过,但日志出现误导性错误
|
|
73
|
+
- 排障时很难第一眼区分“runtime 缺少 broadcaster bootstrap”还是“Q4H 真的注册失败”
|
|
74
|
+
|
|
75
|
+
## Repro
|
|
76
|
+
|
|
77
|
+
1. 在不启动 websocket server、且未安装 recording broadcaster 的脚本环境里创建 dialog
|
|
78
|
+
2. 触发 `askHuman`
|
|
79
|
+
3. 观察日志出现 `Global dialog event broadcaster missing`
|
|
80
|
+
4. 观察上层继续记录 `Q4H register invariant violation`
|
|
81
|
+
|
|
82
|
+
## Root Cause
|
|
83
|
+
|
|
84
|
+
根因不是 Q4H 注册本身,而是 runtime contract 与 bootstrap 现实不一致:
|
|
85
|
+
|
|
86
|
+
- 契约上:broadcaster 是 mandatory infra
|
|
87
|
+
- 现实里:只有 websocket server 显式安装,其它 runtime 没有统一 bootstrap
|
|
88
|
+
|
|
89
|
+
于是“基础设施未初始化”在业务路径中被表象成“Q4H 注册失败”。
|
|
90
|
+
|
|
91
|
+
## Resolution Direction
|
|
92
|
+
|
|
93
|
+
按下面原则修:
|
|
94
|
+
|
|
95
|
+
1. 保持 event layer 的强约束:global-only event 没有 broadcaster 时仍然应 loud fail
|
|
96
|
+
2. 修复点应放在 runtime bootstrap,而不是在 Q4H/子对话业务链路里做“广播失败 best-effort”
|
|
97
|
+
3. 所有 runtime 入口都必须在业务逻辑前安装 broadcaster
|
|
98
|
+
- WebUI server:安装 websocket fanout broadcaster
|
|
99
|
+
- tests / script runtimes:安装 recording broadcaster,可抓取广播内容,也可在无断言需求时忽略
|
|
100
|
+
4. 测试不得再通过业务 helper 临时塞 `() => {}` 绕过问题;应通过统一 runtime bootstrap 安装 recorder
|
|
101
|
+
5. 若 future runtime 漏装 broadcaster,应在 bootstrap 阶段或运行环境初始化阶段尽早暴露,而不是等到 `askHuman` 中途再炸
|
|
102
|
+
|
|
103
|
+
## Rejected Direction
|
|
104
|
+
|
|
105
|
+
以下方向不再采用:
|
|
106
|
+
|
|
107
|
+
- 让 `dispatchGloballyIfNeeded()` graceful degrade
|
|
108
|
+
- 在 `askHuman` 链路里把 broadcast 当成 best-effort
|
|
109
|
+
- 允许测试/business helper 就地注入 noop broadcaster 掩盖 bootstrap 缺口
|
|
110
|
+
|
|
111
|
+
这些做法都会继续模糊“mandatory infra”契约,让问题从 runtime 初始化阶段滑落到业务中途。
|
|
112
|
+
|
|
113
|
+
## Applied Fix Direction
|
|
114
|
+
|
|
115
|
+
当前约定下,正确修法是:
|
|
116
|
+
|
|
117
|
+
- 新增统一 broadcaster bootstrap API
|
|
118
|
+
- WebUI server 改为通过统一 bootstrap API 安装 websocket broadcaster
|
|
119
|
+
- rtws tests 改为通过统一 runner 安装 recording broadcaster
|
|
120
|
+
- kernel-driver helpers 不再偷偷安装 noop broadcaster,而是断言 runtime 已完成 bootstrap
|
|
121
|
+
|
|
122
|
+
## Related Files
|
|
123
|
+
|
|
124
|
+
- `main/bootstrap/global-dialog-event-broadcaster.ts`
|
|
125
|
+
- `main/evt-registry.ts`
|
|
126
|
+
- `main/server/websocket-handler.ts`
|
|
127
|
+
- `tests/rtws-script-runner.ts`
|
|
128
|
+
- `tests/kernel-driver/helpers.ts`
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# LLM Provider Isolation
|
|
2
|
+
|
|
3
|
+
## Principle
|
|
4
|
+
|
|
5
|
+
Dominds treats each LLM provider wrapper as an isolated protocol adapter, not as a flavor of a shared "OpenAI-like" abstraction.
|
|
6
|
+
|
|
7
|
+
This means:
|
|
8
|
+
|
|
9
|
+
- `apiType: codex` owns Codex-native request fields, stream events, tool semantics, and defaults.
|
|
10
|
+
- `apiType: openai` owns OpenAI Responses request fields, stream events, tool semantics, and defaults.
|
|
11
|
+
- `apiType: openai-compatible` owns Chat Completions semantics, even when it reuses the `model_params.openai.*` namespace.
|
|
12
|
+
|
|
13
|
+
Similar field names across wrappers do not imply compatibility. For example, `reasoning_effort`, `verbosity`, `parallel_tool_calls`, and web search controls may look similar but can still differ in accepted values, payload shape, lifecycle events, validation rules, and runtime meaning.
|
|
14
|
+
|
|
15
|
+
## Hard Rules
|
|
16
|
+
|
|
17
|
+
- A wrapper must only read its own provider namespace when building requests.
|
|
18
|
+
- A wrapper must only interpret its own provider-native stream events.
|
|
19
|
+
- A wrapper must not silently fall back to another provider's params, aliases, or event assumptions.
|
|
20
|
+
- Cross-provider convergence is allowed only at the driver/storage/UI boundary, after provider-native events have already been decoded into discriminated unions.
|
|
21
|
+
|
|
22
|
+
## Why
|
|
23
|
+
|
|
24
|
+
This isolation keeps provider integrations honest:
|
|
25
|
+
|
|
26
|
+
- fewer accidental "compatible by coincidence" behaviors
|
|
27
|
+
- easier debugging when providers diverge
|
|
28
|
+
- less hidden coupling between wrappers
|
|
29
|
+
- safer upgrades when official APIs evolve independently
|
|
30
|
+
|
|
31
|
+
## Current Boundary
|
|
32
|
+
|
|
33
|
+
The backend currently uses provider-specific web search event variants inside wrappers and projects them into a narrower dialog event shape in `main/llm/kernel-driver/drive.ts`.
|
|
34
|
+
|
|
35
|
+
That projection layer is intentional: it is the compatibility boundary. Wrapper code on either side should stay provider-native.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# LLM Provider 隔离原则
|
|
2
|
+
|
|
3
|
+
## 原则
|
|
4
|
+
|
|
5
|
+
Dominds 把每个 LLM provider wrapper 视为独立的协议适配器,而不是某种“大一统 OpenAI-like 抽象”的一个变体。
|
|
6
|
+
|
|
7
|
+
这意味着:
|
|
8
|
+
|
|
9
|
+
- `apiType: codex` 只负责 Codex 原生的请求字段、流事件、工具语义和默认行为。
|
|
10
|
+
- `apiType: openai` 只负责 OpenAI Responses 原生的请求字段、流事件、工具语义和默认行为。
|
|
11
|
+
- `apiType: openai-compatible` 虽然复用 `model_params.openai.*` 命名空间,但它负责的是 Chat Completions 语义,不是 Codex,也不是 Responses。
|
|
12
|
+
|
|
13
|
+
不同 wrapper 下看起来同名的字段,不代表它们可以互相兼容。比如 `reasoning_effort`、`verbosity`、`parallel_tool_calls`、web search 相关开关,名字可能相似,但可接受值、请求载荷形状、流事件生命周期、校验规则和运行时含义都可能不同。
|
|
14
|
+
|
|
15
|
+
## 强约束
|
|
16
|
+
|
|
17
|
+
- wrapper 构造请求时,只能读取自己的 provider 参数命名空间。
|
|
18
|
+
- wrapper 解析流事件时,只能解释自己的 provider 原生事件。
|
|
19
|
+
- wrapper 内禁止静默 fallback 到别的 provider 参数、别名或事件假设。
|
|
20
|
+
- 只有在 driver / storage / UI 边界,才允许把 provider-native 事件投影成更窄的共享形态,而且前提是 wrapper 侧已经先完成了 provider-native 解码。
|
|
21
|
+
|
|
22
|
+
## 为什么
|
|
23
|
+
|
|
24
|
+
这样做是为了让 provider 集成保持诚实:
|
|
25
|
+
|
|
26
|
+
- 减少“碰巧兼容”带来的隐式行为
|
|
27
|
+
- 在 provider 分叉时更容易排查问题
|
|
28
|
+
- 降低 wrapper 之间的隐藏耦合
|
|
29
|
+
- 在官方 API 各自演化时,升级更安全
|
|
30
|
+
|
|
31
|
+
## 当前边界
|
|
32
|
+
|
|
33
|
+
目前后端在 wrapper 内保留 provider-specific 的 web search 事件类型,再在 `main/llm/kernel-driver/drive.ts` 投影成较窄的 dialog 事件形态。
|
|
34
|
+
|
|
35
|
+
这个投影层是有意设计的,它就是兼容边界。边界两侧的 wrapper 代码都应继续保持 provider-native。
|
package/dist/llm/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ChatMessage, EnvironmentMsg, FuncCallMsg, FuncResultMsg, PromptingMsg, SayingMsg,
|
|
1
|
+
export type { ChatMessage, EnvironmentMsg, FuncCallMsg, FuncResultMsg, PromptingMsg, SayingMsg, TellaskCarryoverMsg, TellaskResultMsg, ThinkingMsg, TransientGuideMsg, } from '@longrun-ai/kernel/types/chat-message';
|
|
2
2
|
export interface ModelInfo {
|
|
3
3
|
name?: string;
|
|
4
4
|
context_length?: number;
|
|
@@ -52,6 +52,7 @@ export type ProviderApiType = 'codex' | 'anthropic' | 'mock' | 'openai' | 'opena
|
|
|
52
52
|
export type ProviderConfig = {
|
|
53
53
|
name: string;
|
|
54
54
|
apiType: ProviderApiType;
|
|
55
|
+
apiQuirks?: string | string[];
|
|
55
56
|
baseUrl: string;
|
|
56
57
|
apiKeyEnvVar: string;
|
|
57
58
|
tool_result_max_chars?: number;
|
package/dist/llm/defaults.yaml
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
# - llm_retry_conservative_delay_ms: base delay for provider-classified conservative retries (default 30000; fixed for first 10 retries, then ramps by x1.5 with the normal max-delay cap).
|
|
5
5
|
# - llm_retry_backoff_multiplier: exponential factor between retries (default 1.5).
|
|
6
6
|
# - llm_retry_max_delay_ms: upper bound for retry delay (default 1800000 / 30m).
|
|
7
|
+
# - apiQuirks: optional provider/gateway quirk profile(s) for non-standard transport behavior
|
|
8
|
+
# and retry classification. Example: `apiQuirks: xcode.best`.
|
|
7
9
|
# - tool_result_max_chars: optional transport-level cap for a single tool-result text payload
|
|
8
10
|
# before Dominds projects it into the provider request. Use this when a provider/gateway enforces
|
|
9
11
|
# a stricter per-item string limit than Dominds' built-in defaults.
|
|
@@ -27,6 +29,9 @@ providers:
|
|
|
27
29
|
min: 1
|
|
28
30
|
description: Maximum tokens to generate (provider-agnostic override).
|
|
29
31
|
codex:
|
|
32
|
+
# Isolation principle:
|
|
33
|
+
# - These hints describe the Codex wrapper only.
|
|
34
|
+
# - Same-looking fields under `openai` are not aliases and are not fallback-compatible.
|
|
30
35
|
reasoning_effort:
|
|
31
36
|
prominent: true
|
|
32
37
|
default: high
|
|
@@ -701,14 +706,23 @@ providers:
|
|
|
701
706
|
apiKeyEnvVar: OPENAI_API_KEY
|
|
702
707
|
tech_spec_url: https://platform.openai.com/docs
|
|
703
708
|
api_mgmt_url: https://platform.openai.com/api-keys
|
|
709
|
+
# These are documentation hints for humans/agents; they are not auto-applied.
|
|
704
710
|
model_param_options:
|
|
705
711
|
general:
|
|
706
712
|
max_tokens:
|
|
713
|
+
# Provider-agnostic cap on generated tokens.
|
|
707
714
|
type: integer
|
|
708
715
|
min: 1
|
|
709
716
|
description: Maximum tokens to generate (provider-agnostic override).
|
|
710
717
|
openai:
|
|
718
|
+
# Isolation principle:
|
|
719
|
+
# - These hints describe the OpenAI Responses wrapper only.
|
|
720
|
+
# - Same-looking fields under `codex` are not aliases and are not fallback-compatible.
|
|
711
721
|
reasoning_effort:
|
|
722
|
+
prominent: true
|
|
723
|
+
default: high
|
|
724
|
+
# Trade latency/cost for quality on reasoning-capable models.
|
|
725
|
+
# Allowed: none | minimal | low | medium | high | xhigh
|
|
712
726
|
type: enum
|
|
713
727
|
values: [none, minimal, low, medium, high, xhigh]
|
|
714
728
|
description: Reasoning effort level (when supported by the model).
|
|
@@ -717,36 +731,136 @@ providers:
|
|
|
717
731
|
default: auto
|
|
718
732
|
values: [auto, concise, detailed, none]
|
|
719
733
|
description: Reasoning summary detail level (when supported by the model).
|
|
734
|
+
service_tier:
|
|
735
|
+
prominent: true
|
|
736
|
+
type: enum
|
|
737
|
+
values: [auto, default, flex, scale, priority]
|
|
738
|
+
description: Responses `service_tier` request parameter.
|
|
720
739
|
verbosity:
|
|
740
|
+
prominent: true
|
|
741
|
+
default: medium
|
|
742
|
+
# Control response detail level on GPT-5 series models.
|
|
743
|
+
# Allowed: low | medium | high
|
|
721
744
|
type: enum
|
|
722
745
|
values: [low, medium, high]
|
|
723
746
|
description: Response verbosity/detail level (GPT-5 series).
|
|
724
747
|
temperature:
|
|
748
|
+
# Randomness; use 0–0.2 for tool-calling / deterministic behavior.
|
|
725
749
|
type: number
|
|
726
750
|
min: 0
|
|
727
751
|
max: 2
|
|
728
752
|
description: Sampling temperature (0–2).
|
|
729
753
|
top_p:
|
|
754
|
+
# Nucleus sampling; usually leave unset if using temperature.
|
|
730
755
|
type: number
|
|
731
756
|
min: 0
|
|
732
757
|
max: 1
|
|
733
758
|
description: Nucleus sampling probability (0–1).
|
|
734
|
-
|
|
759
|
+
parallel_tool_calls:
|
|
735
760
|
type: boolean
|
|
736
|
-
description:
|
|
761
|
+
description: Responses `parallel_tool_calls` request parameter.
|
|
762
|
+
safety_identifier:
|
|
763
|
+
type: string
|
|
764
|
+
description: Responses `safety_identifier` request parameter.
|
|
765
|
+
text_format:
|
|
766
|
+
prominent: true
|
|
767
|
+
default: text
|
|
768
|
+
type: enum
|
|
769
|
+
values: [text, json_object, json_schema]
|
|
770
|
+
description: Responses `text.format.type`. Prefer `json_schema` over legacy `json_object`.
|
|
771
|
+
text_format_json_schema_name:
|
|
772
|
+
type: string
|
|
773
|
+
description: Required when `text_format=json_schema`; mapped to `text.format.name`.
|
|
774
|
+
text_format_json_schema:
|
|
775
|
+
type: string
|
|
776
|
+
description: Required when `text_format=json_schema`; JSON-encoded schema object mapped to `text.format.schema`.
|
|
777
|
+
text_format_json_schema_strict:
|
|
778
|
+
type: boolean
|
|
779
|
+
description: Optional `text.format.strict` when `text_format=json_schema`.
|
|
780
|
+
web_search_tool:
|
|
781
|
+
prominent: true
|
|
782
|
+
type: boolean
|
|
783
|
+
description: "Enable native Responses `tools: [{type:'web_search'}]`."
|
|
784
|
+
web_search_context_size:
|
|
785
|
+
type: enum
|
|
786
|
+
default: medium
|
|
787
|
+
values: [low, medium, high]
|
|
788
|
+
description: Native web_search `search_context_size`.
|
|
789
|
+
web_search_allowed_domains:
|
|
790
|
+
type: string_array
|
|
791
|
+
description: Native web_search `filters.allowed_domains`.
|
|
792
|
+
web_search_include_sources:
|
|
793
|
+
type: boolean
|
|
794
|
+
description: Include `web_search_call.action.sources` in the response payload.
|
|
737
795
|
max_tokens:
|
|
738
796
|
type: integer
|
|
739
797
|
min: 1
|
|
740
798
|
description: Provider-specific max tokens override.
|
|
741
799
|
models:
|
|
742
|
-
gpt-5.
|
|
743
|
-
name: GPT-5.
|
|
800
|
+
gpt-5.4:
|
|
801
|
+
name: GPT-5.4
|
|
802
|
+
optimal_max_tokens: 200000
|
|
803
|
+
# Caution remediation reinjection cadence in generation turns (default: 10).
|
|
804
|
+
caution_remediation_cadence_generations: 10
|
|
805
|
+
context_length: 272000
|
|
806
|
+
input_length: 272000
|
|
807
|
+
output_length: 32768
|
|
808
|
+
context_window: '272K'
|
|
809
|
+
gpt-5.4-mini:
|
|
810
|
+
name: GPT-5.4 Mini
|
|
811
|
+
optimal_max_tokens: 200000
|
|
812
|
+
# Caution remediation reinjection cadence in generation turns (default: 10).
|
|
813
|
+
caution_remediation_cadence_generations: 10
|
|
744
814
|
context_length: 272000
|
|
745
815
|
input_length: 272000
|
|
746
816
|
output_length: 32768
|
|
747
817
|
context_window: '272K'
|
|
818
|
+
gpt-5.3-codex:
|
|
819
|
+
name: GPT-5.3 Codex
|
|
820
|
+
optimal_max_tokens: 200000
|
|
821
|
+
# Caution remediation reinjection cadence in generation turns (default: 10).
|
|
822
|
+
caution_remediation_cadence_generations: 10
|
|
823
|
+
context_length: 272000
|
|
824
|
+
input_length: 272000
|
|
825
|
+
output_length: 32768
|
|
826
|
+
context_window: '272K'
|
|
827
|
+
gpt-5.3-codex-spark:
|
|
828
|
+
name: GPT-5.3 Codex Spark
|
|
829
|
+
optimal_max_tokens: 80000
|
|
830
|
+
# Caution remediation reinjection cadence in generation turns (default: 10).
|
|
831
|
+
caution_remediation_cadence_generations: 3
|
|
832
|
+
context_length: 128000
|
|
833
|
+
input_length: 128000
|
|
834
|
+
output_length: 32768
|
|
835
|
+
context_window: '128K'
|
|
748
836
|
gpt-5.2-codex:
|
|
749
837
|
name: GPT-5.2 Codex
|
|
838
|
+
optimal_max_tokens: 200000
|
|
839
|
+
# Caution remediation reinjection cadence in generation turns (default: 10).
|
|
840
|
+
caution_remediation_cadence_generations: 10
|
|
841
|
+
context_length: 272000
|
|
842
|
+
input_length: 272000
|
|
843
|
+
output_length: 32768
|
|
844
|
+
context_window: '272K'
|
|
845
|
+
gpt-5.2:
|
|
846
|
+
name: GPT-5.2
|
|
847
|
+
optimal_max_tokens: 200000
|
|
848
|
+
# Caution remediation reinjection cadence in generation turns (default: 10).
|
|
849
|
+
caution_remediation_cadence_generations: 10
|
|
850
|
+
context_length: 272000
|
|
851
|
+
input_length: 272000
|
|
852
|
+
output_length: 32768
|
|
853
|
+
context_window: '272K'
|
|
854
|
+
gpt-5.1-codex-mini:
|
|
855
|
+
name: GPT-5.1 Codex Mini
|
|
856
|
+
optimal_max_tokens: 200000
|
|
857
|
+
context_length: 272000
|
|
858
|
+
input_length: 272000
|
|
859
|
+
output_length: 32768
|
|
860
|
+
context_window: '272K'
|
|
861
|
+
gpt-5.1-codex-max:
|
|
862
|
+
name: GPT-5.1 Codex Max
|
|
863
|
+
optimal_max_tokens: 200000
|
|
750
864
|
context_length: 272000
|
|
751
865
|
input_length: 272000
|
|
752
866
|
output_length: 32768
|
|
@@ -376,9 +376,7 @@ function chatMessageToContentBlocks(chatMsg) {
|
|
|
376
376
|
return [block];
|
|
377
377
|
}
|
|
378
378
|
// Handle saying and thinking messages from assistant
|
|
379
|
-
if (chatMsg.type === 'saying_msg' ||
|
|
380
|
-
chatMsg.type === 'ui_only_markdown_msg' ||
|
|
381
|
-
chatMsg.type === 'thinking_msg') {
|
|
379
|
+
if (chatMsg.type === 'saying_msg' || chatMsg.type === 'thinking_msg') {
|
|
382
380
|
const block = { type: 'text', text: chatMsg.content };
|
|
383
381
|
return [block];
|
|
384
382
|
}
|
|
@@ -406,7 +404,7 @@ function chatMessageToContentBlocks(chatMsg) {
|
|
|
406
404
|
return [block];
|
|
407
405
|
}
|
|
408
406
|
// Handle tellask call results (NOT LLM-native tool use; represented as role='user' text)
|
|
409
|
-
if (chatMsg.type === 'tellask_result_msg' || chatMsg.type === '
|
|
407
|
+
if (chatMsg.type === 'tellask_result_msg' || chatMsg.type === 'tellask_carryover_msg') {
|
|
410
408
|
const msg = {
|
|
411
409
|
type: 'text',
|
|
412
410
|
text: chatMsg.content,
|
package/dist/llm/gen/codex.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Module: llm/gen/codex
|
|
3
3
|
*
|
|
4
4
|
* ChatGPT Codex responses integration (streaming-only).
|
|
5
|
+
* Isolation principle: this wrapper owns Codex-native request/stream semantics and must not reuse
|
|
6
|
+
* OpenAI Responses parameter namespaces or event interpretations.
|
|
5
7
|
*/
|
|
6
8
|
import type { ChatGptResponsesRequest } from '@longrun-ai/codex-auth';
|
|
7
9
|
import type { Team } from '../../team';
|
|
@@ -9,6 +11,15 @@ import type { FuncTool } from '../../tool';
|
|
|
9
11
|
import type { ChatMessage, ProviderConfig } from '../client';
|
|
10
12
|
import type { LlmBatchResult, LlmFailureDisposition, LlmGenerator, LlmRequestContext, LlmStreamReceiver, LlmStreamResult } from '../gen';
|
|
11
13
|
export declare function resolveCodexServiceTier(serviceTier: ChatGptResponsesRequest['service_tier'] | undefined): Exclude<NonNullable<ChatGptResponsesRequest['service_tier']>, 'default'> | undefined;
|
|
14
|
+
export declare function spliceCodexBuiltinPrompt(params: {
|
|
15
|
+
template: string;
|
|
16
|
+
defaultModel: string;
|
|
17
|
+
loadPrompt: (model: string) => string | null;
|
|
18
|
+
}): string;
|
|
19
|
+
export declare function resolveCodexInstructions(systemPrompt: string, options?: {
|
|
20
|
+
defaultModel?: string;
|
|
21
|
+
loadPrompt?: (model: string) => string | null;
|
|
22
|
+
}): string;
|
|
12
23
|
export declare class CodexGen implements LlmGenerator {
|
|
13
24
|
get apiType(): string;
|
|
14
25
|
classifyFailure(error: unknown): LlmFailureDisposition | undefined;
|
package/dist/llm/gen/codex.js
CHANGED
|
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.CodexGen = void 0;
|
|
37
37
|
exports.resolveCodexServiceTier = resolveCodexServiceTier;
|
|
38
|
+
exports.spliceCodexBuiltinPrompt = spliceCodexBuiltinPrompt;
|
|
39
|
+
exports.resolveCodexInstructions = resolveCodexInstructions;
|
|
38
40
|
const log_1 = require("../../log");
|
|
39
41
|
const i18n_text_1 = require("../../runtime/i18n-text");
|
|
40
42
|
const work_language_1 = require("../../runtime/work-language");
|
|
@@ -44,6 +46,7 @@ const tool_call_context_1 = require("./tool-call-context");
|
|
|
44
46
|
const tool_output_limit_1 = require("./tool-output-limit");
|
|
45
47
|
const log = (0, log_1.createLogger)('llm/codex');
|
|
46
48
|
const codexFallbackInstructions = 'You are Codex CLI.';
|
|
49
|
+
const CODEX_SYSTEM_PROMPT_DIRECTIVE_PATTERN = /^([ \t]*)@codex-system-prompt(?::([A-Za-z0-9._-]+))?([ \t]*)$/gm;
|
|
47
50
|
function resolveCodexServiceTier(serviceTier) {
|
|
48
51
|
// The ChatGPT codex backend rejects the literal `default` tier even though some SDK typings
|
|
49
52
|
// still list it. Omitting the field preserves the standard tier semantics without a 400.
|
|
@@ -112,19 +115,29 @@ function tryExtractApiReturnedModel(value) {
|
|
|
112
115
|
const trimmed = model.trim();
|
|
113
116
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
114
117
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
118
|
+
function spliceCodexBuiltinPrompt(params) {
|
|
119
|
+
let replaced = false;
|
|
120
|
+
const resolved = params.template.replace(CODEX_SYSTEM_PROMPT_DIRECTIVE_PATTERN, (_match, leading, overrideModel, trailing) => {
|
|
121
|
+
const selectedModel = overrideModel ?? params.defaultModel;
|
|
122
|
+
const prompt = params.loadPrompt(selectedModel);
|
|
123
|
+
if (prompt === null) {
|
|
124
|
+
throw new Error(`Bundled Codex prompt template not found for model: ${selectedModel}`);
|
|
125
|
+
}
|
|
126
|
+
replaced = true;
|
|
127
|
+
return `${leading}${prompt}${trailing}`;
|
|
128
|
+
});
|
|
129
|
+
return replaced ? resolved : params.template;
|
|
130
|
+
}
|
|
131
|
+
function resolveCodexInstructions(systemPrompt, options) {
|
|
132
|
+
const baseInstructions = systemPrompt.trim().length > 0 ? systemPrompt : codexFallbackInstructions;
|
|
133
|
+
if (options?.defaultModel === undefined || options.loadPrompt === undefined) {
|
|
134
|
+
return baseInstructions;
|
|
123
135
|
}
|
|
124
|
-
return {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
return spliceCodexBuiltinPrompt({
|
|
137
|
+
template: baseInstructions,
|
|
138
|
+
defaultModel: options.defaultModel,
|
|
139
|
+
loadPrompt: options.loadPrompt,
|
|
140
|
+
});
|
|
128
141
|
}
|
|
129
142
|
function funcToolToCodex(funcTool) {
|
|
130
143
|
// MCP schemas are passed through to providers. Codex tool schema types are narrower; runtime
|
|
@@ -145,16 +158,12 @@ const CODEX_JSON_RESPONSE_SCHEMA = {
|
|
|
145
158
|
additionalProperties: true,
|
|
146
159
|
};
|
|
147
160
|
function resolveCodexWebSearchMode(agent) {
|
|
148
|
-
|
|
149
|
-
return codexParams?.web_search ?? 'live';
|
|
161
|
+
return agent.model_params?.codex?.web_search ?? 'live';
|
|
150
162
|
}
|
|
151
163
|
function resolveCodexJsonResponseEnabled(agent) {
|
|
152
164
|
const providerSpecific = agent.model_params?.codex?.json_response;
|
|
153
165
|
if (providerSpecific !== undefined)
|
|
154
166
|
return providerSpecific;
|
|
155
|
-
const openAiSpecific = agent.model_params?.openai?.json_response;
|
|
156
|
-
if (openAiSpecific !== undefined)
|
|
157
|
-
return openAiSpecific;
|
|
158
167
|
return agent.model_params?.json_response === true;
|
|
159
168
|
}
|
|
160
169
|
function buildCodexNativeTools(agent) {
|
|
@@ -168,7 +177,8 @@ function buildCodexNativeTools(agent) {
|
|
|
168
177
|
return [webSearchTool];
|
|
169
178
|
}
|
|
170
179
|
function buildCodexTextControls(agent) {
|
|
171
|
-
|
|
180
|
+
// Provider isolation rule: the Codex wrapper only consumes `model_params.codex.*`.
|
|
181
|
+
const codexParams = agent.model_params?.codex;
|
|
172
182
|
const text = {};
|
|
173
183
|
if (codexParams && codexParams.verbosity) {
|
|
174
184
|
text.verbosity = codexParams.verbosity;
|
|
@@ -184,7 +194,8 @@ function buildCodexTextControls(agent) {
|
|
|
184
194
|
return Object.keys(text).length > 0 ? text : undefined;
|
|
185
195
|
}
|
|
186
196
|
function buildCodexReasoning(agent) {
|
|
187
|
-
|
|
197
|
+
// Provider isolation rule: do not borrow OpenAI Responses params inside the Codex wrapper.
|
|
198
|
+
const codexParams = agent.model_params?.codex;
|
|
188
199
|
if (codexParams?.reasoning_effort === undefined && codexParams?.reasoning_summary === undefined) {
|
|
189
200
|
return null;
|
|
190
201
|
}
|
|
@@ -214,6 +225,7 @@ function assertNoCodexNativeToolCollisions(funcTools, nativeTools) {
|
|
|
214
225
|
}
|
|
215
226
|
function toLlmWebSearchCall(item, itemId, phase) {
|
|
216
227
|
return {
|
|
228
|
+
source: 'codex',
|
|
217
229
|
phase,
|
|
218
230
|
itemId,
|
|
219
231
|
status: item.status,
|
|
@@ -290,12 +302,11 @@ function chatMessageToCodexItems(msg) {
|
|
|
290
302
|
return [messageItem('user', msg.content)];
|
|
291
303
|
case 'transient_guide_msg':
|
|
292
304
|
case 'saying_msg':
|
|
293
|
-
case 'ui_only_markdown_msg':
|
|
294
305
|
return [messageItem('assistant', msg.content)];
|
|
295
306
|
case 'thinking_msg':
|
|
296
307
|
return [thinkingMessageToCodexReasoningItem(msg)];
|
|
297
308
|
case 'tellask_result_msg':
|
|
298
|
-
case '
|
|
309
|
+
case 'tellask_carryover_msg':
|
|
299
310
|
return [messageItem('user', msg.content)];
|
|
300
311
|
case 'func_call_msg':
|
|
301
312
|
return [
|
|
@@ -402,17 +413,13 @@ async function buildCodexInput(context, providerConfig) {
|
|
|
402
413
|
}
|
|
403
414
|
return input;
|
|
404
415
|
}
|
|
405
|
-
async function buildCodexRequest(providerConfig, agent, instructions,
|
|
416
|
+
async function buildCodexRequest(providerConfig, agent, instructions, funcTools, requestContext, context) {
|
|
406
417
|
if (!agent.model) {
|
|
407
418
|
throw new Error(`Internal error: Model is undefined for agent '${agent.id}'`);
|
|
408
419
|
}
|
|
409
|
-
const input =
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
input.push(messageItem('assistant', assistantPrelude));
|
|
413
|
-
}
|
|
414
|
-
input.push(...(await buildCodexInput(context, providerConfig)));
|
|
415
|
-
const codexParams = agent.model_params?.codex ?? agent.model_params?.openai;
|
|
420
|
+
const input = await buildCodexInput(context, providerConfig);
|
|
421
|
+
// Provider isolation rule: request construction must only read Codex-native params here.
|
|
422
|
+
const codexParams = agent.model_params?.codex;
|
|
416
423
|
const parallelToolCalls = codexParams?.parallel_tool_calls ?? true;
|
|
417
424
|
const reasoning = buildCodexReasoning(agent);
|
|
418
425
|
const include = reasoning !== null ? ['reasoning.encrypted_content'] : [];
|
|
@@ -462,8 +469,11 @@ class CodexGen {
|
|
|
462
469
|
if (!agent.model) {
|
|
463
470
|
throw new Error(`Internal error: Model is undefined for agent '${agent.id}'`);
|
|
464
471
|
}
|
|
465
|
-
const
|
|
466
|
-
|
|
472
|
+
const instructions = resolveCodexInstructions(systemPrompt, {
|
|
473
|
+
defaultModel: agent.model,
|
|
474
|
+
loadPrompt: codexAuth.loadCodexPromptSync,
|
|
475
|
+
});
|
|
476
|
+
const payload = await buildCodexRequest(providerConfig, agent, instructions, funcTools, requestContext, context);
|
|
467
477
|
let sayingStarted = false;
|
|
468
478
|
let thinkingStarted = false;
|
|
469
479
|
let sawOutputText = false;
|
|
@@ -115,6 +115,23 @@ function classifyOpenAiLikeFailure(error) {
|
|
|
115
115
|
const lowerMessage = message.toLowerCase();
|
|
116
116
|
const status = readErrorStatus(error);
|
|
117
117
|
const code = readErrorCode(error);
|
|
118
|
+
if (code === 'OPENAI_MALFORMED_BATCH_OUTPUT_ITEM') {
|
|
119
|
+
return {
|
|
120
|
+
kind: 'fatal',
|
|
121
|
+
message,
|
|
122
|
+
status,
|
|
123
|
+
code,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (code === 'XCODE_BEST_STREAM_INTERNAL_ERROR') {
|
|
127
|
+
return {
|
|
128
|
+
kind: 'retriable',
|
|
129
|
+
message,
|
|
130
|
+
status,
|
|
131
|
+
code,
|
|
132
|
+
retryStrategy: 'aggressive',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
118
135
|
if (status === 503 || status === 529 || isConservativeRetryMessage(lowerMessage)) {
|
|
119
136
|
return {
|
|
120
137
|
kind: 'retriable',
|