@cartanova/qgrid-cli 2.5.2 → 2.5.4

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 (68) hide show
  1. package/README.ko.md +7 -2
  2. package/README.md +7 -2
  3. package/bundle/dist/application/monit/monit.frame.js +3 -3
  4. package/bundle/dist/application/qgrid/oauth.js +1 -1
  5. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +41 -15
  6. package/bundle/dist/application/qgrid/qgrid.frame.js +74 -28
  7. package/bundle/dist/application/qgrid/qgrid.types.js +5 -3
  8. package/bundle/dist/application/qgrid/tool-emulation-schema.js +121 -0
  9. package/bundle/dist/application/qgrid/tool-emulation.js +116 -96
  10. package/bundle/dist/application/request-log/request-log.model.js +3 -3
  11. package/bundle/dist/application/request-log-step/request-log-step.model.js +2 -2
  12. package/bundle/dist/application/token/token.model.js +2 -2
  13. package/bundle/dist/server-error-handler.js +15 -0
  14. package/bundle/dist/sonamu.config.js +4 -7
  15. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +5 -4
  16. package/bundle/dist/utils/providers/anthropic/claude-session.js +7 -5
  17. package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +6 -4
  18. package/bundle/dist/utils/providers/common/schema-validation.js +178 -0
  19. package/bundle/dist/utils/providers/common/strictifier.js +329 -42
  20. package/bundle/dist/utils/providers/openai/codex-worker.js +16 -8
  21. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +5 -37
  22. package/bundle/dist/utils/providers/openai/openai-worker-pool-config.js +37 -0
  23. package/bundle/src/application/monit/monit.frame.ts +1 -1
  24. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +103 -0
  25. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +358 -1
  26. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +50 -28
  27. package/bundle/src/application/qgrid/qgrid.frame.test.ts +629 -8
  28. package/bundle/src/application/qgrid/qgrid.frame.ts +107 -27
  29. package/bundle/src/application/qgrid/qgrid.types.ts +4 -1
  30. package/bundle/src/application/qgrid/tool-emulation-schema.test.ts +188 -0
  31. package/bundle/src/application/qgrid/tool-emulation-schema.ts +176 -0
  32. package/bundle/src/application/qgrid/tool-emulation.test.ts +285 -67
  33. package/bundle/src/application/qgrid/tool-emulation.ts +199 -102
  34. package/bundle/src/server-error-handler.ts +22 -0
  35. package/bundle/src/sonamu.config.test.ts +37 -0
  36. package/bundle/src/sonamu.config.ts +2 -5
  37. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +56 -0
  38. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +3 -3
  39. package/bundle/src/utils/providers/anthropic/claude-session.test.ts +10 -0
  40. package/bundle/src/utils/providers/anthropic/claude-session.ts +6 -1
  41. package/bundle/src/utils/providers/anthropic/stream-json-adapter.test.ts +66 -0
  42. package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +12 -3
  43. package/bundle/src/utils/providers/common/provider-dispatcher.ts +2 -0
  44. package/bundle/src/utils/providers/common/schema-validation.test.ts +284 -0
  45. package/bundle/src/utils/providers/common/schema-validation.ts +308 -0
  46. package/bundle/src/utils/providers/common/strictifier.test.ts +746 -0
  47. package/bundle/src/utils/providers/common/strictifier.ts +513 -52
  48. package/bundle/src/utils/providers/openai/codex-worker.test.ts +31 -0
  49. package/bundle/src/utils/providers/openai/codex-worker.ts +12 -4
  50. package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +6 -4
  51. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +6 -79
  52. package/bundle/src/utils/providers/openai/openai-worker-pool-config.ts +80 -0
  53. package/bundle/web-dist/client/assets/{index-BQuKeBC5.js → index-DiY5V2e3.js} +3 -3
  54. package/bundle/web-dist/client/assets/{logs-BSsHMIKT.js → logs-UZ4Cvdmc.js} +1 -1
  55. package/bundle/web-dist/client/assets/{monit-D5ypGmGi.js → monit-C5cJo-HD.js} +1 -1
  56. package/bundle/web-dist/client/assets/{routes-BsCbz225.js → routes-DEKEmoQN.js} +1 -1
  57. package/bundle/web-dist/client/assets/{show-CXPhLZCl.js → show-DnX6b8KG.js} +1 -1
  58. package/bundle/web-dist/client/assets/{tokens-gEGH52O8.js → tokens-Ct0FGD4b.js} +1 -1
  59. package/bundle/web-dist/client/index.html +1 -1
  60. package/bundle/web-dist/server/entry-server.generated.js +1 -1
  61. package/package.json +1 -1
  62. package/skills/qgrid/references/ai-sdk-provider-contract.md +42 -2
  63. package/skills/qgrid/references/anthropic-claude-code-runtime.md +31 -1
  64. package/skills/qgrid/references/cli-env-and-server-boot.md +1 -1
  65. package/skills/qgrid/references/decision-rationale.md +11 -1
  66. package/skills/qgrid/references/repo-map.md +2 -1
  67. package/skills/qgrid/references/tool-calling-and-multiturn.md +33 -6
  68. package/skills/qgrid/references/verification-and-debugging.md +16 -1
