w-dispatch-ai 1.0.25 → 1.0.27

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.
Files changed (62) hide show
  1. package/README.md +57 -4
  2. package/dist/w-dispatch-ai.umd.js +2 -2
  3. package/dist/w-dispatch-ai.umd.js.map +1 -1
  4. package/docs/WDispatchAi.mjs.html +6 -4
  5. package/docs/adapters.mjs.html +10 -3
  6. package/docs/budgetFor.mjs.html +2 -2
  7. package/docs/buildValidator.mjs.html +2 -2
  8. package/docs/castPintOr.mjs.html +2 -2
  9. package/docs/dfTimeoutMs.mjs.html +2 -2
  10. package/docs/dispatchAi.mjs.html +2 -2
  11. package/docs/dispatchAiFallback.mjs.html +2 -2
  12. package/docs/dispatchAiWkf.mjs.html +2 -2
  13. package/docs/dispatchAntigravity.mjs.html +2 -2
  14. package/docs/dispatchApiOpenaiCompat.mjs.html +2 -2
  15. package/docs/dispatchApiOpenaiResponses.mjs.html +2 -2
  16. package/docs/dispatchApiTypesafeSystemone.mjs.html +447 -0
  17. package/docs/dispatchClaude.mjs.html +2 -2
  18. package/docs/dispatchCodex.mjs.html +2 -2
  19. package/docs/dispatchOpencode.mjs.html +27 -5
  20. package/docs/getCliArgs.mjs.html +2 -2
  21. package/docs/getErrorResult.mjs.html +2 -2
  22. package/docs/getErrorType.mjs.html +4 -3
  23. package/docs/global.html +1254 -247
  24. package/docs/index.html +2 -2
  25. package/docs/quota_dfQuotaTimeoutMs.mjs.html +2 -2
  26. package/docs/quota_fetchQuotaJson.mjs.html +2 -2
  27. package/docs/quota_fromCodexUsageHttp.mjs.html +2 -2
  28. package/docs/quota_getQuotaAntigravity.mjs.html +2 -2
  29. package/docs/quota_getQuotaClaude.mjs.html +2 -2
  30. package/docs/quota_getQuotaCodex.mjs.html +2 -2
  31. package/docs/quota_readJsonOrNull.mjs.html +2 -2
  32. package/docs/quota_toQuotaLabel.mjs.html +2 -2
  33. package/docs/quota_toQuotaResult.mjs.html +2 -2
  34. package/docs/quota_toQuotaScopedLabel.mjs.html +2 -2
  35. package/docs/quota_toQuotaWindow.mjs.html +2 -2
  36. package/docs/readEnvFile.mjs.html +2 -2
  37. package/docs/resolveProviders.mjs.html +2 -2
  38. package/docs/wkf_callAiWithFallback.mjs.html +2 -2
  39. package/docs/wkf_createFileStore.mjs.html +2 -2
  40. package/docs/wkf_createUsageCounter.mjs.html +2 -2
  41. package/docs/wkf_extractJsonLoose.mjs.html +2 -2
  42. package/docs/wkf_noSideEffectPrefix.mjs.html +2 -2
  43. package/docs/wkf_runFanout.mjs.html +2 -2
  44. package/docs/wkf_runFanoutPipeline.mjs.html +2 -2
  45. package/docs/wkf_runRolePipeline.mjs.html +2 -2
  46. package/docs/wkf_salvageTruncatedArray.mjs.html +2 -2
  47. package/g.mjs +2 -2
  48. package/package.json +1 -1
  49. package/src/WDispatchAi.mjs +4 -2
  50. package/src/adapters.mjs +8 -1
  51. package/src/dispatchApiTypesafeSystemone.mjs +375 -0
  52. package/src/dispatchOpencode.mjs +25 -3
  53. package/src/getErrorType.mjs +2 -1
  54. package/src/providers.mjs +35 -1
  55. package/test/tools/fakeServerForApiTest.mjs +75 -5
  56. package/test/unit-WDispatchAi.test.mjs +10 -6
  57. package/test/unit-adapters.test.mjs +5 -3
  58. package/test/unit-dispatchAi.test.mjs +1 -1
  59. package/test/unit-dispatchApiTypesafeSystemone.test.mjs +159 -0
  60. package/test/unit-dispatchOpencode.test.mjs +25 -0
  61. package/test/unit-providers.test.mjs +14 -0
  62. package/test/unit-resolveProviders.test.mjs +2 -2
