@cartanova/qgrid-cli 2.3.7 → 2.3.9

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 (82) hide show
  1. package/README.ko.md +131 -0
  2. package/README.md +60 -44
  3. package/bundle/dist/application/qgrid/qgrid-image-generation.js +42 -0
  4. package/bundle/dist/application/qgrid/qgrid-response-format.js +44 -0
  5. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +23 -4
  6. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +15 -6
  7. package/bundle/dist/application/qgrid/qgrid.frame.js +32 -4
  8. package/bundle/dist/application/qgrid/qgrid.types.js +36 -12
  9. package/bundle/dist/application/qgrid/tool-emulation.js +16 -8
  10. package/bundle/dist/application/request-log/request-log.model.js +5 -2
  11. package/bundle/dist/application/sonamu.generated.js +13 -4
  12. package/bundle/dist/application/sonamu.generated.sso.js +9 -3
  13. package/bundle/dist/i18n/sd.generated.js +4 -1
  14. package/bundle/dist/migrations/20260705140614_alter_request_logs_add1_alter1.js +16 -0
  15. package/bundle/dist/migrations/20260706120000_alter_request_logs_add_image_cost.js +17 -0
  16. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +2 -1
  17. package/bundle/dist/utils/providers/openai/codex-rpc.js +1 -1
  18. package/bundle/dist/utils/providers/openai/codex-worker.js +78 -6
  19. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +21 -4
  20. package/bundle/src/application/qgrid/qgrid-image-generation.ts +62 -0
  21. package/bundle/src/application/qgrid/qgrid-response-format.ts +59 -0
  22. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +105 -0
  23. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +34 -2
  24. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +33 -0
  25. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +16 -5
  26. package/bundle/src/application/qgrid/qgrid.frame.test.ts +78 -0
  27. package/bundle/src/application/qgrid/qgrid.frame.ts +48 -5
  28. package/bundle/src/application/qgrid/qgrid.types.ts +22 -0
  29. package/bundle/src/application/qgrid/tool-emulation.test.ts +44 -1
  30. package/bundle/src/application/qgrid/tool-emulation.ts +27 -4
  31. package/bundle/src/application/request-log/request-log.entity.json +73 -47
  32. package/bundle/src/application/request-log/request-log.model.ts +7 -0
  33. package/bundle/src/application/sonamu.generated.http +16 -3
  34. package/bundle/src/application/sonamu.generated.sso.ts +6 -0
  35. package/bundle/src/application/sonamu.generated.ts +12 -0
  36. package/bundle/src/i18n/sd.generated.ts +3 -0
  37. package/bundle/src/migrations/20260705140614_alter_request_logs_add1_alter1.ts +15 -0
  38. package/bundle/src/migrations/20260706120000_alter_request_logs_add_image_cost.ts +14 -0
  39. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +5 -0
  40. package/bundle/src/utils/providers/common/provider-dispatcher.ts +14 -0
  41. package/bundle/src/utils/providers/openai/codex-rpc.ts +2 -0
  42. package/bundle/src/utils/providers/openai/codex-worker.test.ts +219 -0
  43. package/bundle/src/utils/providers/openai/codex-worker.ts +154 -5
  44. package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +95 -1
  45. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +42 -0
  46. package/bundle/web-dist/client/assets/index-Cj1iWi5i.js +78 -0
  47. package/bundle/web-dist/client/assets/index-VE_24EJf.css +1 -0
  48. package/bundle/web-dist/client/assets/{logs-B0pWpDrg.js → logs-DvGWhZTu.js} +1 -1
  49. package/bundle/web-dist/client/assets/{routes-7XhVuG7o.js → routes-B_xvkM-J.js} +1 -1
  50. package/bundle/web-dist/client/assets/{sd.generated-DXhjjL2f.js → sd.generated-BrUnRt0B.js} +1 -1
  51. package/bundle/web-dist/client/assets/{services.generated-B7u0U1wC.js → services.generated-AsEvHbJ7.js} +1 -1
  52. package/bundle/web-dist/client/assets/{show-CcjZ9nr7.js → show-C6W3ywZz.js} +3 -3
  53. package/bundle/web-dist/client/assets/{tokens-DlwAm0v4.js → tokens-CxjeN72g.js} +1 -1
  54. package/bundle/web-dist/client/index.html +3 -3
  55. package/bundle/web-dist/server/assets/{lazyRouteComponent-B0LkbiYf.js → lazyRouteComponent-BswulcOx.js} +1 -1
  56. package/bundle/web-dist/server/assets/{logs-dLx6gMXT.js → logs-B_ZLzpwY.js} +4 -4
  57. package/bundle/web-dist/server/assets/{routes-DD3Hb9xO.js → routes-D9OiKoqa.js} +2 -2
  58. package/bundle/web-dist/server/assets/{sd.generated-DdfpD_iN.js → sd.generated-DRuQSIKo.js} +3 -0
  59. package/bundle/web-dist/server/assets/{services.generated-D3ZCvIkt.js → services.generated-Dp0uKgpV.js} +1 -1
  60. package/bundle/web-dist/server/assets/{show-BQjeyv6A.js → show-CGlX9OYt.js} +104 -13
  61. package/bundle/web-dist/server/assets/{tokens-CSOsBpP7.js → tokens-Cjw6cQxZ.js} +2 -2
  62. package/bundle/web-dist/server/entry-server.generated.js +9 -8
  63. package/package.json +7 -3
  64. package/postinstall.mjs +119 -0
  65. package/skills/qgrid/SKILL.md +54 -0
  66. package/skills/qgrid/agents/openai.yaml +4 -0
  67. package/skills/qgrid/references/ai-sdk-provider-contract.md +100 -0
  68. package/skills/qgrid/references/anthropic-claude-code-runtime.md +170 -0
  69. package/skills/qgrid/references/cli-env-and-server-boot.md +66 -0
  70. package/skills/qgrid/references/decision-rationale.md +179 -0
  71. package/skills/qgrid/references/docs-workflow.md +32 -0
  72. package/skills/qgrid/references/openai-codex-runtime.md +153 -0
  73. package/skills/qgrid/references/prompt-cache-and-usage.md +101 -0
  74. package/skills/qgrid/references/provider-runtime-differences.md +54 -0
  75. package/skills/qgrid/references/repo-map.md +37 -0
  76. package/skills/qgrid/references/request-log-run-lifecycle.md +76 -0
  77. package/skills/qgrid/references/sonamu-api-web-flow.md +47 -0
  78. package/skills/qgrid/references/token-auth-quota-lifecycle.md +205 -0
  79. package/skills/qgrid/references/tool-calling-and-multiturn.md +144 -0
  80. package/skills/qgrid/references/verification-and-debugging.md +193 -0
  81. package/bundle/web-dist/client/assets/index-5WdFEqZT.css +0 -1
  82. package/bundle/web-dist/client/assets/index-jnUY7nJ-.js +0 -78