@@ -10,6 +10,7 @@ Use this reference before changing Anthropic provider behavior, Claude Code spaw
10
10
  - Spawn args
11
11
  - Spawn env
12
12
  - Config isolation
13
+ - Timeout and cancellation
13
14
  - Input adaptation
14
15
  - Output adaptation
15
16
  - Fable refusal fallback
@@ -84,6 +85,9 @@ Important details:
84
85
  - `--setting-sources project` plus seeded settings isolates user configuration.
85
86
  - `--system-prompt` or `--system-prompt-file` is always supplied. Omitting it would allow Claude Code default system prompt injection.
86
87
  - Large system prompts over 64 KiB are written to a temporary file to avoid argv `E2BIG`.
88
+ - Final structured-output schemas are limited to 64 KiB UTF-8 because Claude Code receives each
89
+ schema as one `--json-schema` argv value. qgrid applies this check before request-log or stream
90
+ allocation and repeats it at dispatcher/session boundaries.
87
91
  - `--thinking disabled`, `MAX_THINKING_TOKENS=0`, and adaptive thinking env suppression keep thinking off for existing models. Fable 5 requires always-on adaptive thinking. Opus 5 defaults to adaptive thinking and rejects disabled thinking at `xhigh`/`max` effort. qgrid omits all three suppressors for both models and uses `effort` to control depth.
88
92
 
89
93
  ## Spawn env
@@ -118,6 +122,24 @@ Never pass `ANTHROPIC_API_KEY` or inherited auth env vars to child processes. OA
118
122
 
119
123
  This blocks user-scope Claude settings, hooks, memory, and token cross-contamination as much as the current Claude Code interface allows.
120
124
 
125
+ ## Timeout and cancellation
126
+
127
+ Claude sessions default to a 240-second server-side process timeout. AI SDK callers can override it
128
+ with `providerOptions.qgrid.timeoutMs`; the wire value is validated as a positive integer no greater
129
+ than 30 minutes and is applied to both `generateText` and `streamText`.
130
+
131
+ Do not substitute AI SDK's standard top-level `timeout` for this setting. AI SDK consumes that value
132
+ as its overall client budget and exposes only the resulting `abortSignal` to custom providers. The
133
+ two controls are complementary: `timeoutMs` limits the Claude process, while the AI SDK timeout also
134
+ covers client/network overhead.
135
+
136
+ Cancellation must reach the child process independently of the timer:
137
+
138
+ - AI SDK aborts cancel the qgrid HTTP request.
139
+ - Streaming SSE close aborts the provider execution.
140
+ - Non-streaming request abort or incomplete response close aborts the provider execution.
141
+ - Logged requests cancelled by disconnect finish as `aborted`, not `error`.
142
+
121
143
  ## Input adaptation
122
144
 