package/README.md CHANGED
@@ -32,7 +32,7 @@ Note:
32
32
  #### Functions:
33
33
  | function | description |
34
34
  | --- | --- |
35
- | `dispatchAi(kind, prompt, opt)` | dispatch to the adapter of `kind`, one of `'opencode'`、`'claude'`、`'codex'`、`'antigravity'`、`'api-openai-compat'`、`'api-openai-responses'` |
35
+ | `dispatchAi(kind, prompt, opt)` | dispatch to the adapter of `kind`, one of `'opencode'`、`'claude'`、`'codex'`、`'antigravity'`、`'api-openai-compat'`、`'api-openai-responses'`、`'api-typesafe-systemone'` |
36
36
  | `dispatchAiFallback(prompt, opt)` | call ai with an ordered provider list, auto rotating keys within a group and falling back to the next group |
37
37
  | `dispatchAiWkf(opt)` | workflow factory: inject a named provider table once, returns bound `callAi`/`runFanout`/`runRolePipeline`/`runFanoutPipeline` |
38
38
  | `dispatchOpencode(prompt, opt)` | call an ai model by opencode cli, supports per-call api key and provider config |
@@ -41,6 +41,7 @@ Note:
41
41
  | `dispatchAntigravity(prompt, opt)` | call an ai model by google antigravity cli (`agy`), a multi-model gateway (gemini, claude, gpt-oss) |
42
42
  | `dispatchApiOpenaiCompat(prompt, opt)` | call an ai model by direct fetch to any OpenAI-compatible API (`baseURL`+`key`+`model`), no cli and no login required |
