@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,153 @@
1
+ # OpenAI Codex Runtime
2
+
3
+ Use this reference before changing OpenAI provider behavior, Codex worker lifecycle, thread reuse, cache behavior, OAuth refresh, image-generation gates, or quota routing.
4
+
5
+ ## Contents
6
+
7
+ - Process model
8
+ - Codex configuration
9
+ - Initialization and auth
10
+ - Thread and turn flow
11
+ - Thread retention
12
+ - Queue and routing
13
+ - Usage and notifications
14
+ - Streaming
15
+ - Image generation status
16
+
17
+ ## Process model
18
+
19
+ OpenAI tokens use persistent Codex app-server workers.
20
+
21
+ - Dispatcher: `packages/api/src/utils/providers/openai/openai-dispatcher.ts`.
22
+ - Worker: `packages/api/src/utils/providers/openai/codex-worker.ts`.
23
+ - RPC client: `packages/api/src/utils/providers/openai/codex-rpc.ts`.
24
+ - Process command: `codex app-server --listen stdio://`.
25
+ - Workers per token: `QGRID_WORKERS_PER_TOKEN`, default 3, capped at 5.
26
+ - Worker id: `tokenId * 10 + workerIndex`.
27
+ - Worker home: `/tmp/qgrid-codex/${tokenId}-${workerIndex}` when `workerIndex` exists.
28
+ - Worker cwd: `${CODEX_HOME}/cwd`.
29
+
30
+ Workers are persistent. Turns are single-flight per worker (`busy` flag). When all eligible workers are busy, requests enter an in-memory queue.
31
+
32
+ ## Codex configuration
33
+
34
+ qgrid writes a worker-local `config.toml` under `CODEX_HOME` before spawning Codex. The intent is to use Codex as a plain generation backend, not as a coding agent.
35
+
36
+ Disabled by config:
37
+
38
+ - web search
39
+ - shell tool
40
+ - tool search/suggest
41
+ - multi-agent
42
+ - image generation by default
43
+ - apps/plugins
44
+ - view_image
45
+ - bundled skills/instructions
46
+ - permissions/apps/environment instruction blocks
47
+
48
+ Spawn env is intentionally small:
49
+
50
+ - `PATH`
51
+ - `TMPDIR`
52
+ - `CODEX_HOME`
53
+ - `CODEX_EXEC_SERVER_URL=none`
54
+
55
+ Do not re-enable built-in Codex tools/instructions unless the user explicitly asks for agentic Codex behavior.
56
+
57
+ ## Initialization and auth
58
+
59
+ Worker initialization:
60
+
61
+ 1. Spawn Codex app-server over stdio.
62
+ 2. Create `CodexRpcClient`.
63
+ 3. Send `initialize`.
64
+ 4. Login with ChatGPT/OAuth credentials.
65
+ 5. Bind Codex server-request `account/chatgptAuthTokens/refresh` to qgrid's token refresh handler.
66
+
67
+ If Codex asks to refresh ChatGPT auth tokens, qgrid handles it through `handleChatgptAuthTokensRefresh(tokenId)`.
68
+
69
+ ## Thread and turn flow
70
+
71
+ Cold path:
72
+
73
+ 1. `thread/start` with `ephemeral: true`.
74
+ 2. `baseInstructions` is a minimal assistant prompt.
75
+ 3. `developerInstructions` carries the qgrid system prompt.
76
+ 4. Optional `thread/inject_items` injects full history.
77
+ 5. `turn/start` runs current input.
78
+
79
+ Reuse path:
80
+
81
+ 1. qgrid verifies the incoming `threadCoord`.
82
+ 2. Dispatcher reacquires the exact worker by `workerId`.
83
+ 3. It checks worker readiness, active status, epoch, thread presence, and quota threshold.
84
+ 4. It waits up to 5 seconds for that worker to become free.
85
+ 5. It calls `turn/start` on the existing thread with delta input only.
86
+
87
+ Thread metadata is stored only inside the worker process. Worker restart increments `epoch` and clears thread metadata. Thread reuse must fall back cold if epoch or thread lookup fails.
88
+
89
+ ## Thread retention
90
+
91
+ Worker thread metadata:
92
+
93
+ - idle TTL: 10 minutes.
94
+ - max threads per worker: 16.
95
+ - cleanup is lazy before creating a new thread.
96
+
97
+ There is no explicit close RPC for old ephemeral threads; qgrid removes them from the reuse map and stops routing turns to them.
98
+
99
+ ## Queue and routing
100
+
101
+ OpenAI worker selection:
102
+
103
+ - Prefer reuse worker when a valid reuse coordinate exists.
104
+ - Otherwise select an idle ready active worker round-robin across eligible workers.
105
+ - If no worker is free, enqueue.
106
+ - Queue timeout: 60 seconds.
107
+ - Max queue size: 50.
108
+
109
+ Quota threshold:
110
+
111
+ - `quota_threshold` is stored on tokens.
112
+ - Dispatcher reads rate limits through a ready worker.
113
+ - Rate limit reads are cached for 60 seconds.
114
+ - Lookup failure is fail-open.
115
+ - If all ready active tokens are over threshold, throw `QuotaThresholdExceededError`.
116
+
117
+ ## Usage and notifications
118
+
119
+ Codex RPC notifications drive result collection:
120
+
121
+ - `item/agentMessage/delta`: text deltas and TTFT.
122
+ - `item/completed`: final text and image-generation results.
123
+ - `thread/tokenUsage/updated`: token usage.
124
+ - `turn/completed`: duration and terminal status.
125
+ - `error`: terminal error.
126
+
127
+ When using thread reuse, use `tokenUsage.last`, not `tokenUsage.total`, for request logs. `.total` is conversation cumulative and mixes previous turns, which corrupts per-request cache hit metrics.
128
+
129
+ ## Streaming
130
+
131
+ Streaming uses the same worker/thread execution with callbacks:
132
+
133
+ - `onThreadId` emits the thread id when a new thread is created.
134
+ - `onTurnId` emits turn id after `turn/start`.
135
+ - qgrid can interrupt OpenAI turns on SSE close by calling `turn/interrupt` across ready workers.
136
+
137
+ ## Image generation
138
+
139
+ Image generation is OpenAI/Codex-only and implemented as an opt-in Codex `image_generation` tool call. It is not a direct OpenAI Images API call from qgrid.
140
+
141
+ - It is opt-in through `imageGeneration`.
142
+ - `imageGenerationOptions` carries quality/size hints and the cost-estimation basis.
143
+ - It is non-stream only; streaming path rejects it.
144
+ - It always uses a cold one-shot thread and does not issue a reusable coordinate.
145
+ - It enables `features.image_generation` only on that thread; global config remains disabled.
146
+ - It swaps the normal "text only" base instruction for an image-permitting instruction on that thread.
147
+ - It gates on provider capability and model multimodality.
148
+ - Failure kinds are `gate`, `not_called`, and `incomplete`.
149
+ - Codex notifications expose image items through `item/started` and `item/completed`; qgrid treats non-empty base64 `result` as the completion signal, not the item `status` string.
150
+ - Completed images are surfaced as qgrid `image` content parts and AI SDK `file` parts with `mediaType: "image/png"`.
151
+ - qgrid uses `gpt-image-2` as the image-tool pricing/model assumption. Codex does not expose exact image tool token usage, so `image_cost_usd` is an estimate from the public price table, not exact billing.
152
+
153
+ Before modifying this area, inspect latest docs/plans/brainstorms and current tests.
@@ -0,0 +1,101 @@
1
+ # Prompt Cache And Usage
2
+
3
+ Use this reference for `sessionKey`, `threadCoord`, prompt cache, request-log usage metrics, and cost accounting.
4
+
5
+ ## qgrid thread coordinates
6
+
7
+ Client-facing `QgridThreadCoord`:
8
+
9
+ ```ts
10
+ {
11
+ workerId: number;
12
+ threadId: string;
13
+ epoch: number;
14
+ systemHash: string;
15
+ }
16
+ ```
17
+
18
+ - Server issues this after a provider turn.
19
+ - Client returns it through `runContext.threadCoord`.
20
+ - AI SDK hides this behind `providerOptions.qgrid.sessionKey` for OpenAI models.
21
+ - `systemHash` is the first 16 hex chars of SHA-256 over the system prompt.
22
+ - Reuse is eligible only when incoming `systemHash` matches current system prompt.
23
+
24
+ ## AI SDK `sessionKey`
25
+
26
+ `packages/ai-sdk/src/index.ts` stores `threadCoord` in a module-level map by `sessionKey` for 10 minutes.
27
+
28
+ - OpenAI models: `sessionKey` can replay the stored coordinate.
29
+ - Anthropic models: `reusableSessionKey` returns `undefined`, so the coordinate is not stored or replayed.
30
+ - Tool-call loop run context and non-tool multi-turn context share the same `runContext` boundary.
31
+
32
+ ## OpenAI/Codex cache behavior
33
+
34
+ Codex sets OpenAI prompt-cache affinity from the Codex conversation/thread id. qgrid originally got cache misses by creating a new thread for every request. Thread reuse fixes this by keeping the same Codex thread for a logical conversation.
35
+
36
+ Cache hit requires:
37
+
38
+ 1. `QGRID_OPENAI_THREAD_REUSE` is not `"false"`.
39
+ 2. AI SDK sends a stable `sessionKey`.
40
+ 3. Server receives a `threadCoord`.
41
+ 4. `systemHash` matches.
42
+ 5. The target worker exists, is ready/active, has same `epoch`, and still has the thread.
43
+ 6. The worker becomes free within the reuse wait window.
44
+ 7. Prefix content remains stable.
45
+
46
+ When reuse succeeds, qgrid sends only delta input to `turn/start`. When reuse fails, qgrid falls back to cold execution with full prompt plus history injection.
47
+
48
+ Important cache breakers:
49
+
50
+ - Different system prompt.
51
+ - Worker restart or thread TTL eviction.
52
+ - Schema/output format changes near the prefix. Codex places `outputSchema` in a prefix-sensitive area, so changing schemas turn-by-turn can break cache even on the same thread.
53
+ - Image generation requests, which intentionally use cold one-shot threads and do not issue reuse coordinates.
54
+
55
+ ## Anthropic/Claude Code cache behavior
56
+
57
+ Anthropic uses fresh Claude Code process spawn per request. qgrid does not replay Anthropic `sessionKey`.
58
+
59
+ Prompt cache can still work when prefixes are stable, but:
60
+
61
+ - Full history is replayed through stream-json after flattening.
62
+ - Cache write/read can be eventually consistent; immediate next request is not guaranteed to hit.
63
+ - Claude Code OAuth token cache behavior has provider-specific quirks documented in `docs/solutions`.
64
+ - Avoid putting volatile schema text into the system prompt. qgrid uses `--json-schema` / `StructuredOutput` for structured output.
65
+
66
+ ## Usage normalization
67
+
68
+ qgrid's standard usage meaning:
69
+
70
+ - `input_tokens`: total input for this request, including cache read and cache creation input.
71
+ - `cache_read_tokens`: cached input read.
72
+ - `cache_creation_tokens`: input written to prompt cache.
73
+ - `output_tokens`: output tokens.
74
+
75
+ OpenAI/Codex already reports `inputTokens` as total input including cached input. Use Codex `tokenUsage.last` for per-request logs, not `tokenUsage.total`.
76
+
77
+ Anthropic native usage categories are mutually exclusive:
78
+
79
+ - `input_tokens`
80
+ - `cache_creation_input_tokens`
81
+ - `cache_read_input_tokens`
82
+
83
+ qgrid normalizes Anthropic by summing those three into standard `inputTokens` and preserving cache read/write as subfields.
84
+
85
+ ## Cache hit rate
86
+
87
+ For qgrid-standard usage, hit rate denominator is `input_tokens`, not `input_tokens + cache_read_tokens + cache_creation_tokens`.
88
+
89
+ Do not double-count cache subfields. This was a real dashboard bug: OpenAI cache hit rates could be displayed near half their actual value when cache tokens were added to the denominator again.
90
+
91
+ ## Cost accounting
92
+
93
+ `packages/api/src/utils/providers/common/model-cost.ts` contains model price fallback.
94
+
95
+ - OpenAI cost normally uses qgrid's price table.
96
+ - OpenAI image generation stores a separate `image_cost_usd` estimate. It assumes Codex's image tool is priced as `gpt-image-2` for the selected/default quality and size, because qgrid does not receive exact image tool usage from Codex.
97
+ - Anthropic Claude Code may return `total_cost_usd`; qgrid prefers provider cost when present and positive.
98
+ - Anthropic cache creation uses 5-minute cache write price in fallback tables.
99
+ - OpenAI long-context surcharge applies to the whole request when input exceeds the model threshold, not just the excess tokens.
100
+
101
+ When changing models or prices, verify against current official pricing before editing the table.
@@ -0,0 +1,54 @@
1
+ # Provider Runtime Differences
2
+
3
+ Use this reference when comparing OpenAI and Anthropic behavior, debugging provider-specific bugs, or reviewing cross-provider changes.
4
+
5
+ | Topic | OpenAI via Codex | Anthropic via Claude Code |
6
+ |---|---|---|
7
+ | Process lifetime | Persistent `codex app-server` workers | Fresh `claude` process per request |
8
+ | Worker pool | Token x `QGRID_WORKERS_PER_TOKEN`, default 3, max 5 | No workers; in-memory token pool only |
9
+ | Worker id | `tokenId * 10 + workerIndex` | `tokenId` |
10
+ | Epoch | Worker spawn counter; changes on restart | Always `0` |
11
+ | Request concurrency | One turn per worker; queue when all eligible workers busy | Fresh process per request; token selection uses least-used RR |
12
+ | Model routing | `openai/*`; qgrid strips provider prefix before provider call | `anthropic/*`; provider canonicalizes model and strips prefix/`[1m]` |
13
+ | Prefix-less models | Fallback not implemented | Fallback not implemented |
14
+ | Thread/session | Ephemeral Codex thread stored in worker memory | Fresh Claude `--session-id` per request |
15
+ | `sessionKey` | AI SDK stores and replays `threadCoord` for thread reuse | AI SDK intentionally disables storage/replay |
16
+ | Multi-turn | Reuse sends delta input to existing Codex thread; cold fallback injects full history | Fresh spawn receives flattened full history through stream-json |
17
+ | Cache key | Codex conversation/thread id is prompt-cache affinity | Anthropic prefix cache via Claude Code; fresh spawn still can cache stable prefixes |
18
+ | Built-in tools | Codex tools/apps/plugins/skills disabled by worker config | Claude tools disabled via `--tools ""`; `StructuredOutput` allowed only for schema |
19
+ | AI SDK tools | Emulated via qgrid structured output schema, then mapped to AI SDK `tool-call` content | Same emulation path; not native Claude Code tools |
20
+ | Structured output | Codex `outputSchema` passed to `turn/start`; schema changes can affect prefix cache | Claude `--json-schema` through `StructuredOutput` tool; strict schemas required |
21
+ | Usage accounting | Codex usage already reports `inputTokens` including cached input | Native Anthropic categories are mutually exclusive; qgrid sums them into total input |
22
+ | Cost source | qgrid model price fallback | Prefer Claude Code `total_cost_usd`, else qgrid price fallback |
23
+ | Settings isolation | Per-worker `CODEX_HOME` and config.toml | Shared project cwd plus per-token `CLAUDE_CONFIG_DIR` |
24
+ | Streaming close | Can interrupt Codex turn with `turn/interrupt` | Abort kills the fresh child process |
25
+ | Image generation | In progress, OpenAI/Codex non-stream only | Explicitly unsupported |
26
+
27
+ ## Routing contract
28
+
29
+ Provider routing happens in `packages/api/src/application/qgrid/qgrid.dispatcher.ts`.
30
+
31
+ - `openai/<model>` routes to `OpenAIDispatcher`.
32
+ - `anthropic/<model>` routes to `AnthropicDispatcher`.
33
+ - models without provider prefix produce "Direct LLM API fallback not implemented".
34
+
35
+ Do not silently route prefix-less Claude model names such as `claude-sonnet-4-6` to Anthropic. Tests intentionally guard against this.
36
+
37
+ ## Shared contracts
38
+
39
+ Both providers return `GenerateResult` with:
40
+
41
+ - `text`
42
+ - `tokenName`
43
+ - `usage`
44
+ - `durationMs`
45
+ - optional `ttftMs`
46
+ - optional provider cost
47
+ - canonical `model`
48
+ - `threadCoord`
49
+
50
+ `qgrid.dispatcher.ts` maps this into `QueryOutput`, applies tool-call emulation, calculates fallback cost, and issues a client-facing `QgridThreadCoord` by adding `systemHash`.
51
+
52
+ Both providers receive strictified output schemas from `buildStrictOutputSchema`.
53
+
54
+ Tool calling uses this shared strict-schema path. It is not native provider tool calling on either route.
@@ -0,0 +1,37 @@
1
+ # Repo Map
2
+
3
+ Use this map to orient qgrid work quickly.
4
+
5
+ ## Active packages
6
+
7
+ - `packages/api`: Sonamu server, qgrid API frame, provider dispatchers, token/request-log models, migrations, OAuth, runtime integration.
8
+ - `packages/ai-sdk`: active public AI SDK provider and logger integration. Prefer this for public SDK behavior and examples.
9
+ - `packages/web`: dashboard UI. Usually depends on Sonamu-generated API clients and API model shape.
10
+ - `packages/cli`: global `qgrid` command, bundled server boot, dependency checks, DB preflight, and qgrid Codex/Claude Code skill sync. The CLI package ships `skills/qgrid` and its `postinstall` links it into consuming projects.
11
+ - `scripts` and `packages/api/scripts`: smoke tests, debug scripts, runtime probes.
12
+ - `docs`: local planning, diagnosis, and solution history. `docs/WORKFLOW.md` is the collaboration convention.
13
+
14
+ ## Deprecated/context-only package
15
+
16
+ - `packages/sdk`: deprecated. Read only for historical context or migration clues. Do not implement new features, docs, or examples against this package unless explicitly asked for legacy compatibility.
17
+
18
+ ## Runtime hot spots
19
+
20
+ - Provider router: `packages/api/src/application/qgrid/qgrid.dispatcher.ts`.
21
+ - Conversation/thread reuse: `packages/api/src/application/qgrid/conv-routing.ts`.
22
+ - Query API and SSE stream: `packages/api/src/application/qgrid/qgrid.frame.ts`.
23
+ - Request logging lifecycle: `packages/api/src/application/qgrid/qgrid-run-lifecycle.ts`.
24
+ - Token LISTEN/NOTIFY sync: `packages/api/src/application/qgrid/token-subscriber.ts`.
25
+ - Token trigger setup: `packages/api/src/application/qgrid/token-trigger-setup.ts`.
26
+ - OpenAI Codex runtime: `packages/api/src/utils/providers/openai/*`.
27
+ - Anthropic Claude Code runtime: `packages/api/src/utils/providers/anthropic/*`.
28
+ - Cost/usage accounting: `packages/api/src/utils/providers/common/model-cost.ts`.
29
+ - Tool-call emulation: `packages/api/src/application/qgrid/tool-emulation.ts`.
30
+
31
+ ## Web/Sonamu hot spots
32
+
33
+ - Entity definitions: `packages/api/src/application/**/**.entity.json`.
34
+ - API models/types: `packages/api/src/application/**/**.model.ts`, `*.types.ts`.
35
+ - Generated API files: `packages/api/src/application/sonamu.generated*`, `packages/web/src/services` when present, generated route/client files.
36
+ - Dashboard components: `packages/web/src/components/qgrid/*`.
37
+ - Dashboard routes: `packages/web/src/routes/*`.
@@ -0,0 +1,76 @@
1
+ # Request Log Run Lifecycle
2
+
3
+ Use this reference when changing request logging, tool-call loop logging, telemetry logger integration, request log steps, or dashboard metrics.
4
+
5
+ ## Modes
6
+
7
+ qgrid query supports `logMode`:
8
+
9
+ - `auto`: save one request_log after a simple request succeeds.
10
+ - `run`: create/update a run and append steps for multi-step/tool-call flows.
11
+ - `none`: no automatic logging.
12
+
13
+ If `logMode` is omitted, `qgrid.frame.ts` uses `run` when `isStep` is true and `auto` otherwise.
14
+
15
+ ## Query flow
16
+
17
+ For `logMode: "run"`:
18
+
19
+ 1. `beforeQuery(args)` creates or extends a run.
20
+ 2. `QgridDispatcher.query` or `queryStream` executes the provider call.
21
+ 3. `afterQuery(...)` records result/step data and returns `runContext`.
22
+ 4. qgrid merges lifecycle `requestLogId` with provider `threadCoord`.
23
+ 5. Errors call `finishRunWithError`; stream close can call abort handling.
24
+
25
+ For `auto`:
26
+
27
+ - qgrid saves a single request log row after the provider returns.
28
+ - It records token name, project name, model, prompts, response, usage, duration, TTFT, driver cost, image cost estimate, effort, history, status, tool-call count, and image-generation flag.
29
+ - Image results are also appended as synthetic `image_generation` tool-call steps so the detail page can inspect the generated image and pricing assumption.
30
+
31
+ ## Tool-call loop
32
+
33
+ The AI SDK provider tracks pending tool-call IDs. When the next AI SDK call contains all required tool results, it sends:
34
+
35
+ - existing `runContext`
36
+ - `toolResults`
37
+ - `logMode: "run"`
38
+
39
+ qgrid converts tool results into continuation input for providers. Cold fallback still includes a "continue using these results" text input.
40
+
41
+ Tool calls are produced through qgrid structured-output emulation, not native provider tool calling. The request log lifecycle records the emulated generate step, pending tool-call step rows, tool results on follow-up, and the final generate step. For full semantics, read `tool-calling-and-multiturn.md`.
42
+
43
+ ## Usage fields
44
+
45
+ Stored request log usage uses qgrid-standard semantics:
46
+
47
+ - `input_tokens`: total input, including cache read/write.
48
+ - `cache_read_tokens`: cached input read.
49
+ - `cache_creation_tokens`: prompt cache write input.
50
+ - `output_tokens`: output tokens.
51
+ - `cost_usd`: integer micro-USD in DB; displayed USD is `cost_usd / 1_000_000`.
52
+ - `image_cost_usd`: integer micro-USD estimate for Codex image generation output.
53
+ - `image_cost_method`: string such as `assumed:gpt-image-2:medium:1536x1024:png`.
54
+
55
+ OpenAI/Codex: use per-turn `.last` usage from `thread/tokenUsage/updated`.
56
+
57
+ Anthropic: normalize native mutually exclusive categories by summing input + cache creation + cache read into total input.
58
+
59
+ For image requests, keep `cost_usd` as the Codex driver model token cost. Image output cost is separate because qgrid observes Codex's `image_generation` result, not the OpenAI Images API usage object. Treat `image_cost_usd` as a price-table estimate that may be inaccurate if Codex changes its underlying image accounting.
60
+
61
+ ## Legacy normalization
62
+
63
+ `RequestLogModel` normalizes legacy Anthropic rows where stored `input_tokens` may not include cache read/write. Keep this in mind when changing cost or display logic.
64
+
65
+ ## TTFT
66
+
67
+ TTFT is tracked by wrapping first provider delta:
68
+
69
+ - OpenAI: first `item/agentMessage/delta`.
70
+ - Anthropic: first text or structured partial JSON delta.
71
+
72
+ If missing, qgrid maps TTFT to `0` in `QueryOutput` and nullable/optional places as appropriate.
73
+
74
+ ## Logger integration
75
+
76
+ `packages/ai-sdk/src/logger.ts` lets non-qgrid providers write logs into qgrid. It skips model provider `qgrid` to avoid double logging. Preserve stale-run cleanup because AI SDK telemetry may emit start without a final event on provider failures.
@@ -0,0 +1,47 @@
1
+ # Sonamu API And Web Flow
2
+
3
+ Use this reference for dashboard/API changes.
4
+
5
+ qgrid dashboard work is usually a Sonamu API/model/generated-client/web change, not an isolated React change.
6
+
7
+ ## Full path to inspect
8
+
9
+ 1. Entity and model definitions in `packages/api/src/application/**`.
10
+ 2. API frame/model methods decorated with `@api` or `@stream`.
11
+ 3. Generated Sonamu files such as `packages/api/src/application/sonamu.generated*`.
12
+ 4. Web API consumers and generated client/types.
13
+ 5. Dashboard routes/components in `packages/web/src/**`.
14
+
15
+ ## Common patterns
16
+
17
+ - Entity fields live in `*.entity.json`.
18
+ - Model methods live in `*.model.ts`.
19
+ - API frame methods for qgrid live in `qgrid.frame.ts`.
20
+ - i18n generated files may change when entity labels or static data change.
21
+ - Web components under `packages/web/src/components/qgrid` consume generated API shapes.
22
+ - The main dashboard sidebar version should stay synced to `packages/cli/package.json`. `packages/web/vite.config.ts` reads that package version and exposes it as `__QGRID_CLI_VERSION__`; `Sidebar.tsx` displays it.
23
+
24
+ ## Request log dashboard
25
+
26
+ Request log changes usually touch:
27
+
28
+ - `request-log.entity.json`
29
+ - `request-log.model.ts`
30
+ - `request-log.types.ts`
31
+ - `request-log-step.*`
32
+ - `qgrid-run-lifecycle.ts`
33
+ - `RequestLogTable.tsx`
34
+ - `routes/requests/show.tsx`
35
+
36
+ Check list and detail views. Avoid loading large text columns in list views unless intentionally needed.
37
+
38
+ Image-generation request logs add `is_image_generation`, `image_cost_usd`, and `image_cost_method`. The list view displays driver plus image cost as the total cost cell; the detail view shows driver cost and image cost separately and renders generated image data URLs from response/tool-step content.
39
+
40
+ ## Verification
41
+
42
+ After API shape changes:
43
+
44
+ - Regenerate or inspect generated Sonamu files according to repo workflow.
45
+ - Run targeted API tests.
46
+ - Check web consumers compile against generated types.
47
+ - Verify dashboard display for null/legacy values when changing stored request log fields.
@@ -0,0 +1,205 @@
1
+ # Token Auth Quota Lifecycle
2
+
3
+ Use this reference before changing token storage, OAuth flows, token activation, token subscriber behavior, quota thresholds, usage APIs, or provider token routing.
4
+
5
+ ## Contents
6
+
7
+ - Source files
8
+ - Token table contract
9
+ - Token APIs
10
+ - Anthropic OAuth flow
11
+ - OpenAI OAuth and refresh flow
12
+ - Token sync and reconcile
13
+ - Provider runtime consequences
14
+ - Quota threshold semantics
15
+
16
+ ## Source Files
17
+
18
+ - Token model/types: `packages/api/src/application/token/token.model.ts`, `token.types.ts`, `token.entity.json`.
19
+ - qgrid API frame and OAuth endpoints: `packages/api/src/application/qgrid/qgrid.frame.ts`.
20
+ - Anthropic OAuth utilities and usage API: `packages/api/src/application/qgrid/oauth.ts`.
21
+ - Token DB trigger setup: `packages/api/src/application/qgrid/token-trigger-setup.ts`.
22
+ - Token subscriber: `packages/api/src/application/qgrid/token-subscriber.ts`.
23
+ - OpenAI refresh/quota: `packages/api/src/utils/providers/openai/openai-refresh.ts`, `openai-quota.ts`.
24
+ - Anthropic quota: `packages/api/src/utils/providers/anthropic/anthropic-quota.ts`.
25
+ - Provider token event handlers: `openai-dispatcher.ts`, `anthropic-dispatcher.ts`.
26
+
27
+ ## Token Table Contract
28
+
29
+ `tokens` stores:
30
+
31
+ - `provider`: provider string such as `openai` or `anthropic`.
32
+ - `credentials`: JSONB credentials.
33
+ - `name`: display/logging name.
34
+ - `active`: whether provider dispatchers may use it.
35
+ - `ord`: dashboard ordering.
36
+ - `quota_threshold`: nullable integer percentage, validated as 1..100 when present.
37
+
38
+ On creation, `TokenModel.save` applies default `quota_threshold = 80` unless `id` or `quota_threshold` is already provided.
39
+
40
+ OpenAI credentials:
41
+
42
+ ```ts
43
+ {
44
+ accessToken: string;
45
+ refreshToken: string;
46
+ idToken?: string;
47
+ accessTokenExpiresAt: number;
48
+ idTokenExpiresAt?: number;
49
+ accountId: string;
50
+ planType?: string;
51
+ }
52
+ ```
53
+
54
+ Anthropic credentials:
55
+
56
+ ```ts
57
+ {
58
+ accessToken: string;
59
+ refreshToken: string;
60
+ expiresAt: number;
61
+ accountUuid: string;
62
+ }
63
+ ```
64
+
65
+ Duplicate account replacement is account-identifier based:
66
+
67
+ - OpenAI: `credentials.accountId`.
68
+ - Anthropic: `credentials.accountUuid`.
69
+
70
+ ## Token APIs
71
+
72
+ `QgridFrame` exposes token APIs:
73
+
74
+ - `addToken(provider, credentials, name)`: saves a token directly.
75
+ - `updateToken(id, name?, quotaThreshold?)`: updates name and/or quota threshold, preserving provider/credentials.
76
+ - `removeToken(id)`: deletes token row.
77
+ - `toggleToken(id)`: flips `active`.
78
+ - `stats()`: reports dispatcher cache stats.
79
+ - `health()`: reports active token cache count and subscriber status.
80
+ - `usage(tokenId?)`: returns provider usage/rate-limit summary.
81
+
82
+ When updating a token, preserve existing provider and credentials unless intentionally rotating credentials.
83
+
84
+ ## Anthropic OAuth Flow
85
+
86
+ Anthropic OAuth is implemented directly in qgrid.
87
+
88
+ 1. `oauthStart(name)` generates PKCE verifier/challenge/state.
89
+ 2. qgrid builds the Claude OAuth authorize URL with Claude Code-like scopes.
90
+ 3. Pending state is cached under `oauth:state:<state>` for 5 minutes.
91
+ 4. `/callback` calls `handleOAuthCallback(code, state, reply)`.
92
+ 5. qgrid exchanges the code for tokens.
93
+ 6. Existing Anthropic tokens with the same `accountUuid` are deleted.
94
+ 7. qgrid saves a new `provider: "anthropic"` token.
95
+
96
+ `QGRID_PUBLIC_BASE_URL` controls the public callback base. If unset, callback defaults to `http://localhost:${PORT}/callback`.
97
+
98
+ Refresh:
99
+
100
+ - `refreshToken(token)` uses the stored refresh token and `refreshAccessToken`.
101
+ - Refreshed credentials are saved back through `TokenModel.save`.
102
+ - `usage()` refreshes expired Anthropic access tokens before usage lookup when a refresh token exists.
103
+ - `AnthropicDispatcher` also preemptively refreshes when expiration is within 60 seconds.
104
+
105
+ Usage/quota:
106
+
107
+ - Usage API is `https://api.anthropic.com/api/oauth/usage`.
108
+ - qgrid caches Anthropic usage by access-token suffix for 60 seconds.
109
+ - Quota threshold uses `five_hour.utilization`.
110
+ - Usage lookup failure is fail-open for routing.
111
+
112
+ ## OpenAI OAuth And Refresh Flow
113
+
114
+ OpenAI OAuth is delegated to Codex app-server.
115
+
116
+ Browser registration:
117
+
118
+ 1. `oauthStartOpenAI(name)` calls `OpenAIDispatcher.startBrowserLogin(name)`.
119
+ 2. Dispatcher creates a temporary `CodexAppServerWorker`.
120
+ 3. Worker spawns `codex app-server` and calls `account/login/start` with `type: "chatgpt"`.
121
+ 4. qgrid returns Codex's `authUrl`.
122
+ 5. In the background, dispatcher waits for `account/login/completed`.
123
+ 6. Worker reads managed credentials from its `auth.json`.
124
+ 7. Existing OpenAI tokens with the same `accountId` are deleted.
125
+ 8. qgrid saves a new `provider: "openai"` token.
126
+ 9. Temporary worker is killed; pending browser login times out after 5 minutes.
127
+
128
+ Worker login:
129
+
130
+ - Normal OpenAI workers call `account/login/start` with `type: "chatgptAuthTokens"`, `accessToken`, `chatgptAccountId`, and optional `chatgptPlanType`.
131
+
132
+ Refresh:
133
+
134
+ - Codex can send server-request `account/chatgptAuthTokens/refresh`.
135
+ - qgrid handles it through `handleChatgptAuthTokensRefresh(tokenId)`.
136
+ - Refresh is deduplicated per token with an in-flight promise.
137
+ - Refresh has a 5-second minimum interval; too-soon calls read current DB credentials.
138
+ - Refresh token rotation is saved immediately. If DB save fails after rotation, treat it as token-death risk because the old refresh token may already be invalid.
139
+
140
+ Usage/quota:
141
+
142
+ - OpenAI quota threshold uses Codex `account/rateLimits/read`.
143
+ - qgrid reads rate limits through a ready worker.
144
+ - Rate limit data is cached for 60 seconds.
145
+ - Threshold uses primary `usedPercent`.
146
+ - Usage lookup failure is fail-open for routing.
147
+
148
+ ## Token Sync And Reconcile
149
+
150
+ On server start, qgrid creates PostgreSQL triggers for `tokens_changed`.
151
+
152
+ Triggers notify on:
153
+
154
+ - INSERT
155
+ - DELETE
156
+ - UPDATE when `active`, `credentials`, `provider`, `name`, or `quota_threshold` changes.
157
+
158
+ `TokenSubscriber`:
159
+
160
+ - connects to PostgreSQL and `LISTEN`s on `tokens_changed`;
161
+ - reconnects with jittered exponential backoff up to 30 seconds;
162
+ - runs periodic reconcile every 10 minutes because LISTEN/NOTIFY can miss changes while disconnected;
163
+ - replaces dispatcher cache from `TokenModel.findActive("A")`.
164
+
165
+ On DELETE or missing row:
166
+
167
+ - remove from `QgridDispatcher.tokens`;
168
+ - call OpenAI `onTokenRemoved`;
169
+ - call Anthropic `onTokenRemoved`.
170
+
171
+ OpenAI row changes:
172
+
173
+ - active INSERT: `onTokenAdded` spawns workers.
174
+ - active UPDATE: `onTokenUpdated`, then `onTokenActivated`.
175
+ - inactive row: `onTokenDeactivated`.
176
+ - reconcile uses `replaceTokens`, which removes absent tokens, updates existing tokens, adds new tokens, and activates rows present in active DB set.
177
+
178
+ Anthropic row changes:
179
+
180
+ - active INSERT: add to in-memory pool.
181
+ - active UPDATE: update in-memory pool.
182
+ - inactive row: remove from pool.
183
+ - reconcile uses active Anthropic rows to replace the in-memory pool.
184
+
185
+ ## Provider Runtime Consequences
186
+
187
+ OpenAI token changes can create, update, deactivate, or kill persistent Codex workers.
188
+
189
+ - In-place OpenAI worker update is allowed only when account id and plan type match.
190
+ - Otherwise workers are killed and respawned.
191
+ - If no ready active OpenAI workers remain, queued OpenAI requests are rejected.
192
+
193
+ Anthropic token changes only affect the in-memory token pool because every request fresh-spawns Claude Code.
194
+
195
+ ## Quota Threshold Semantics
196
+
197
+ `quota_threshold` is a routing gate, not a hard external-provider guarantee.
198
+
199
+ - Null means no threshold gate.
200
+ - Lookup failure is fail-open.
201
+ - Individual over-threshold tokens are excluded from selection.
202
+ - If all ready/eligible tokens are over threshold, qgrid throws `QuotaThresholdExceededError`.
203
+ - Log messages use `quota_threshold gate` with reasons such as `over_threshold`, `lookup_fail_open`, and `all_exceeded`.
204
+
205
+ OpenAI queue items keep an `excludedTokenIds` set so a token found over threshold for a request is not immediately reselected for that same queued request.