123
145
  qgrid sends Claude stdin as JSONL with `--input-format stream-json`.
@@ -134,7 +156,10 @@ This is not equivalent to persistent Claude session reuse. It is full-history re
134
156
  Claude stdout JSONL is parsed by `stream-json-adapter.ts`.
135
157
 
136
158
  - Text mode streams `text_delta`.
137
- - Structured mode streams `input_json_delta` and preserves `StructuredOutput` tool input as final text.
159
+ - Structured mode streams `input_json_delta`. On a successful result, qgrid returns
160
+ `result.structured_output`, because earlier assistant `StructuredOutput` inputs may be attempts
161
+ that Claude Code rejected before an internal retry. The first tool input is retained only as a
162
+ diagnostic fallback when no successful final structured output is available.
138
163
  - `result` lines provide final text, usage, duration, cost, subtype, and terminal reason.
139
164
  - Non-success subtype, `is_error`, or `terminal_reason: model_error` is treated as an error.
140
165
  - Quota exhaustion is detected when text starts with `You've hit`.
@@ -192,6 +217,11 @@ qgrid pins this env var only when the call is **structured and streaming**. The
192
217
 
193
218
  Non-streaming structured calls therefore keep Claude Code's default retry budget. This matters because Claude models comply with structured output less reliably than OpenAI models: pinning retries to 1 turns a single rejected attempt into an immediate `error_max_structured_output_retries`. Measured on dev0 before the fix, `deti_production` on Opus 4.8 failed 39.6% of non-streaming structured calls for this reason.
194
219
 
220
+ When a non-streaming retry succeeds, the result event's `structured_output` is the canonical
221
+ schema-validated value. Do not let a previously captured assistant tool input override it. Preserve
222
+ the first input only for non-success results such as `error_max_turns` or
223
+ `error_max_structured_output_retries`, where it is useful diagnostic evidence.
224
+
195
225
  The other half of SON-495 is unchanged: any non-success subtype still sets `isError`, so a degenerate output after retries fails honestly instead of leaking partial data.
196
226
 
197
227
  ## 1M context
@@ -74,7 +74,7 @@ When helping a user set up qgrid locally, check for `QGRID_PROJECT_NAME` or conf
74
74
  | `QGRID_OPENAI_MAX_ESTIMATED_RSS_GIB` | `16` | Refuse scale-up when estimated qgrid worker RSS would exceed this value. Estimate: `0.71 + 0.157 * totalWorkerCount` GiB. |
75
75
  | `QGRID_OPENAI_MIN_HOST_AVAILABLE_GIB` | `20` | Refuse scale-up when current host available memory is below this value. |
76
76
  | `QGRID_OPENAI_THREAD_REUSE` | enabled | Set to `"false"` to disable OpenAI thread reuse and force cold thread behavior. |
77
- | `MAX_STRUCTURED_OUTPUT_RETRIES` | `1` for structured Anthropic calls | Claude Code structured-output retry count, clamped to at least 1 by qgrid. |
77
+ | `MAX_STRUCTURED_OUTPUT_RETRIES` | `1` for structured Anthropic streaming calls | Claude Code structured-output retry count for streaming only, clamped to at least 1 by qgrid. Non-streaming `generate` leaves the variable unset and uses Claude Code's default retry budget. |
78
78
 
79
79
  ## Server boot lifecycle
80
80
 
@@ -30,6 +30,7 @@ Key decisions:
30
30
  - qgrid's AI SDK provider follows the AI SDK `LanguageModelV3` contract. Generic usage belongs to AI SDK docs; qgrid-specific docs should focus on qgrid routing, logging, cache/session behavior, and `providerOptions.qgrid`.
31
31
  - qgrid-specific options live under `providerOptions.qgrid`, not under provider-specific namespaces such as `providerOptions.openai`. Consumers should not need to branch on Codex versus Claude internals.
32
32
  - AI SDK exposes the outer `providerOptions` as a generic JSON record. `QgridProviderOptions` intentionally types the nested `providerOptions.qgrid` value, and examples should apply `satisfies` there to restore qgrid-specific inference without replacing AI SDK's outer contract.