43
43
  | `dispatchApiOpenaiResponses(prompt, opt)` | same, but for the OpenAI **Responses API** (`/responses`) — required by model families that are not served on `/chat/completions` (e.g. OpenCode Zen's muse-spark and GPT families) |
44
+ | `dispatchApiTypesafeSystemone(prompt, opt)` | call TypeSafe's **jev** decision model (`POST /v1/systemone`): not text generation — the prompt is the state to evaluate and `opt.questions` defines typed yes/no, choice and score questions; returns typed `answers` with probabilities (API only, TypeSafe has no cli) |
44
45
  | `providers` | curated provider entries verified by real tests (cli and rest paths), pick or use all via `resolveProviders` |
45
46
  | `resolveProviders(providers, opt)` | expand `envVar` → `keys` from env (comma-separated, missing vars auto-skipped), supports `pick` subset by id, `exes` per-kind exe injection and `patch` per-id field override; unknown picked ids are reported in `missing` with fuzzy spelling `hints` |
46
47
  | `readEnvFile(file)` | read a `.env` file into a plain object for `resolveProviders`'s `opt.env`, without polluting `process.env` |
@@ -52,7 +53,7 @@ Note:
52
53
  | `getQuotaClaude(email, opt)` | read the current subscription quota windows (5h / 7d / per-model 7d) of the locally logged-in Claude Code account via Anthropic's OAuth usage API; `email` is compared against the local account, not used to look one up |
53
54
  | `getQuotaCodex(email, opt)` | same for the Codex CLI account: primary path `codex app-server` JSON-RPC (auth handled by codex), fallback to chatgpt.com's usage endpoint |
54
55
  | `getQuotaAntigravity(email, opt)` | same for the Antigravity CLI (`agy`) account via its headless `-p "/usage" --output-format json` (agy ≥ 1.1.11, version-gated) |
55
- | `KINDS` | array of available kinds, `['opencode', 'claude', 'codex', 'antigravity', 'api-openai-compat', 'api-openai-responses']` |
56
+ | `KINDS` | array of available kinds, `['opencode', 'claude', 'codex', 'antigravity', 'api-openai-compat', 'api-openai-responses', 'api-typesafe-systemone']` |
56
57
 
57
58
  #### Example:
58
59
  > **Link:** [[dev source code](https://github.com/yuda-lyu/w-dispatch-ai/blob/master/g.mjs)]
@@ -86,7 +87,7 @@ let test = async () => {
86
87
 
87
88
  //可用之AI供應商種類
88
89
  console.log('KINDS:', wdi.KINDS)
89
- // => KINDS: [ 'opencode', 'claude', 'codex', 'antigravity', 'api-openai-compat', 'api-openai-responses' ]
90
+ // => KINDS: [ 'opencode', 'claude', 'codex', 'antigravity', 'api-openai-compat', 'api-openai-responses', 'api-typesafe-systemone' ]
90
91
 
91
92
  let prompt = '請只回覆兩個字:完成,不要有任何其他文字'
92
93
 
@@ -228,6 +229,7 @@ await test()
228
229
  | --- | --- | --- | --- |
229
230
  | `key` | String | `''` | 該provider之API key,須與`provider`同時給予才會以`OPENCODE_AUTH_CONTENT`注入 |
230
231
  | `provider` | String | `''` | `key`所屬provider名稱,須與`model`為同一組 |
232
+ | `useStoredAuth` | Boolean | `true` | 未注入金鑰時是否沿用本機`auth.json`之登入。`false`代表以空憑證(`OPENCODE_AUTH_CONTENT='{}'`)匿名存取,用於opencode免費模型,避免結果隨本機登入帳號而異(登入帳號之工作區未開該模型時會回`Model is disabled`);已同時給`key`與`provider`時不作用 |
231
233
  | `config` | Object\|String | `null` | opencode設定內容,將以`OPENCODE_CONFIG_CONTENT`注入,供補上第三方provider之定義 |
232
234
  | `agent` | String | `'build'` | opencode代理名稱 |
233
235
 
@@ -316,12 +318,63 @@ Codex 0.149 起 Windows 預設走 elevated 沙箱(專用使用者 `CodexSandbo
316
318
  | `http` | HTTP非2xx(`code`為狀態碼) | api類 |
317
319
  | `fetch` | 網路層錯誤(DNS/連線拒絕) | api類 |
318
320
  | `tool-unsupported` | 模型回tool_calls而api類不支援工具 | api類 |
319
- | `invalid-response` | 回應缺`choices[0].message.content` | api類 |
321
+ | `invalid-response` | 回應結構不合規(缺`choices[0].message.content`、缺`output`陣列,或 systemone 缺`answers`/缺所請求題目之答案) | api類 |
320
322
  | `aborted` | `shouldStop`中止 | fallback層 |
321
323
  | `budget` | 時間預算用盡 | fallback層 |
322
324
 
323
325
  僅涵蓋**機械可判**者:CLI類之其餘失敗(額度上限/金鑰無效/服務端錯誤,各家字樣不同且隨版本漂移)一律歸`exec`,套件不維護簽章表(與否決金鑰停用清單同一理由)——需細分時以`coolDetect`式注入自判,或依`tried`內之`error`與`stderr`自行決策。
324
326
 
327
+ #### Options only for dispatchApiTypesafeSystemone:
328
+ [TypeSafe](https://typesafe.ai) 的 **jev** 是「System One」決策模型:**不產生文字**,而是對一段內容(state)回答你定義的型別化問題,每題回傳受限於你給的選項之答案與機率。官方只提供 API 與 Python/JavaScript SDK,**沒有 CLI**,故本套件只有 API 版(`kind: 'api-typesafe-systemone'`)。權威文件:[API reference](https://docs.typesafe.ai/api)。
329
+
330
+ | key | type | default | description |
331
+ | --- | --- | --- | --- |
332
+ | `questions` | Object | 必填 | 題目物件,鍵為自訂題目 id,值為下表三型之一;題型與欄位由伺服器驗證(不合規回 422) |
333
+ | `baseURL` | String | `'https://api.typesafe.ai/v1'` | 將於尾端接上`/systemone` |
334
+ | `model` | String | `'jev-latest'` | 另有`'jev-preview'`;回應之實際版本見結果之`modelResolved`(如`'jev-1.13.0'`) |
335
+ | `key` | String | `''` | API key(`.env` 慣用 `TYPESAFE_KEYS`),以`Bearer`置於`Authorization`標頭 |
336
+ | `body`/`headers` | Object | `{}` | 額外請求本體/標頭,同名鍵覆寫預設 |
337
+ | `timeoutMs`/`validate`/`maxRetries`/`retryDelayMs` | | | 同 `dispatchApiOpenaiCompat`(4xx 除 429 外不重試) |
338
+
339
+ | 題型 `type` | `criteria` | 答案欄位 |
340
+ | --- | --- | --- |
341
+ | `noul`(是非題) | 選填 `{ true, false }` 說明是與否的意思 | `noul`:答案為「是」的機率(0~1) |
342
+ | `choice`(單選題) | 必填 `{ 選項: 描述或 null }` | `choice`、`probabilities`、`confidence` |
343
+ | `score`(有序量表) | 必填 `[層級描述, ...]`(至少 2 級) | `score`(可落在兩級之間)、`legend`、`probabilities`、`confidence` |
344
+
345
+ ```alias
346
+ let r = await wdi.dispatchApiTypesafeSystemone('房間浴室水龍頭一直滴水,吵到睡不著', {
347
+ key: typesafeKeys[0],
348
+ questions: {
349
+ category: {
350
+ type: 'choice',
351
+ instructions: '這則客房訊息屬於哪一類?',
352
+ criteria: {
353
+ '設備故障報修': '客人回報房間硬體設備損壞、水電問題或故障',
354
+ '索取備品': '客人需要毛巾、牙刷、礦泉水等客房備品',
355
+ '退房詢問': '詢問退房時間、行李寄放或延退相關事宜',
356
+ '其他複雜對話': '閒聊、餐廳推薦或特殊客訴',
357
+ },
358
+ },
359
+ urgent: { type: 'noul', instructions: '客人是否表達急迫性?' },
360
+ },
361
+ })
362
+ console.log(r.ok, r.answers.category.choice, r.answers.category.probabilities, r.answers.urgent.noul)
363
+ // => true 設備故障報修 { '設備故障報修': 1, '索取備品': 0, '退房詢問': 0, '其他複雜對話': 0 } 0.86 (2026-09-17 實測約 1 秒;noul 為機率,每次可能差 0.01)
364
+ ```
365
+
366
+ - **prompt 即 state**:結構化內容請傳 `JSON.stringify(物件)`(實測與傳物件之答案一致),題目的 `instructions` 可用 `` `ticket.messages[0].text` `` 這類路徑指向其中欄位。
367
+ - **結果**:`stdout` 為 `answers` 的 JSON 字串(遞補層與工作流層的 `parse`/`check` 可直接用),另追加 `answers`(已解析物件)與 `modelResolved`;`usage` 為 `{ input_tokens, output_tokens }` 原樣透傳。請求的題目 id 在回應中缺任何一個即回 `invalid-response`。
368
+ - **錯誤(實測)**:壞金鑰 401、未知 model 400、題型不合規 422,皆為 `errorType: 'http'`,原始本體(含 `detail`)在 `stderr`。
369
+ - **預設 `providers` 收有 `typesafe:jev-latest`(`envVar: 'TYPESAFE_KEYS'`),請以 `pick` 單獨取出**,`questions` 放呼叫層即會透傳;它不可與文字生成條目一起遞補(答案形狀不同)。全取做文字遞補時,此條因沒有 `questions` 會以 `params` 錯誤 0 毫秒失敗(每把金鑰各一次)後換下一家,不影響其他家;它刻意不放在清單末端,免得前面全敗時最終錯誤變成「questions 必填」而掩蓋真正原因:
370
+
371
+ ```alias
372
+ let { providers: jev } = wdi.resolveProviders(wdi.providers, { env, pick: ['typesafe:jev-latest'] })
373
+ let r = await wdi.dispatchAiFallback(state, { providers: jev, questions })
374
+ ```
375
+
376
+ - **經工作流 `callAi` 呼叫時務必傳 `promptPrefix: ''`**:預設的防寫檔前綴會被當成 state 的一部分送去評估。
377
+
325
378
  #### Options for dispatchAiFallback:
326
379
  | key | type | default | description |
327
380
  | --- | --- | --- | --- |