@@ -0,0 +1,100 @@
1
+ # AI SDK Provider Contract
2
+
3
+ Use this reference before changing `packages/ai-sdk`.
4
+
5
+ ## Active public surface
6
+
7
+ `packages/ai-sdk` is the active public SDK package. `packages/sdk` is deprecated and context-only.
8
+
9
+ qgrid's custom provider implements the AI SDK `LanguageModelV3` contract. General usage should follow the AI SDK docs and normal AI SDK APIs such as `generateText`, `streamText`, tools, structured output, and telemetry. Do not re-document generic AI SDK usage in qgrid-specific docs unless the behavior differs. qgrid docs and this skill should focus on qgrid-specific config, `providerOptions.qgrid`, request logging, runtime routing, cache/session behavior, and provider limitations.
10
+
11
+ For tool calling, qgrid intentionally follows the AI SDK interface while implementing provider calls through qgrid structured-output emulation. Read `tool-calling-and-multiturn.md` before changing tool behavior, `stopWhen`/multi-step behavior, or tool-call request logs.
12
+
13
+ Main files:
14
+
15
+ - `packages/ai-sdk/src/index.ts`: `qgrid()` provider implementation.
16
+ - `packages/ai-sdk/src/index.types.ts`: public config/options/types.
17
+ - `packages/ai-sdk/src/utils.ts`: prompt/history/tool conversion.
18
+ - `packages/ai-sdk/src/logger.ts`: telemetry logger integration.
19
+ - `packages/ai-sdk/src/qgrid.constant.ts`: defaults and warnings.
20
+
21
+ ## Provider config
22
+
23
+ `qgrid(modelId, config)` supports:
24
+
25
+ - `serverUrl`: default `process.env.QGRID_URL`, then `http://localhost:44900`.
26
+ - `defaultEffort`: default `low`.
27
+ - `projectName`: default `process.env.QGRID_PROJECT_NAME`.
28
+
29
+ Strongly recommend setting `QGRID_PROJECT_NAME` in the environment for real workloads. The config-level `projectName` override is valid, but the env var is the preferred project-wide default. It is stored as `request_logs.project_name` and makes high-volume request logs usable: operators can filter by project/workflow, identify which task produced which request, compare token/cache/cost/TTFT metrics by workload, and debug noisy callers without scanning prompts manually.
30
+
31
+ Use camelCase `projectName` in TypeScript and qgrid API payloads. `project_name` is the database/Sonamu request-log field name, not an AI SDK provider option.
32
+
33
+ Setup agents should treat a missing project label as an actionable configuration gap. When installing or wiring qgrid for a local app, inspect the target app's env/config. If neither `QGRID_PROJECT_NAME` nor qgrid provider/logger config `projectName` is present, ask the user for a stable project or workflow name, then add it using the project's normal env/config pattern. A repo or package name is an acceptable default only when it is clearly the user's intended workload label.
34
+
35
+ ## Provider options
36
+
37
+ qgrid-specific behavior is controlled through `providerOptions.qgrid`. Explain these options precisely because they are qgrid's extension point beyond normal AI SDK usage.
38
+
39
+ `providerOptions.qgrid` supports:
40
+
41
+ - `sessionKey`: OpenAI thread reuse key. Disabled for Anthropic models.
42
+ - `effort`
43
+ - `verbosity`: OpenAI/Codex route only.
44
+ - `reasoningSummary`: OpenAI/Codex route only.
45
+ - `serviceTier`: OpenAI/Codex route only.
46
+ - `fallbackModels`: reserved for future fallback routing.
47
+ - `imageGeneration`: OpenAI/Codex non-stream only. Enables Codex's built-in `image_generation` tool for that request.
48
+ - `imageGenerationOptions`: optional image quality/size hints and cost-estimation basis. Current supported values are `quality: "low" | "medium" | "high"` and `size: "1024x1024" | "1024x1536" | "1536x1024"`.
49
+
50
+ `providerOptions.qgrid` does not currently support `projectName` or `project_name`. Prefer `QGRID_PROJECT_NAME` for the default project label; use config `projectName` only when a caller needs to override that default.
51
+
52
+ ## Request construction
53
+
54
+ The provider sends `POST /api/qgrid/query` for generate and `prepareStream` plus SSE for stream.
55
+
56
+ Payload responsibilities:
57
+
58
+ - Extract current prompt, system prompt, and history from AI SDK prompt messages.
59
+ - Convert AI SDK function tools to qgrid tools.
60
+ - Send `jsonSchema` only when no tools are present and response format top-level schema is `object`.
61
+ - Send `history` as JSON string when prior messages exist.
62
+ - Send `projectName` when configured.
63
+ - Use `logMode: "run"` for tool-call loops.
64
+ - Preserve and resend `runContext` for tool-call follow-ups.
65
+ - Store/resend OpenAI `threadCoord` by `sessionKey`.
66
+ - Send `imageGeneration` and `imageGenerationOptions` when configured.
67
+
68
+ Tools and `jsonSchema` cannot be used together at qgrid dispatcher level.
69
+
70
+ When tools are present, qgrid sends tool definitions to the server as `tools`; it does not send them to OpenAI or Anthropic as native provider tools. The server converts them into a strict structured-output schema and maps the model's structured result back into AI SDK `tool-call` content.
71
+
72
+ Image generation is different from AI SDK client tools: it is an opt-in Codex-hosted tool exposed by qgrid through `providerOptions.qgrid.imageGeneration`. Use `generateText`; `streamText` rejects image generation before opening the stream.
73
+
74
+ ## Response mapping
75
+
76
+ qgrid response content maps to AI SDK content:
77
+
78
+ - qgrid `text` -> AI SDK text content.
79
+ - qgrid `tool-call` -> AI SDK tool-call content.
80
+ - qgrid `image` -> AI SDK file content with `mediaType: "image/png"`.
81
+
82
+ `finishReason` maps `tool-calls` when qgrid returns tool calls.
83
+
84
+ When `imageGeneration` was requested and the server returns no image part, the AI SDK provider throws a version-skew/error guard instead of silently accepting text-only output.
85
+
86
+ Usage maps qgrid standard usage into AI SDK V3 usage:
87
+
88
+ - `inputTokens.total = input_tokens`
89
+ - `inputTokens.cacheRead = cache_read_input_tokens`
90
+ - `inputTokens.cacheWrite = cache_creation_input_tokens`
91
+ - `inputTokens.noCache = max(input - cacheRead - cacheWrite, 0)`
92
+ - `outputTokens.total = output_tokens`
93
+
94
+ ## Anthropic sessionKey guard
95
+
96
+ Do not store or replay `sessionKey` thread coordinates for `anthropic/*` models. Tests cover this because Anthropic fresh-spawn runtime cannot safely use Codex-style thread reuse.
97
+
98
+ ## Logger integration
99
+
100
+ `createQgridLogger` records external provider calls into qgrid request logs. It skips qgrid provider calls to avoid double-logging. When changing logger behavior, preserve stale-run fallback because AI SDK telemetry lacks a reliable error hook for all failure modes.
@@ -0,0 +1,170 @@
1
+ # Anthropic Claude Code Runtime
2
+
3
+ Use this reference before changing Anthropic provider behavior, Claude Code spawn args/env, stream-json adaptation, structured output, 1M context behavior, token routing, OAuth refresh, or quota handling.
4
+
5
+ ## Contents
6
+
7
+ - Process model
8
+ - Token routing
9
+ - OAuth refresh
10
+ - Spawn args
11
+ - Spawn env
12
+ - Config isolation
13
+ - Input adaptation
14
+ - Output adaptation
15
+ - Structured output
16
+ - 1M context
17
+
18
+ ## Process model
19
+
20
+ Anthropic uses fresh Claude Code process spawn per request. It does not use a persistent worker pool.
21
+
22
+ - Dispatcher: `packages/api/src/utils/providers/anthropic/anthropic-dispatcher.ts`.
23
+ - Session runner: `packages/api/src/utils/providers/anthropic/claude-session.ts`.
24
+ - Stream adapter: `packages/api/src/utils/providers/anthropic/stream-json-adapter.ts`.
25
+ - Constants/model normalization: `packages/api/src/utils/providers/anthropic/anthropic-constants.ts`.
26
+ - Process command: `claude -p ...`.
27
+ - Each request gets a fresh UUID `--session-id`.
28
+ - `workerId` is `tokenId`.
29
+ - `epoch` is always `0`.
30
+
31
+ The emitted `threadCoord` lets qgrid keep a shared response shape, but AI SDK disables `sessionKey` storage/replay for `anthropic/*` models. Do not implement Anthropic cache by pretending Claude sessions are reused unless the runtime design changes.
32
+
33
+ ## Token routing
34
+
35
+ Anthropic tokens live in an in-memory `Map<tokenId, PooledToken>`.
36
+
37
+ - Startup loads active Anthropic tokens from DB.
38
+ - Token subscriber events add/update/remove entries.
39
+ - Periodic reconcile replaces pool contents from active DB rows.
40
+ - Request selection is least-used plus round-robin tie-break.
41
+ - Request count is incremented before awaiting execution so concurrent requests spread across tokens.
42
+
43
+ Quota threshold:
44
+
45
+ - `quota_threshold` is checked through Anthropic quota usage.
46
+ - Lookup failure is fail-open.
47
+ - If all eligible tokens exceed threshold, throw `QuotaThresholdExceededError`.
48
+
49
+ ## OAuth refresh
50
+
51
+ Before spawning Claude, qgrid refreshes access tokens when expiration is within 60 seconds and a refresh token exists.
52
+
53
+ Refresh is attempted through `QgridFrame.refreshToken` with provider set to `anthropic`. Refresh failure is logged and the current access token is still tried.
54
+
55
+ ## Spawn args
56
+
57
+ `buildClaudeArgs` constructs Claude Code flags:
58
+
59
+ ```text
60
+ claude -p
61
+ --tools ""
62
+ --allowed-tools StructuredOutput # only when jsonSchema exists
63
+ --disallowedTools Monitor PushNotification RemoteTrigger
64
+ --input-format stream-json
65
+ --output-format stream-json
66
+ --verbose
67
+ --include-partial-messages # streaming path only
68
+ --permission-mode bypassPermissions
69
+ --setting-sources project
70
+ --model <canonical-model-or-1m-suffix>
71
+ --system-prompt <text> # small system prompt
72
+ --system-prompt-file <path> # large system prompt
73
+ --thinking disabled
74
+ --effort <effort-or-low>
75
+ --disable-slash-commands
76
+ --session-id <uuid>
77
+ --json-schema <schema> # only when jsonSchema exists
78
+ ```
79
+
80
+ Important details:
81
+
82
+ - `--tools ""` blocks normal tools. With structured output, only `StructuredOutput` is allowed.
83
+ - `--setting-sources project` plus seeded settings isolates user configuration.
84
+ - `--system-prompt` or `--system-prompt-file` is always supplied. Omitting it would allow Claude Code default system prompt injection.
85
+ - Large system prompts over 64 KiB are written to a temporary file to avoid argv `E2BIG`.
86
+ - `--thinking disabled`, `MAX_THINKING_TOKENS=0`, and adaptive thinking env suppression keep thinking off.
87
+
88
+ ## Spawn env
89
+
90
+ qgrid builds a fresh env whitelist. Do not spread `process.env` into Claude child env.
91
+
92
+ Included env:
93
+
94
+ - `PATH`
95
+ - `TMPDIR`
96
+ - `CLAUDE_CODE_OAUTH_TOKEN`
97
+ - `CLAUDE_CONFIG_DIR`
98
+ - `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1`
99
+ - `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1`
100
+ - `CLAUDE_CODE_DISABLE_CLAUDE_MDS=1`
101
+ - `CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1`
102
+ - `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS=1`
103
+ - `CLAUDE_CODE_DISABLE_WORKFLOWS=1`
104
+ - `CLAUDE_CODE_ATTRIBUTION_HEADER=0`
105
+ - `MAX_THINKING_TOKENS=0`
106
+ - `CLAUDE_CODE_DISABLE_1M_CONTEXT=1` when model does not support qgrid's 1M path
107
+ - `MAX_STRUCTURED_OUTPUT_RETRIES` for structured output only
108
+
109
+ Never pass `ANTHROPIC_API_KEY` or inherited auth env vars to child processes. OAuth must flow through `CLAUDE_CODE_OAUTH_TOKEN`.
110
+
111
+ ## Config isolation
112
+
113
+ - Shared cwd: `/tmp/qgrid-anthropic`.
114
+ - Project settings file: `/tmp/qgrid-anthropic/.claude/settings.json`, written as `{}`.
115
+ - Per-token config dir: `/tmp/qgrid-anthropic-config/${tokenId}`.
116
+ - qgrid writes `{}` to both `.claude.json` and `settings.json` inside the per-token config dir before each run.
117
+
118
+ This blocks user-scope Claude settings, hooks, memory, and token cross-contamination as much as the current Claude Code interface allows.
119
+
120
+ ## Input adaptation
121
+
122
+ qgrid sends Claude stdin as JSONL with `--input-format stream-json`.
123
+
124
+ - Current input is the only executable user line.
125
+ - Prior conversation history is flattened into a single assistant context line prefixed with `Prior conversation context:`.
126
+ - Function calls and function outputs from AI SDK history are flattened into text.
127
+ - Each JSONL line is decorated with `session_id`, `uuid`, and `parent_tool_use_id: null`.
128
+
129
+ This is not equivalent to persistent Claude session reuse. It is full-history replay through a fresh process.
130
+
131
+ ## Output adaptation
132
+
133
+ Claude stdout JSONL is parsed by `stream-json-adapter.ts`.
134
+
135
+ - Text mode streams `text_delta`.
136
+ - Structured mode streams `input_json_delta` and preserves `StructuredOutput` tool input as final text.
137
+ - `result` lines provide final text, usage, duration, cost, subtype, and terminal reason.
138
+ - Non-success subtype, `is_error`, or `terminal_reason: model_error` is treated as an error.
139
+ - Quota exhaustion is detected when text starts with `You've hit`.
140
+
141
+ Anthropic native usage categories are mutually exclusive. qgrid normalizes them into its provider-standard shape by setting:
142
+
143
+ ```text
144
+ inputTokens = input_tokens + cache_creation_input_tokens + cache_read_input_tokens
145
+ cachedInputTokens = cache_read_input_tokens
146
+ cacheCreationInputTokens = cache_creation_input_tokens
147
+ ```
148
+
149
+ ## Structured output
150
+
151
+ qgrid strictifies schemas before provider dispatch. Claude Code `--json-schema` is not grammar-constrained decoding; it is a `StructuredOutput` tool plus post-validation behavior. Keep schemas strict and preserve error signaling for failed structured-output attempts.
152
+
153
+ `MAX_STRUCTURED_OUTPUT_RETRIES` defaults to `1` for structured Anthropic calls and is clamped to at least 1. Avoid setting it to 0; Claude Code can fail before emitting a useful attempt.
154
+
155
+ ## 1M context
156
+
157
+ Model normalization strips provider prefix and `[1m]` for canonical model/cost keys.
158
+
159
+ qgrid's measured 1M support set currently includes:
160
+
161
+ - `claude-sonnet-4-6`
162
+ - `claude-opus-4-6`
163
+ - `claude-opus-4-8`
164
+
165
+ Models requiring CLI `[1m]` suffix:
166
+
167
+ - `claude-sonnet-4-6`
168
+ - `claude-opus-4-6`
169
+
170
+ Unsupported `[1m]` suffixes throw early.
@@ -0,0 +1,66 @@
1
+ # CLI, Env, And Server Boot
2
+
3
+ Use this reference for qgrid startup, configuration, and runtime-facing environment variables.
4
+
5
+ ## CLI options
6
+
7
+ - `--db <url>`: PostgreSQL URL in `postgres://user:password@host:port/dbname` or `postgresql://...` form. Parsed by `packages/cli/src/cli.ts` and copied into `QGRID_DB_*` env vars before server boot.
8
+ - `-p, --port <port>`: server port. Default `44900`. The CLI validates the port and refuses to start if it is already in use.
9
+ - `--skip-update`: skip qgrid CLI self-update check.
10
+
11
+ The CLI also ensures runtime CLIs are installed/current enough:
12
+
13
+ - `codex` from `@openai/codex`, required for OpenAI tokens.
14
+ - `claude` from `@anthropic-ai/claude-code`, required for Anthropic tokens.
15
+
16
+ ## Server env vars agents should know
17
+
18
+ | Env | Default | Meaning |
19
+ |---|---:|---|
20
+ | `QGRID_DB_HOST` | `localhost` | PostgreSQL host. |
21
+ | `QGRID_DB_PORT` | `5432` | PostgreSQL port. |
22
+ | `QGRID_DB_USER` | `postgres` | PostgreSQL user. |
23
+ | `QGRID_DB_PASSWORD` | `postgres` | PostgreSQL password. |
24
+ | `QGRID_DB_NAME` | `qgrid` | PostgreSQL database. |
25
+ | `HOST` | `localhost` | Sonamu server listen host when running API directly. |
26
+ | `PORT` | `44900` | Sonamu server listen port. In CLI mode this is derived from `--port`, not read as user input. |
27
+ | `QGRID_PUBLIC_BASE_URL` | empty | Public base URL used to construct Anthropic OAuth callback URL. If unset, callback defaults to `http://localhost:${PORT}/callback`. |
28
+ | `PROJECT_NAME` | `Qgrid` | Sonamu project name. Not the same as request log `projectName`. |
29
+
30
+ Do not treat CLI bundle bootstrapping internals as user-facing qgrid configuration.
31
+
32
+ ## SDK/client env vars
33
+
34
+ | Env | Default | Meaning |
35
+ |---|---:|---|
36
+ | `QGRID_URL` | `http://localhost:44900` | Server URL used by `@cartanova/qgrid-ai-sdk` and logger integration. |
37
+ | `QGRID_PROJECT_NAME` | empty | Request log project name sent by the AI SDK provider/logger unless overridden in config. Configure this in the calling app or agent runtime environment, not only in the qgrid server process. |
38
+
39
+ When helping a user set up qgrid locally, check for `QGRID_PROJECT_NAME` or config `projectName`. If absent, ask for a stable project/workflow label or add the obvious app/repo name when the setup context makes it unambiguous. This keeps request logs, metrics, and dashboard filters usable once traffic volume grows.
40
+
41
+ ## Provider runtime knobs
42
+
43
+ | Env | Default | Meaning |
44
+ |---|---:|---|
45
+ | `QGRID_WORKERS_PER_TOKEN` | `3` | OpenAI Codex workers per token. Capped at 5 in code. |
46
+ | `QGRID_OPENAI_THREAD_REUSE` | enabled | Set to `"false"` to disable OpenAI thread reuse and force cold thread behavior. |
47
+ | `MAX_STRUCTURED_OUTPUT_RETRIES` | `1` for structured Anthropic calls | Claude Code structured-output retry count, clamped to at least 1 by qgrid. |
48
+
49
+ ## Server boot lifecycle
50
+
51
+ On server start, `packages/api/src/sonamu.config.ts`:
52
+
53
+ 1. Loads `.env` from `packages/api/.env` when running API directly.
54
+ 2. Configures Sonamu database connection from `QGRID_DB_*`.
55
+ 3. Runs latest migrations from `packages/api/src/migrations`.
56
+ 4. Ensures PostgreSQL `tokens_changed` triggers.
57
+ 5. Starts `TokenSubscriber` for LISTEN/NOTIFY plus periodic reconcile.
58
+ 6. Starts `OpenAIDispatcher` and `AnthropicDispatcher`.
59
+ 7. Logs server URL and provider readiness counts.
60
+
61
+ On shutdown, it stops provider dispatchers and the token subscriber.
62
+
63
+ ## OAuth callbacks
64
+
65
+ - Anthropic OAuth uses `/callback` on the qgrid server. The callback URL is based on `QGRID_PUBLIC_BASE_URL` when set, otherwise localhost and `PORT`.
66
+ - OpenAI OAuth is handled by Codex app-server's own callback flow and completed through qgrid's OpenAI OAuth APIs.
@@ -0,0 +1,179 @@
1
+ # Decision Rationale
2
+
3
+ Use this reference when the question is "why is qgrid designed this way?" or before changing an established runtime contract. Current code is the source of truth for behavior; repo docs are the source of truth for the decision trail.
4
+
5
+ Do not copy these notes blindly into implementation. Use them to find the relevant docs, then inspect the current code and tests.
6
+
7
+ ## Contents
8
+
9
+ - AI SDK provider boundary
10
+ - Tool calling and request-log lifecycle
11
+ - OpenAI/Codex runtime
12
+ - Anthropic/Claude Code runtime
13
+ - Token sync and quota thresholds
14
+ - Request logs, metrics, and dashboard
15
+ - CLI packaging and server boot
16
+ - Image generation
17
+
18
+ ## AI SDK Provider Boundary
19
+
20
+ Sources:
21
+
22
+ - `docs/plans/2026-05-07-feat-qgrid-ai-sdk-provider-plan.md`
23
+ - `docs/solutions/conventions/qgrid-provider-options-boundary.md`
24
+ - `docs/plans/2026-05-26-refactor-ai-sdk-wrapper-correlation-boundary-plan.md`
25
+
26
+ Key decisions:
27
+
28
+ - `packages/ai-sdk` is the active public SDK. `packages/sdk` is deprecated and should be read only for legacy context.
29
+ - 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`.
30
+ - 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.
31
+ - `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.
32
+ - `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.
33
+ - The SDK should stay a thin adapter. Server APIs own request-log lifecycle, provider dispatch, structured-output emulation, and provider runtime details.
34
+
35
+ ## Tool Calling And Request-Log Lifecycle
36
+
37
+ Sources:
38
+
39
+ - `docs/plans/2026-05-07-feat-qgrid-ai-sdk-provider-plan.md`
40
+ - `docs/plans/2026-05-20-feat-request-log-run-lifecycle-api-plan.md`
41
+ - `docs/plans/2026-05-26-refactor-ai-sdk-wrapper-correlation-boundary-plan.md`
42
+ - `docs/solutions/tooling-decisions/anthropic-structured-stream-keep-required-and-fail-honestly.md`
43
+
44
+ Key decisions:
45
+
46
+ - 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.
47
+ - Tools and `jsonSchema` are mutually exclusive at the qgrid dispatcher boundary because both need the provider structured-output slot.
48
+ - 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.
49
+ - 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.
50
+ - `runContext.requestLogId` is intentionally direct. qgrid SDK and server are in the same product boundary, so an opaque indirection was not worth the complexity.
51
+ - Lifecycle endpoints remain public because `createQgridLogger` records non-qgrid AI SDK calls into qgrid logs without forcing those calls through the qgrid provider.
52
+ - Structured-output failures should fail honestly. Especially on Claude Code, partial or invalid structured output must not be rescued into a fake success.
53
+
54
+ ## OpenAI/Codex Runtime
55
+
56
+ Sources:
57
+
58
+ - `docs/plans/2026-05-18-feat-codex-app-server-backend-pivot-plan.md`
59
+ - `docs/plans/2026-05-30-feat-codex-builtin-tool-suppression-plan.md`
60
+ - `docs/plans/2026-06-06-feat-codex-thread-reuse-prompt-cache-plan.md`
61
+ - `docs/solutions/logic-errors/qgrid-prompt-cache-hit-rate-metric-miscalculation.md`
62
+
63
+ Key decisions:
64
+
65
+ - OpenAI uses Codex `app-server` directly over stdio, not `codex exec`. The app-server path gives qgrid OAuth login, persistent workers, and thread handles.
66
+ - Workers are persistent and token-scoped. qgrid can spawn multiple workers per token, but each worker is single-flight for turns. This differs from Anthropic, which fresh-spawns per request.
67
+ - qgrid unsets normal OpenAI API-key paths and logs in with ChatGPT/OAuth credentials. This keeps the runtime aligned with subscription-token pooling rather than API billing.
68
+ - Built-in Codex tools, apps, skills, web search, shell, and large instruction blocks are suppressed. The primary reason is tool-call correctness: Codex built-ins can steal calls that qgrid expects to represent as emulated AI SDK tool calls. Token savings are secondary.
69
+ - Thread reuse exists because Codex/OpenAI prompt cache keys are tied to the Codex conversation/thread. New `thread/start` calls miss cache even with identical prefixes.
70
+ - Reuse must be explicit through a server-issued coordinate such as `threadCoord`. Do not use content hashes or hidden closures; same-looking prompts from different sessions can cross-contaminate.
71
+ - Reuse fallback should be cold and correct. Invalid worker id, epoch mismatch, missing thread, busy worker timeout, or over-threshold token should not corrupt a conversation.
72
+ - Cache and usage metrics use per-turn usage. For Codex, `tokenUsage.total` is conversation cumulative; request logs must use `tokenUsage.last`.
73
+ - OpenAI/Codex cached tokens are included in `input_tokens`. Cache-hit denominator is `input_tokens`, not `input + cache_read + cache_creation`.
74
+
75
+ ## Anthropic/Claude Code Runtime
76
+
77
+ Sources:
78
+
79
+ - `docs/brainstorms/2026-06-16-anthropic-provider-revival-problem-definition.md`
80
+ - `docs/plans/2026-06-24-001-refactor-anthropic-cold-only-routing-plan.md`
81
+ - `docs/solutions/integration-issues/claude-cli-drops-assistant-role-in-stream-json.md`
82
+ - `docs/solutions/tooling-decisions/anthropic-structured-stream-keep-required-and-fail-honestly.md`
83
+ - `docs/plans/2026-06-23-001-feat-anthropic-1m-context-plan.md`
84
+ - `docs/solutions/security-issues/claude-cli-anthropic-api-key-leak-audit.md`
85
+ - `docs/solutions/security-issues/qgrid-cross-user-context-leak-via-claude-settings-sources.md`
86
+ - `docs/solutions/integration-issues/claude-code-oauth-tokens-share-prompt-cache.md`
87
+
88
+ Key decisions:
89
+
90
+ - Anthropic is an internal Claude subscription OAuth path. The API-key provider was not pursued because it does not serve qgrid's subscription-pooling value.
91
+ - Anthropic is cold-only. It does not use Codex-style thread reuse because Claude Code prompt cache is prefix-based and AI SDK consumers already send full message history for each step.
92
+ - The shared `threadCoord` response shape may still appear for provider symmetry, but AI SDK storage/replay is disabled for `anthropic/*` models.
93
+ - Claude Code `stream-json` drops structural assistant role replay in the outbound API payload. qgrid flattens prior history into text because structural role replay through the CLI is not reliable.
94
+ - Claude Code `--json-schema` is not constrained native tool calling. It creates a `StructuredOutput` tool and validates after model generation. Keep schemas strict and fail on non-success subtypes or retry exhaustion.
95
+ - `MAX_STRUCTURED_OUTPUT_RETRIES=1` means one attempt. Do not set it to 0 expecting "no retry but one attempt"; that can mean no useful attempt.
96
+ - 1M context support is measured per model and is a mitigation, not a guarantee. It reduces compaction/hang frequency for huge prompts, but does not make every structured prompt reliable.
97
+ - Spawn env must be whitelisted. Never pass inherited `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `ANTHROPIC_BASE_URL` unless intentionally building an API-key/proxy path.
98
+ - `--setting-sources project` is required for shared-service isolation. User/local Claude settings can load hooks or memory and leak context across requests.
99
+ - Claude Code OAuth prompt cache sharing across tokens in the same workspace is an observed cost behavior, not a product contract. Monitor it; do not promise it.
100
+
101
+ ## Token Sync And Quota Thresholds
102
+
103
+ Sources:
104
+
105
+ - `docs/plans/2026-05-02-feat-token-sync-via-pg-listen-notify-plan.md`
106
+ - `docs/brainstorms/2026-06-30-token-quota-threshold-requirements.md`
107
+ - `docs/plans/2026-06-30-001-feat-token-quota-threshold-plan.md`
108
+
109
+ Key decisions:
110
+
111
+ - Token changes propagate with PostgreSQL `LISTEN/NOTIFY` plus periodic reconcile. Reconcile is kept because notifications can be missed during disconnects.
112
+ - Trigger setup is boot-time idempotent SQL, not a raw migration file, because the Sonamu migration directory is managed and raw files can conflict.
113
+ - Trigger payloads stay small (`op`, `id`). Dispatchers reload token rows instead of trusting a large notification body.
114
+ - The token subscriber disables statement/idle timeouts for `LISTEN` and uses reconnect backoff.
115
+ - `quota_threshold` is a routing guardrail, not a load balancer. It prevents an individual token from crossing a configured utilization percentage; it does not equalize traffic.
116
+ - Threshold values are nullable integers from 1 to 100. `0` is not valid; `100` means exclude only at full utilization.
117
+ - Provider criteria differ by primary window: Anthropic uses `five_hour.utilization`; OpenAI uses `primary.usedPercent`.
118
+ - Threshold gates are by token id, not token name. Names are display/logging labels and can change.
119
+ - Lookup failure is fail-open with logs/metrics. Hard failure happens only when usage was read successfully and the token is over threshold.
120
+ - OpenAI needs the gate in reuse, idle selection, and queue drain paths. A reusable but over-threshold worker must fall back cold to another eligible token when possible.
121
+
122
+ ## Request Logs, Metrics, And Dashboard
123
+
124
+ Sources:
125
+
126
+ - `docs/plans/2026-05-21-feat-qgrid-logger-telemetry-integration-plan.md`
127
+ - `docs/brainstorms/2026-07-01-request-log-ttft-metric-requirements.md`
128
+ - `docs/solutions/logic-errors/qgrid-prompt-cache-hit-rate-metric-miscalculation.md`
129
+ - `docs/solutions/performance-issues/sonamu-findmany-aggregate-slowdown.md`
130
+ - `docs/solutions/performance-issues/qgrid-request-logs-subset-and-distinct-query-optimization.md`
131
+
132
+ Key decisions:
133
+
134
+ - The dashboard/logging layer is a core qgrid value, not an incidental web UI. It lets operators see cost, cache, TTFT, token routing, tool steps, and project-level workloads.
135
+ - `createQgridLogger` exists so teams can keep using native AI SDK providers while still recording those calls in qgrid request logs. It should not throw into user code.
136
+ - Request-log TTFT is the first generate-step TTFT. It intentionally measures generation responsiveness, not queue time or full request latency.
137
+ - Cache-hit metrics must be derived consistently from normalized provider accounting. Keep metric logic centralized when possible.
138
+ - Request-log list queries should avoid large text/blob columns unless the UI needs them. The list view is for scanning, not payload archival.
139
+ - Aggregate endpoints can intentionally duplicate filters in raw SQL instead of reusing `findMany`. Materializing full rows for aggregates was measured as too slow.
140
+ - Project-name filters matter at scale. Encourage `QGRID_PROJECT_NAME` so dashboards can distinguish workloads without inspecting prompts.
141
+ - Setup agents are part of the intended local qgrid workflow. They should ask for or add a project label during setup because retroactively untangling anonymous request logs is expensive once multiple projects or workflows share a qgrid server.
142
+
143
+ ## CLI Packaging And Server Boot
144
+
145
+ Sources:
146
+
147
+ - `docs/plans/2026-04-06-feat-package-split-sdk-cli-plan.md`
148
+ - `docs/plans/2026-04-13-refactor-cli-bundle-rollback-plan.md`
149
+ - `references/cli-env-and-server-boot.md`
150
+
151
+ Key decisions:
152
+
153
+ - CLI and SDK were split so AI consumers do not install Sonamu, database, and runtime-server dependencies just to call qgrid.
154
+ - The CLI is a direct Node/bundle runner, not a Docker wrapper. Docker was removed to keep `npm i -g ...` plus `qgrid start` usable without local Docker setup.
155
+ - CLI user-facing configuration is intentionally small: database connection, port, public callback URL, server URL, and project name. Bundle bootstrapping details are implementation internals.
156
+ - The CLI checks runtime CLIs (`codex`, `claude`) because qgrid delegates provider execution to those local binaries.
157
+ - The dashboard version should follow the CLI package version because the dashboard is shipped by the CLI bundle, not as an independently versioned web product.
158
+ - qgrid skills ship with the CLI package, not a separate skills package. qgrid cannot be used with the AI SDK alone; a local qgrid CLI server is required, so CLI install is the right moment to sync `.agents/skills/qgrid` and `.claude/skills/qgrid` into the consuming project.
159
+
160
+ ## Image Generation
161
+
162
+ Sources:
163
+
164
+ - `docs/brainstorms/2026-07-05-qgrid-image-generation-requirements.md`
165
+ - `docs/plans/2026-07-05-001-feat-codex-image-generation-plan.md`
166
+ - `docs/brainstorms/2026-07-06-qgrid-image-persistence-requirements.md`
167
+ - `docs/brainstorms/2026-07-06-codex-image-cost-estimate.md`
168
+
169
+ Key decisions:
170
+
171
+ - Image generation is OpenAI/Codex-only and request-level opt-in. It is essentially a Codex-hosted `image_generation` tool call, not qgrid directly calling the OpenAI Images API. Always-on image tools would change every turn's tool configuration and threaten prompt-cache stability.
172
+ - It is non-stream only. Codex returns completed base64 image payloads, not useful image deltas.
173
+ - Image turns are cold one-shot threads and are excluded from thread reuse. This prevents base64 payloads from entering reusable conversation state and cache prefixes.
174
+ - qgrid performs preflight capability/model gates and postflight "image count must be greater than zero" checks. Codex can otherwise silently return text when image generation is unavailable or unused.
175
+ - Returned images are inline base64 content parts for consumers and AI SDK `file` parts. qgrid does not promise aspect ratio, edits, or reference-image control on this path.
176
+ - `imageGenerationOptions` currently supports quality and size. The worker instruction and request-log pricing assumption use `gpt-image-2`, with defaults `medium` and `1536x1024`.
177
+ - qgrid does not manage generated images as durable assets through a `generated_images` table or object-storage layer. Current request logs can still contain inline image data URLs for inspection and synthetic `image_generation` tool-call steps.
178
+ - Image cost is stored separately in `request_logs.image_cost_usd`; `request_logs.cost_usd` remains the Codex driver model token cost. Because Codex does not expose exact image tool usage, `image_cost_usd` is a price-table estimate and may be inaccurate.
179
+ - Inspect current plans, tests, and implementation before changing behavior; the Codex tool surface can change underneath qgrid.
@@ -0,0 +1,32 @@
1
+ # Docs Workflow
2
+
3
+ Use this reference for qgrid planning, diagnosis, and review workflow.
4
+
5
+ The project workflow is documented in `docs/WORKFLOW.md`.
6
+
7
+ ## Standard flow
8
+
9
+ 1. Brainstorm and plan before substantial work.
10
+ 2. Store local docs under `docs/` using existing directories:
11
+ - `docs/brainstorms/`
12
+ - `docs/plans/`
13
+ - `docs/diagnosis/`
14
+ - `docs/solutions/`
15
+ 3. Use peer review for plans and docs when requested.
16
+ 4. Accept review feedback by default unless there is a concrete tradeoff-based reason not to.
17
+
18
+ ## Existing docs are context
19
+
20
+ Before changing runtime-sensitive areas, search `docs/diagnosis`, `docs/solutions`, and recent `docs/plans` for prior decisions. qgrid has important historical decisions around:
21
+
22
+ - Codex thread reuse and prompt cache.
23
+ - Claude Code fresh spawn.
24
+ - structured output strictness.
25
+ - Anthropic usage categories.
26
+ - request log cache hit metrics.
27
+ - quota threshold routing.
28
+ - token isolation and auth env leaks.
29
+
30
+ Do not copy stale docs blindly. Use docs to identify decisions and then verify against current code.
31
+
32
+ When a repo doc establishes or changes durable rationale for a qgrid feature, reflect the stable decision in `references/decision-rationale.md`. Keep the skill reference as a compact index of why the decision exists and where the source docs live; do not copy full plans or brainstorms into the skill.