33
+ - AI SDK's standard `timeout` owns the client-side total budget and reaches custom providers only as an `AbortSignal`. `providerOptions.qgrid.timeoutMs` separately owns the Anthropic server-side Claude process timer; keeping the two explicit avoids pretending the original AI SDK timeout number is available inside qgrid.
33
34
  - `projectName` is provider config/payload camelCase. `project_name` is the Sonamu/database request-log column. Prefer `QGRID_PROJECT_NAME` as the project-wide default so request logs and metrics remain filterable at volume.
34
35
  - `fallbackModels` may exist as a typed future option, but it is not a server wire contract until the server implements fallback routing. Do not forward it as behavior just because the type exists.
35
36
  - Fable 5 refusal fallback is an upstream Claude Code safety path to Opus 4.8, not qgrid's reserved `fallbackModels` feature or Claude Code's overload-only `--fallback-model` flag. qgrid observes and reports the actual route and provider cost; it must not retry again.
@@ -48,7 +49,16 @@ Sources:
48
49
  Key decisions:
49
50
 
50
51
  - qgrid tool calling is intentionally implemented through structured-output emulation, not native provider tool use. The provider call returns a structured action with tool calls; the AI SDK loop executes tools and sends follow-up turns.
51
- - Tools and `jsonSchema` are mutually exclusive at the qgrid dispatcher boundary because both need the provider structured-output slot.
52
+ - Tools and `jsonSchema` share one provider structured-output slot by composing
53
+ an action envelope on every turn. Its final `answer` branch embeds the user's
54
+ schema; its `tool_call` branch carries emulated AI SDK calls. This keeps final
55
+ output constrained even when tools are available but never called.
56
+ - Final output is identified by `action: "answer"`, not by tool presence or turn
57
+ count. Structured answers are serialized as JSON text for AI SDK
58
+ `Output.object` parsing, while tools-only callers retain their legacy string
59
+ answer and tolerant parse fallback.
60
+ - AI SDK `toolChoice` remains outside qgrid's wire contract. Do not imply that
61
+ qgrid transports or enforces it.
52
62
  - Tool-call request logs are a multi-step run: create run, append generate/tool steps, then finish run. This makes AI SDK multi-step behavior visible in the dashboard instead of logging only one opaque completion.
53
63
  - Tool results update the existing `tool_call` step by `request_log_id + tool_call_id`. They are not logged as a second unrelated completion row.
54
64
  - The server infers single-turn completion versus an open tool run from context and finish reason. The removed caller-selected `logMode` contract must not be recreated.
@@ -28,7 +28,8 @@ Use this map to orient qgrid work quickly.
28
28
  - Cost/usage accounting: `packages/api/src/utils/providers/common/model-cost.ts`.
29
29
  - Weighted token selection: `packages/api/src/utils/providers/common/smooth-weighted-round-robin.ts`.
30
30
  - Boot order and startup migrations: `packages/api/src/server-bootstrap.ts`, `packages/api/src/startup-migrations.ts`.
31
- - Tool-call emulation: `packages/api/src/application/qgrid/tool-emulation.ts`.
31
+ - Tool-call schema composition: `packages/api/src/application/qgrid/tool-emulation-schema.ts`.
32
+ - Tool-call response decoding: `packages/api/src/application/qgrid/tool-emulation.ts`.
32
33
 
33
34
  ## Web/Sonamu hot spots
34
35
 
@@ -24,6 +24,11 @@ qgrid supports the AI SDK `tools` interface, but it does not use native provider
24
24
  - qgrid maps that structured object back into AI SDK `tool-call` content.
25
25
  - The AI SDK executes the actual tool functions and calls qgrid again with tool results.
26
26
 
27
+ `generateText` and `streamText` default to `stepCountIs(1)`. When an executable
28
+ tool can run before the final answer, callers must set a bounded `stopWhen`
29
+ (for example `stepCountIs(3)`) or the AI SDK stops after the first tool-call
30
+ step and cannot produce the final text or `Output.object` value.
31
+
27
32
  Do not describe this as OpenAI native function calling, Claude native tool use, or Codex/Claude executing client tools. The tools are client-side AI SDK tools requested through structured-output emulation.
28
33
 
29
34
  ## Client Flow
@@ -36,7 +41,9 @@ When `options.tools` contains function tools:
36
41
  2. Convert them with `toQgridTool`.
37
42
  3. Send `tools` in `/api/qgrid/query` or `/api/qgrid/prepareStream`.
38
43
  4. Forward the per-call `logger` switch when request logging is disabled.
39
- 5. Do not send `jsonSchema` response format at the same time. The server infers whether the logged request becomes a multi-step run.
44
+ 5. When a top-level object response format is present, send `jsonSchema` alongside
45
+ `tools`. The server composes both schemas and infers whether the logged request
46
+ becomes a multi-step run.
40
47
 
41
48
  When qgrid responds with `finishReason: "tool-calls"`:
42
49
 
@@ -57,9 +64,10 @@ The registry is keyed per pending run so concurrent `generateText(...tools...)`
57
64
 
58
65
  ## Server Schema Emulation
59
66
 
60
- `packages/api/src/application/qgrid/tool-emulation.ts` builds the tool-call schema.
67
+ `packages/api/src/application/qgrid/tool-emulation-schema.ts` builds the tool-call schema.
68
+ `tool-emulation.ts` decodes the model's action envelope into qgrid answer/tool-call content.
61
69
 
62
- The schema shape is:
70
+ The tools-only schema shape is:
63
71
 
64
72
  ```json
65
73
  {
@@ -83,7 +91,13 @@ The schema explicitly tells the model:
83
91
 
84
92
  `qgrid.dispatcher.ts` strictifies this schema through `buildStrictOutputSchema` before it reaches provider dispatchers.
85
93
 
86
- If tools and `jsonSchema` are both present, dispatcher rejects the request.
94
+ When tools and a user `jsonSchema` are both present, the dispatcher builds the
95
+ same outer envelope on every turn, but replaces `answer: string | null` with
96
+ `answer: <user schema> | null`. The user schema is namespaced under
97
+ `$defs.__qgrid_user_output`, including rebased local JSON pointers, before the
98
+ whole envelope is strictified. This prevents tool availability from disabling
99
+ the final structured-output constraint, including when the model answers without
100
+ calling a tool.
87
101
 
88
102
  ## Provider-Specific Path
89
103
 
@@ -111,9 +125,14 @@ In both providers, native provider tools are not the public abstraction. The pub
111
125
  `applyToolCallEmulation` handles provider text:
112
126
 
113
127
  - No tools: return normal text content.
114
- - Tools present and JSON parse fails: warn and fall back to text with `finishReason: "stop"`.
128
+ - Tools-only mode and JSON parse failure: preserve the legacy warning and text
129
+ fallback with `finishReason: "stop"`.
130
+ - Tools plus a user schema and malformed or incoherent envelope: return an
131
+ explicit structured-output error; do not rescue it as successful text.
115
132
  - `action: "tool_call"`: validate tool names, generate qgrid `toolCallId`, return `finishReason: "tool-calls"`.
116
- - `action: "answer"`: return final text with `finishReason: "stop"`.
133
+ - `action: "answer"` in tools-only mode: return the string answer as final text.
134
+ - `action: "answer"` with a user schema: JSON-serialize the structured `answer`
135
+ as final text so AI SDK can parse and validate `Output.object`.
117
136
 
118
137
  Unknown tool names throw.
119
138
 
@@ -128,6 +147,14 @@ AI SDK multi-step behavior is client-orchestrated:
128
147
 
129
148
  This is different from a provider-native tool loop. Neither Codex nor Claude Code executes the AI SDK tool functions.
130
149
 
150
+ The composed schema is reapplied to tool-result follow-up turns. Final output is
151
+ identified by `action: "answer"` rather than by whether a tool happened to be
152
+ available or called. AI SDK `toolChoice` is not transported or enforced by
153
+ qgrid; the model selects between the envelope's answer and tool-call actions.
154
+
155
+ The combined contract requires qgrid server 2.5.4 and
156
+ `@cartanova/qgrid-ai-sdk` 2.5.4.
157
+
131
158
  OpenAI/Codex follow-ups can reuse the same Codex thread when `runContext.threadCoord` remains valid. For tool-result follow-up turns, qgrid sends delta input containing tool result text to the existing thread; cold fallback injects full history.
132
159
 
133
160
  Anthropic follow-ups do not reuse Claude sessions. qgrid starts a fresh Claude process, replays flattened full history, and sends the tool-result continuation input.
@@ -30,6 +30,7 @@ AI SDK package `@cartanova/qgrid-ai-sdk`:
30
30
  - `pnpm --filter @cartanova/qgrid-ai-sdk test`.
31
31
  - `pnpm --filter @cartanova/qgrid-ai-sdk build`.
32
32
  - `pnpm --filter @cartanova/qgrid-ai-sdk e2e`.
33
+ - `pnpm --filter @cartanova/qgrid-ai-sdk e2e:tools-output`.
33
34
  - `pnpm --filter @cartanova/qgrid-ai-sdk e2e:logger`.
34
35
 
35
36
  CLI package `@cartanova/qgrid-cli`:
@@ -54,7 +55,7 @@ Choose by affected area:
54
55
  | AI SDK logger integration | `packages/ai-sdk/src/logger.test.ts` |
55
56
  | qgrid provider routing and strict schema | `packages/api/src/application/qgrid/qgrid.dispatcher.test.ts` |
56
57
  | conversation/thread reuse routing | `packages/api/src/application/qgrid/conv-routing.test.ts` |
57
- | tool-call emulation | `packages/api/src/application/qgrid/tool-emulation.test.ts` |
58
+ | tool-call emulation | `packages/api/src/application/qgrid/tool-emulation.test.ts`, `tool-emulation-schema.test.ts` |
58
59
  | request-log run lifecycle | `packages/api/src/application/qgrid/qgrid-run-lifecycle.test.ts` |
59
60
  | qgrid frame API behavior | `packages/api/src/application/qgrid/qgrid.frame.test.ts` |
60
61
  | token subscriber and LISTEN/NOTIFY handling | `packages/api/src/application/qgrid/token-subscriber.test.ts` |
@@ -101,6 +102,20 @@ AI SDK structured output:
101
102
  - Env: `QGRID_URL`, `SMOKE_MODEL`, `SMOKE_CASES`, `SMOKE_REPEAT`.
102
103
  - Uses AI SDK 6 `streamText` with `Output.object`; useful for structured-output regressions.
103
104
 
105
+ AI SDK tools plus structured output:
106
+
107
+ - `packages/ai-sdk/e2e/tools-structured-output.ts`
108
+ - This is the public-path release acceptance matrix: `qgrid()` HTTP/SSE,
109
+ `generateText`/`streamText`, OpenAI/Anthropic, direct final answers, and one
110
+ client-tool result followed by `Output.object`.
111
+ - It consumes real provider quota and requires the explicit
112
+ `QGRID_REAL_PROVIDER_ACCEPTANCE=1` opt-in.
113
+ - Uses `QGRID_URL`, `QGRID_ACCEPTANCE_OPENAI_MODEL`, and
114
+ `QGRID_ACCEPTANCE_ANTHROPIC_MODEL`. OpenAI direct-final coverage defaults to
115
+ 10 generate plus 10 stream attempts and can be changed with
116
+ `QGRID_ACCEPTANCE_OPENAI_DIRECT_REPEATS_PER_MODE`. Request logs use the stable
117
+ project name `qgrid-ai-sdk-tools-output-acceptance`.
118
+
104
119
  Image generation:
105
120
 
106
121
  - `packages/api/scripts/smoke-test-image-generation.ts`