@bitkyc08/opencodex 2.34.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/dist/assets/{index-C4TMRloX.js → index-DNdRKXK9.js} +11 -11
- package/gui/dist/index.html +1 -1
- package/package.json +3 -1
- package/src/adapters/base.ts +26 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +34 -41
- package/src/adapters/cursor/envelope-echo.ts +128 -0
- package/src/adapters/cursor/request-builder.ts +19 -12
- package/src/adapters/cursor/tool-definitions.ts +2 -1
- package/src/adapters/cursor/tool-result-normalize.ts +23 -31
- package/src/adapters/cursor.ts +21 -2
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +71 -2
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google.ts +15 -1
- package/src/adapters/kiro-constants.ts +12 -0
- package/src/adapters/kiro.ts +128 -11
- package/src/adapters/openai-chat.ts +16 -2
- package/src/adapters/openai-responses.ts +15 -2
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +2 -1
- package/src/adapters/xai-web-search.ts +10 -14
- package/src/claude/outbound.ts +14 -3
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +84 -15
- package/src/cli/account-extended.ts +261 -28
- package/src/cli/account-main.ts +12 -12
- package/src/cli/account.ts +40 -10
- package/src/cli/agent.ts +8 -1
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-desktop.ts +31 -11
- package/src/cli/dispatch.ts +195 -27
- package/src/cli/doctor.ts +100 -1
- package/src/cli/help.ts +11 -2
- package/src/cli/index.ts +19 -3
- package/src/cli/inspect.ts +230 -0
- package/src/cli/observe.ts +11 -3
- package/src/cli/registry.ts +34 -2
- package/src/cli/runtime-api.ts +51 -7
- package/src/cli/status.ts +16 -0
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/usage-report.ts +52 -2
- package/src/cli/version-skew.ts +46 -0
- package/src/codex/account-label.ts +21 -0
- package/src/codex/catalog/provider-fetch.ts +4 -0
- package/src/codex/transition-state.ts +12 -3
- package/src/compatibility/openai-responses.ts +9 -1
- package/src/generated/compatibility-version.json +95 -59
- package/src/integrations/ownership-policy.ts +24 -5
- package/src/integrations/ownership.ts +36 -2
- package/src/integrations/state.ts +40 -7
- package/src/integrations/writer.ts +21 -3
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/errors.ts +25 -1
- package/src/lib/service-secrets.ts +15 -0
- package/src/oauth/store.ts +14 -5
- package/src/providers/label.ts +34 -1
- package/src/responses/turn-termination.ts +107 -0
- package/src/server/management/logs-usage-routes.ts +0 -16
- package/src/server/management/route-registry.ts +311 -0
- package/src/server/proxy-liveness.ts +27 -4
- package/src/server/request-log.ts +29 -1
- package/src/server/responses/core.ts +80 -0
- package/src/service.ts +34 -0
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/usage/log.ts +44 -4
- package/src/usage/summary.ts +10 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declared inventory of every reachable management route.
|
|
3
|
+
*
|
|
4
|
+
* DECLARED, not harvested. A grep cannot see this surface: 18 routes are registered
|
|
5
|
+
* through a regex, an `endsWith`, a `pathname.slice`, a prefix decode, a path constant, or a
|
|
6
|
+
* negated `pathname !== "…"` guard, and two of those are live routes whose only textual
|
|
7
|
+
* trace is the negated form. For `GET /api/storage` an equality scan finds solely the dead
|
|
8
|
+
* shadowed copy in `logs-usage-routes.ts` and never the live one.
|
|
9
|
+
*
|
|
10
|
+
* This module is pure DATA and must stay that way. It is imported by
|
|
11
|
+
* `src/server/management-api.ts`, which `tests/core-lab-boundary.test.ts` protects: a user
|
|
12
|
+
* with one provider and no Lab must execute no Lab code. Route paths are strings, so
|
|
13
|
+
* declaring `/api/lab/status` here creates no module edge. Never import a handler, and
|
|
14
|
+
* never import anything from `src/lab/`. The `module` field names the owning file as text
|
|
15
|
+
* for exactly this reason.
|
|
16
|
+
*
|
|
17
|
+
* Reconciliation lives in `tests/management-route-registry.test.ts`, which resolves
|
|
18
|
+
* `(method, path)` pairs from source and fails loudly on a route whose method it cannot
|
|
19
|
+
* determine. Adding a route without declaring it here fails that test.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Why a route has no CLI verb. Every value is a claim about the route that a reviewer
|
|
26
|
+
* can check, not a way to quiet the parity test.
|
|
27
|
+
*/
|
|
28
|
+
export type ExemptionReason =
|
|
29
|
+
/** Requires a dashboard browser session. Includes the user-consent star boundary. */
|
|
30
|
+
| "session-only"
|
|
31
|
+
/** Deliberately returns 405; there is nothing to drive. */
|
|
32
|
+
| "disabled"
|
|
33
|
+
/** Gated on a process-scoped capability principal, not an operator action. */
|
|
34
|
+
| "capability-principal"
|
|
35
|
+
/** A test seam, not an operator capability. */
|
|
36
|
+
| "test-seam"
|
|
37
|
+
/** The CLI reaches the same data through a local transport instead of HTTP. */
|
|
38
|
+
| "local-transport"
|
|
39
|
+
/** Unreachable in the live dispatch order; delete rather than expose. */
|
|
40
|
+
| "dead"
|
|
41
|
+
/**
|
|
42
|
+
* A verb is owed but belongs to a later work-phase. BOUNDED: requires `owner` and
|
|
43
|
+
* `ownerDoc`, and the parity test asserts that tracked doc exists and names the route.
|
|
44
|
+
* The doc is deliberately a repository file rather than the goalplan, which is
|
|
45
|
+
* gitignored -- a test reading machine-local state passes here and finds nothing in CI.
|
|
46
|
+
*/
|
|
47
|
+
| "deferred-verb";
|
|
48
|
+
|
|
49
|
+
export interface RouteExemption {
|
|
50
|
+
readonly reason: ExemptionReason;
|
|
51
|
+
/** Free text; required, because an exemption nobody justified is how a gate erodes. */
|
|
52
|
+
readonly why: string;
|
|
53
|
+
/** Work-phase that owes the verb. Required for `deferred-verb`. */
|
|
54
|
+
readonly owner?: string;
|
|
55
|
+
/** Tracked doc naming the route. Required for `deferred-verb`. */
|
|
56
|
+
readonly ownerDoc?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** How a route is registered, for routes an equality scan cannot see. */
|
|
60
|
+
export type NonLiteralMechanism =
|
|
61
|
+
| "negated-guard"
|
|
62
|
+
| "path-constant"
|
|
63
|
+
| "prefix-decode"
|
|
64
|
+
| "slice"
|
|
65
|
+
| "ends-with"
|
|
66
|
+
| "regex";
|
|
67
|
+
|
|
68
|
+
export interface ManagementRoute {
|
|
69
|
+
readonly method: HttpMethod;
|
|
70
|
+
readonly path: string;
|
|
71
|
+
/** Owning source file, repo-relative without the `src/` prefix or `.ts` suffix. */
|
|
72
|
+
readonly module: string;
|
|
73
|
+
readonly mutates: boolean;
|
|
74
|
+
/** Set when the route is not recoverable from an equality scan of its own file. */
|
|
75
|
+
readonly mechanism?: NonLiteralMechanism;
|
|
76
|
+
readonly exempt?: RouteExemption;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/** Every reachable management route. */
|
|
81
|
+
export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
|
|
82
|
+
// server/management-api
|
|
83
|
+
{ method: "POST", path: "/api/stop", module: "server/management-api", mutates: true },
|
|
84
|
+
// codex/auth-api
|
|
85
|
+
{ method: "DELETE", path: "/api/codex-auth/accounts", module: "codex/auth-api", mutates: true },
|
|
86
|
+
{ method: "GET", path: "/api/codex-auth/accounts", module: "codex/auth-api", mutates: false },
|
|
87
|
+
{ method: "GET", path: "/api/codex-auth/active", module: "codex/auth-api", mutates: false },
|
|
88
|
+
{ method: "GET", path: "/api/codex-auth/login-status", module: "codex/auth-api", mutates: false },
|
|
89
|
+
{ method: "GET", path: "/api/codex-auth/quota", module: "codex/auth-api", mutates: false },
|
|
90
|
+
{ method: "GET", path: "/api/codex-auth/reset-credits", module: "codex/auth-api", mutates: false },
|
|
91
|
+
{ method: "PATCH", path: "/api/codex-auth/pool-strategy", module: "codex/auth-api", mutates: true },
|
|
92
|
+
{ method: "POST", path: "/api/codex-auth/accounts", module: "codex/auth-api", mutates: true },
|
|
93
|
+
{ method: "POST", path: "/api/codex-auth/accounts/clear-cooldown", module: "codex/auth-api", mutates: true },
|
|
94
|
+
{ method: "POST", path: "/api/codex-auth/login", module: "codex/auth-api", mutates: true },
|
|
95
|
+
{ method: "POST", path: "/api/codex-auth/login/cancel", module: "codex/auth-api", mutates: true },
|
|
96
|
+
{ method: "POST", path: "/api/codex-auth/login/code", module: "codex/auth-api", mutates: true },
|
|
97
|
+
{ method: "POST", path: "/api/codex-auth/reset-credits/consume", module: "codex/auth-api", mutates: true },
|
|
98
|
+
{ method: "PUT", path: "/api/codex-auth/accounts/alias", module: "codex/auth-api", mutates: true },
|
|
99
|
+
{ method: "PUT", path: "/api/codex-auth/accounts/pause", module: "codex/auth-api", mutates: true },
|
|
100
|
+
{ method: "PUT", path: "/api/codex-auth/accounts/pause-exhausted", module: "codex/auth-api", mutates: true },
|
|
101
|
+
{ method: "PUT", path: "/api/codex-auth/accounts/priority", module: "codex/auth-api", mutates: true },
|
|
102
|
+
{ method: "PUT", path: "/api/codex-auth/active", module: "codex/auth-api", mutates: true },
|
|
103
|
+
{ method: "PUT", path: "/api/codex-auth/auto-switch", module: "codex/auth-api", mutates: true },
|
|
104
|
+
{ method: "PUT", path: "/api/codex-auth/failover", module: "codex/auth-api", mutates: true },
|
|
105
|
+
{ method: "PUT", path: "/api/codex-auth/pool-strategy", module: "codex/auth-api", mutates: true },
|
|
106
|
+
// codex/native-profile-api
|
|
107
|
+
{ method: "GET", path: "/api/native-main-profiles", module: "codex/native-profile-api", mutates: false },
|
|
108
|
+
{ method: "GET", path: "/api/native-main-profiles/doctor", module: "codex/native-profile-api", mutates: false },
|
|
109
|
+
{ method: "POST", path: "/api/native-main-profiles/recover", module: "codex/native-profile-api", mutates: true },
|
|
110
|
+
{ method: "POST", path: "/api/native-main-profiles/register", module: "codex/native-profile-api", mutates: true },
|
|
111
|
+
{ method: "POST", path: "/api/native-main-profiles/stage", module: "codex/native-profile-api", mutates: true },
|
|
112
|
+
{ method: "POST", path: "/api/native-main-profiles/stage/cancel", module: "codex/native-profile-api", mutates: true },
|
|
113
|
+
{ method: "POST", path: "/api/native-main-profiles/stage/finish", module: "codex/native-profile-api", mutates: true },
|
|
114
|
+
{ method: "POST", path: "/api/native-main-profiles/stage/heartbeat", module: "codex/native-profile-api", mutates: true },
|
|
115
|
+
{ method: "POST", path: "/api/native-main-profiles/switch", module: "codex/native-profile-api", mutates: true },
|
|
116
|
+
// server/management/agent-settings-routes
|
|
117
|
+
{ method: "GET", path: "/api/claude-code", module: "server/management/agent-settings-routes", mutates: false },
|
|
118
|
+
{ method: "GET", path: "/api/claude-desktop", module: "server/management/agent-settings-routes", mutates: false },
|
|
119
|
+
{ method: "GET", path: "/api/claude-desktop/status", module: "server/management/agent-settings-routes", mutates: false },
|
|
120
|
+
{ method: "GET", path: "/api/codex-auth/features/default-mode-request-user-input", module: "server/management/agent-settings-routes", mutates: false },
|
|
121
|
+
{ method: "GET", path: "/api/effort-caps", module: "server/management/agent-settings-routes", mutates: false },
|
|
122
|
+
{ method: "GET", path: "/api/grok", module: "server/management/agent-settings-routes", mutates: false },
|
|
123
|
+
{ method: "GET", path: "/api/injection-model", module: "server/management/agent-settings-routes", mutates: false },
|
|
124
|
+
{ method: "GET", path: "/api/subagent-model-fallback", module: "server/management/agent-settings-routes", mutates: false },
|
|
125
|
+
{ method: "GET", path: "/api/subagent-models", module: "server/management/agent-settings-routes", mutates: false },
|
|
126
|
+
{ method: "GET", path: "/api/v2", module: "server/management/agent-settings-routes", mutates: false },
|
|
127
|
+
{ method: "POST", path: "/api/claude-desktop/apply", module: "server/management/agent-settings-routes", mutates: true },
|
|
128
|
+
{ method: "POST", path: "/api/grok/apply", module: "server/management/agent-settings-routes", mutates: true },
|
|
129
|
+
{ method: "PUT", path: "/api/claude-code", module: "server/management/agent-settings-routes", mutates: true },
|
|
130
|
+
{ method: "PUT", path: "/api/claude-desktop", module: "server/management/agent-settings-routes", mutates: true },
|
|
131
|
+
{ method: "PUT", path: "/api/codex-auth/features/default-mode-request-user-input", module: "server/management/agent-settings-routes", mutates: true },
|
|
132
|
+
{ method: "PUT", path: "/api/effort-caps", module: "server/management/agent-settings-routes", mutates: true },
|
|
133
|
+
{ method: "PUT", path: "/api/grok/selection", module: "server/management/agent-settings-routes", mutates: true },
|
|
134
|
+
{ method: "PUT", path: "/api/injection-model", module: "server/management/agent-settings-routes", mutates: true },
|
|
135
|
+
{ method: "PUT", path: "/api/subagent-model-fallback", module: "server/management/agent-settings-routes", mutates: true },
|
|
136
|
+
{ method: "PUT", path: "/api/subagent-models", module: "server/management/agent-settings-routes", mutates: true },
|
|
137
|
+
{ method: "PUT", path: "/api/v2", module: "server/management/agent-settings-routes", mutates: true },
|
|
138
|
+
// server/management/codex-prompt-routes
|
|
139
|
+
{ method: "GET", path: "/api/codex-prompt", module: "server/management/codex-prompt-routes", mutates: false },
|
|
140
|
+
{ method: "GET", path: "/api/codex-prompt/text", module: "server/management/codex-prompt-routes", mutates: false },
|
|
141
|
+
{ method: "POST", path: "/api/codex-prompt/adopt", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Prompt adoption requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
142
|
+
{ method: "POST", path: "/api/codex-prompt/repair", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Prompt repair requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
143
|
+
{ method: "PUT", path: "/api/codex-prompt/base", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Base prompt write requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
144
|
+
{ method: "PUT", path: "/api/codex-prompt/base/select", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Base prompt selection requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
145
|
+
{ method: "PUT", path: "/api/codex-prompt/custom", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Custom prompt write requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
146
|
+
{ method: "PUT", path: "/api/codex-prompt/toggle", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Prompt toggle requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
147
|
+
// server/management/combo-routes
|
|
148
|
+
{ method: "DELETE", path: "/api/combos", module: "server/management/combo-routes", mutates: true },
|
|
149
|
+
{ method: "GET", path: "/api/combos", module: "server/management/combo-routes", mutates: false },
|
|
150
|
+
{ method: "PUT", path: "/api/combos", module: "server/management/combo-routes", mutates: true },
|
|
151
|
+
// server/management/config-routes
|
|
152
|
+
{ method: "GET", path: "/api/config", module: "server/management/config-routes", mutates: false },
|
|
153
|
+
{ method: "GET", path: "/api/diagnostics/project-config", module: "server/management/config-routes", mutates: false },
|
|
154
|
+
{ method: "GET", path: "/api/settings", module: "server/management/config-routes", mutates: false },
|
|
155
|
+
{ method: "GET", path: "/api/shadow-call-settings", module: "server/management/config-routes", mutates: false },
|
|
156
|
+
{ method: "GET", path: "/api/sidecar-settings", module: "server/management/config-routes", mutates: false },
|
|
157
|
+
{ method: "GET", path: "/api/startup-health", module: "server/management/config-routes", mutates: false },
|
|
158
|
+
{ method: "GET", path: "/api/update/check", module: "server/management/config-routes", mutates: false },
|
|
159
|
+
{ method: "GET", path: "/api/update/status", module: "server/management/config-routes", mutates: false },
|
|
160
|
+
{ method: "GET", path: "/api/windows-tray", module: "server/management/config-routes", mutates: false },
|
|
161
|
+
{ method: "POST", path: "/api/startup-action", module: "server/management/config-routes", mutates: true },
|
|
162
|
+
{ method: "POST", path: "/api/sync", module: "server/management/config-routes", mutates: true },
|
|
163
|
+
{ method: "POST", path: "/api/update/run", module: "server/management/config-routes", mutates: true },
|
|
164
|
+
{ method: "POST", path: "/api/windows-tray", module: "server/management/config-routes", mutates: true },
|
|
165
|
+
{ method: "PUT", path: "/api/config", module: "server/management/config-routes", mutates: true, exempt: { reason: "disabled", why: "Returns 405 by design; provider changes go through POST /api/providers." } },
|
|
166
|
+
{ method: "PUT", path: "/api/settings", module: "server/management/config-routes", mutates: true },
|
|
167
|
+
{ method: "PUT", path: "/api/shadow-call-settings", module: "server/management/config-routes", mutates: true },
|
|
168
|
+
{ method: "PUT", path: "/api/sidecar-settings", module: "server/management/config-routes", mutates: true },
|
|
169
|
+
// server/management/integration-routes
|
|
170
|
+
{ method: "GET", path: "/api/client-integrations", module: "server/management/integration-routes", mutates: false },
|
|
171
|
+
{ method: "GET", path: "/api/client-integrations/journal", module: "server/management/integration-routes", mutates: false },
|
|
172
|
+
{ method: "POST", path: "/api/client-integrations/restore", module: "server/management/integration-routes", mutates: true },
|
|
173
|
+
// server/management/lab-automation-routes
|
|
174
|
+
{ method: "GET", path: "/api/lab/automation", module: "server/management/lab-automation-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
175
|
+
{ method: "GET", path: "/api/lab/automation/runs", module: "server/management/lab-automation-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
176
|
+
{ method: "POST", path: "/api/lab/automation/run", module: "server/management/lab-automation-routes", mutates: true, exempt: { reason: "deferred-verb", why: "Lab automation run has no CLI verb yet. A local SQLite read cannot drive it, so local-transport does not apply.", owner: "wp7", ownerDoc: "devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md" } },
|
|
177
|
+
{ method: "PUT", path: "/api/lab/automation", module: "server/management/lab-automation-routes", mutates: true, exempt: { reason: "deferred-verb", why: "Lab automation config update has no CLI verb yet. A local SQLite read cannot drive it, so local-transport does not apply.", owner: "wp7", ownerDoc: "devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md" } },
|
|
178
|
+
// server/management/lab-routes
|
|
179
|
+
{ method: "GET", path: "/api/lab/artifacts", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
180
|
+
{ method: "GET", path: "/api/lab/catalog", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
181
|
+
{ method: "GET", path: "/api/lab/events", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
182
|
+
{ method: "GET", path: "/api/lab/observations", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
183
|
+
{ method: "GET", path: "/api/lab/production-signals", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
184
|
+
{ method: "GET", path: "/api/lab/public/community", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
185
|
+
{ method: "GET", path: "/api/lab/status", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
186
|
+
{ method: "GET", path: "/api/lab/subjects", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
187
|
+
{ method: "GET", path: "/api/lab/verdicts", module: "server/management/lab-routes", mutates: false, exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
188
|
+
{ method: "POST", path: "/api/lab/public/community/import", module: "server/management/lab-routes", mutates: true, exempt: { reason: "deferred-verb", why: "Community evidence import has no CLI verb yet. A local SQLite read cannot drive it, so local-transport does not apply.", owner: "wp7", ownerDoc: "devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md" } },
|
|
189
|
+
{ method: "POST", path: "/api/lab/public/export", module: "server/management/lab-routes", mutates: true, exempt: { reason: "deferred-verb", why: "Public evidence export has no CLI verb yet. A local SQLite read cannot drive it, so local-transport does not apply.", owner: "wp7", ownerDoc: "devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md" } },
|
|
190
|
+
{ method: "POST", path: "/api/lab/public/preview", module: "server/management/lab-routes", mutates: true, exempt: { reason: "deferred-verb", why: "Public evidence preview has no CLI verb yet. A local SQLite read cannot drive it, so local-transport does not apply.", owner: "wp7", ownerDoc: "devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md" } },
|
|
191
|
+
{ method: "POST", path: "/api/lab/public/verify", module: "server/management/lab-routes", mutates: true, exempt: { reason: "deferred-verb", why: "Public evidence verification has no CLI verb yet. A local SQLite read cannot drive it, so local-transport does not apply.", owner: "wp7", ownerDoc: "devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md" } },
|
|
192
|
+
// server/management/logs-usage-routes
|
|
193
|
+
{ method: "GET", path: "/api/claude/inbound-debug", module: "server/management/logs-usage-routes", mutates: false },
|
|
194
|
+
{ method: "GET", path: "/api/debug", module: "server/management/logs-usage-routes", mutates: false },
|
|
195
|
+
{ method: "GET", path: "/api/debug/injection-logs", module: "server/management/logs-usage-routes", mutates: false },
|
|
196
|
+
{ method: "GET", path: "/api/debug/logs", module: "server/management/logs-usage-routes", mutates: false },
|
|
197
|
+
{ method: "GET", path: "/api/debug/usage-logs", module: "server/management/logs-usage-routes", mutates: false },
|
|
198
|
+
{ method: "GET", path: "/api/logs", module: "server/management/logs-usage-routes", mutates: false },
|
|
199
|
+
{ method: "GET", path: "/api/storage/cleanup-policy", module: "server/management/logs-usage-routes", mutates: false },
|
|
200
|
+
{ method: "GET", path: "/api/storage/cleanup-policy/test-stream", module: "server/management/logs-usage-routes", mutates: false, exempt: { reason: "test-seam", why: "Opt-in streaming seam declared at src/storage/policy-job.ts:71." } },
|
|
201
|
+
{ method: "GET", path: "/api/storage/trash", module: "server/management/logs-usage-routes", mutates: false },
|
|
202
|
+
{ method: "GET", path: "/api/storage/trash/restore/test-stream", module: "server/management/logs-usage-routes", mutates: false, exempt: { reason: "test-seam", why: "Opt-in streaming seam declared at src/storage/restore-job.ts:34." } },
|
|
203
|
+
{ method: "GET", path: "/api/usage", module: "server/management/logs-usage-routes", mutates: false },
|
|
204
|
+
{ method: "POST", path: "/api/storage/cleanup", module: "server/management/logs-usage-routes", mutates: true },
|
|
205
|
+
{ method: "POST", path: "/api/storage/cleanup-policy/run", module: "server/management/logs-usage-routes", mutates: true },
|
|
206
|
+
{ method: "POST", path: "/api/storage/cleanup/preview", module: "server/management/logs-usage-routes", mutates: true },
|
|
207
|
+
{ method: "POST", path: "/api/storage/trash/restore", module: "server/management/logs-usage-routes", mutates: true },
|
|
208
|
+
{ method: "PUT", path: "/api/debug", module: "server/management/logs-usage-routes", mutates: true },
|
|
209
|
+
{ method: "PUT", path: "/api/storage/cleanup-policy", module: "server/management/logs-usage-routes", mutates: true },
|
|
210
|
+
// server/management/model-routes
|
|
211
|
+
{ method: "GET", path: "/api/aliases", module: "server/management/model-routes", mutates: false },
|
|
212
|
+
{ method: "GET", path: "/api/catalog", module: "server/management/model-routes", mutates: false },
|
|
213
|
+
{ method: "GET", path: "/api/client-config", module: "server/management/model-routes", mutates: false },
|
|
214
|
+
{ method: "GET", path: "/api/custom-models", module: "server/management/model-routes", mutates: false },
|
|
215
|
+
{ method: "GET", path: "/api/model-discovery", module: "server/management/model-routes", mutates: false },
|
|
216
|
+
{ method: "GET", path: "/api/model-presets", module: "server/management/model-routes", mutates: false },
|
|
217
|
+
{ method: "GET", path: "/api/models", module: "server/management/model-routes", mutates: false },
|
|
218
|
+
{ method: "GET", path: "/api/selected-models", module: "server/management/model-routes", mutates: false },
|
|
219
|
+
{ method: "POST", path: "/api/custom-models", module: "server/management/model-routes", mutates: true },
|
|
220
|
+
{ method: "POST", path: "/api/model-discovery/acknowledge", module: "server/management/model-routes", mutates: true },
|
|
221
|
+
{ method: "PUT", path: "/api/default-aliases", module: "server/management/model-routes", mutates: true },
|
|
222
|
+
{ method: "PUT", path: "/api/disabled-models", module: "server/management/model-routes", mutates: true },
|
|
223
|
+
{ method: "PUT", path: "/api/model-discovery", module: "server/management/model-routes", mutates: true },
|
|
224
|
+
{ method: "PUT", path: "/api/model-presets", module: "server/management/model-routes", mutates: true },
|
|
225
|
+
{ method: "PUT", path: "/api/model-visibility", module: "server/management/model-routes", mutates: true },
|
|
226
|
+
{ method: "PUT", path: "/api/selected-models", module: "server/management/model-routes", mutates: true },
|
|
227
|
+
// server/management/native-integration-routes
|
|
228
|
+
{ method: "GET", path: "/api/native-integrations", module: "server/management/native-integration-routes", mutates: false },
|
|
229
|
+
{ method: "PUT", path: "/api/native-integrations/claude", module: "server/management/native-integration-routes", mutates: true },
|
|
230
|
+
{ method: "PUT", path: "/api/native-integrations/claude-desktop", module: "server/management/native-integration-routes", mutates: true },
|
|
231
|
+
{ method: "PUT", path: "/api/native-integrations/codex", module: "server/management/native-integration-routes", mutates: true },
|
|
232
|
+
{ method: "PUT", path: "/api/native-integrations/grok", module: "server/management/native-integration-routes", mutates: true },
|
|
233
|
+
// server/management/oauth-account-routes
|
|
234
|
+
{ method: "DELETE", path: "/api/keys", module: "server/management/oauth-account-routes", mutates: true },
|
|
235
|
+
{ method: "DELETE", path: "/api/oauth/accounts", module: "server/management/oauth-account-routes", mutates: true },
|
|
236
|
+
{ method: "DELETE", path: "/api/providers/keys", module: "server/management/oauth-account-routes", mutates: true },
|
|
237
|
+
{ method: "GET", path: "/api/key-providers", module: "server/management/oauth-account-routes", mutates: false },
|
|
238
|
+
{ method: "GET", path: "/api/keys", module: "server/management/oauth-account-routes", mutates: false },
|
|
239
|
+
{ method: "GET", path: "/api/oauth/accounts", module: "server/management/oauth-account-routes", mutates: false },
|
|
240
|
+
{ method: "GET", path: "/api/oauth/accounts/pool", module: "server/management/oauth-account-routes", mutates: false },
|
|
241
|
+
{ method: "GET", path: "/api/oauth/providers", module: "server/management/oauth-account-routes", mutates: false },
|
|
242
|
+
{ method: "GET", path: "/api/oauth/status", module: "server/management/oauth-account-routes", mutates: false },
|
|
243
|
+
{ method: "GET", path: "/api/providers/keys", module: "server/management/oauth-account-routes", mutates: false },
|
|
244
|
+
{ method: "PATCH", path: "/api/keys", module: "server/management/oauth-account-routes", mutates: true },
|
|
245
|
+
{ method: "PATCH", path: "/api/oauth/accounts/pool", module: "server/management/oauth-account-routes", mutates: true },
|
|
246
|
+
{ method: "POST", path: "/api/keys", module: "server/management/oauth-account-routes", mutates: true },
|
|
247
|
+
{ method: "POST", path: "/api/oauth/accounts/clear-cooldown", module: "server/management/oauth-account-routes", mutates: true },
|
|
248
|
+
{ method: "POST", path: "/api/oauth/accounts/import", module: "server/management/oauth-account-routes", mutates: true },
|
|
249
|
+
{ method: "POST", path: "/api/oauth/login", module: "server/management/oauth-account-routes", mutates: true },
|
|
250
|
+
{ method: "POST", path: "/api/oauth/login/cancel", module: "server/management/oauth-account-routes", mutates: true },
|
|
251
|
+
{ method: "POST", path: "/api/oauth/login/code", module: "server/management/oauth-account-routes", mutates: true },
|
|
252
|
+
{ method: "POST", path: "/api/oauth/logout", module: "server/management/oauth-account-routes", mutates: true },
|
|
253
|
+
{ method: "POST", path: "/api/providers/keys", module: "server/management/oauth-account-routes", mutates: true },
|
|
254
|
+
{ method: "PUT", path: "/api/oauth/accounts/active", module: "server/management/oauth-account-routes", mutates: true },
|
|
255
|
+
{ method: "PUT", path: "/api/oauth/accounts/alias", module: "server/management/oauth-account-routes", mutates: true },
|
|
256
|
+
{ method: "PUT", path: "/api/oauth/accounts/pool", module: "server/management/oauth-account-routes", mutates: true },
|
|
257
|
+
{ method: "PUT", path: "/api/providers/keys/active", module: "server/management/oauth-account-routes", mutates: true },
|
|
258
|
+
{ method: "PUT", path: "/api/providers/keys/alias", module: "server/management/oauth-account-routes", mutates: true },
|
|
259
|
+
// server/management/provider-routes
|
|
260
|
+
{ method: "DELETE", path: "/api/providers", module: "server/management/provider-routes", mutates: true },
|
|
261
|
+
{ method: "GET", path: "/api/provider-context-caps", module: "server/management/provider-routes", mutates: false },
|
|
262
|
+
{ method: "GET", path: "/api/provider-presets", module: "server/management/provider-routes", mutates: false },
|
|
263
|
+
{ method: "GET", path: "/api/provider-quotas", module: "server/management/provider-routes", mutates: false },
|
|
264
|
+
{ method: "GET", path: "/api/provider-request-pacing", module: "server/management/provider-routes", mutates: false },
|
|
265
|
+
{ method: "GET", path: "/api/providers", module: "server/management/provider-routes", mutates: false },
|
|
266
|
+
{ method: "PATCH", path: "/api/providers", module: "server/management/provider-routes", mutates: true },
|
|
267
|
+
{ method: "POST", path: "/api/providers", module: "server/management/provider-routes", mutates: true },
|
|
268
|
+
{ method: "POST", path: "/api/providers/test", module: "server/management/provider-routes", mutates: true },
|
|
269
|
+
{ method: "PUT", path: "/api/provider-context-caps", module: "server/management/provider-routes", mutates: true },
|
|
270
|
+
// server/management/request-history-routes
|
|
271
|
+
{ method: "GET", path: "/api/request-history", module: "server/management/request-history-routes", mutates: false },
|
|
272
|
+
// server/management/routing-analytics-routes
|
|
273
|
+
// server/management/routing-profile-routes
|
|
274
|
+
{ method: "DELETE", path: "/api/routing-profiles", module: "server/management/routing-profile-routes", mutates: true },
|
|
275
|
+
{ method: "GET", path: "/api/routing-profiles", module: "server/management/routing-profile-routes", mutates: false },
|
|
276
|
+
{ method: "POST", path: "/api/routing-profiles/dry-run", module: "server/management/routing-profile-routes", mutates: true },
|
|
277
|
+
{ method: "PUT", path: "/api/routing-profiles", module: "server/management/routing-profile-routes", mutates: true },
|
|
278
|
+
// server/management/sidebar-routes
|
|
279
|
+
{ method: "GET", path: "/api/github/star", module: "server/management/sidebar-routes", mutates: false },
|
|
280
|
+
{ method: "GET", path: "/api/update/badge", module: "server/management/sidebar-routes", mutates: false },
|
|
281
|
+
{ method: "POST", path: "/api/github/star", module: "server/management/sidebar-routes", mutates: true, exempt: { reason: "session-only", why: "User-consent boundary in AGENTS_INSTALL.md: starring spends the user's identity. Must never gain a CLI verb." } },
|
|
282
|
+
// server/management/storage-log-guard-routes
|
|
283
|
+
{ method: "GET", path: "/api/storage/codex-logs", module: "server/management/storage-log-guard-routes", mutates: false },
|
|
284
|
+
{ method: "POST", path: "/api/storage/codex-logs/compact", module: "server/management/storage-log-guard-routes", mutates: true },
|
|
285
|
+
{ method: "POST", path: "/api/storage/codex-logs/protect", module: "server/management/storage-log-guard-routes", mutates: true },
|
|
286
|
+
{ method: "POST", path: "/api/storage/codex-logs/repair", module: "server/management/storage-log-guard-routes", mutates: true },
|
|
287
|
+
{ method: "POST", path: "/api/storage/codex-logs/unprotect", module: "server/management/storage-log-guard-routes", mutates: true },
|
|
288
|
+
// server/management/system-routes
|
|
289
|
+
{ method: "GET", path: "/api/system/memory", module: "server/management/system-routes", mutates: false },
|
|
290
|
+
{ method: "GET", path: "/api/system/windows-replace-retries", module: "server/management/system-routes", mutates: false },
|
|
291
|
+
{ method: "POST", path: "/api/system/restart", module: "server/management/system-routes", mutates: true },
|
|
292
|
+
// --- Routes an equality scan of their own file cannot see (18). ---
|
|
293
|
+
// Each carries `mechanism`; the reconciliation test counts these separately.
|
|
294
|
+
{ method: "GET", path: "/api/storage", module: "server/management/storage-log-guard-routes", mutates: false, mechanism: "negated-guard" },
|
|
295
|
+
{ method: "GET", path: "/api/routing-analytics", module: "server/management/routing-analytics-routes", mutates: false, mechanism: "negated-guard" },
|
|
296
|
+
{ method: "GET", path: "/api/system/codex-app-server", module: "server/management/system-routes", mutates: false, mechanism: "path-constant" },
|
|
297
|
+
{ method: "POST", path: "/api/system/codex-restart", module: "server/management/system-routes", mutates: true, mechanism: "path-constant" },
|
|
298
|
+
{ method: "POST", path: "/api/providers/reload", module: "server/management/provider-routes", mutates: true, mechanism: "path-constant", exempt: { reason: "capability-principal", why: "Gated on the local-provider-reload-capability principal (provider-routes.ts:467), not an operator action." } },
|
|
299
|
+
{ method: "GET", path: "/api/client-integrations/{clientId}", module: "server/management/integration-routes", mutates: false, mechanism: "prefix-decode" },
|
|
300
|
+
{ method: "PUT", path: "/api/client-integrations/{clientId}", module: "server/management/integration-routes", mutates: true, mechanism: "prefix-decode" },
|
|
301
|
+
{ method: "GET", path: "/api/request-history/{id}", module: "server/management/request-history-routes", mutates: false, mechanism: "slice" },
|
|
302
|
+
{ method: "GET", path: "/api/request-history/{id}/route-decision", module: "server/management/request-history-routes", mutates: false, mechanism: "ends-with" },
|
|
303
|
+
{ method: "PUT", path: "/api/providers/{provider}/alias", module: "server/management/model-routes", mutates: true, mechanism: "regex" },
|
|
304
|
+
{ method: "PUT", path: "/api/providers/{provider}/model-aliases", module: "server/management/model-routes", mutates: true, mechanism: "regex" },
|
|
305
|
+
{ method: "PUT", path: "/api/custom-models/{id}", module: "server/management/model-routes", mutates: true, mechanism: "regex" },
|
|
306
|
+
{ method: "DELETE", path: "/api/custom-models/{id}", module: "server/management/model-routes", mutates: true, mechanism: "regex" },
|
|
307
|
+
{ method: "GET", path: "/api/lab/subjects/{id}", module: "server/management/lab-routes", mutates: false, mechanism: "regex", exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
308
|
+
{ method: "GET", path: "/api/lab/events/{id}", module: "server/management/lab-routes", mutates: false, mechanism: "regex", exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
309
|
+
{ method: "GET", path: "/api/lab/artifacts/{digest}", module: "server/management/lab-routes", mutates: false, mechanism: "regex", exempt: { reason: "local-transport", why: "ocx lab reads the same rows from the local SQLite projection; src/cli/lab.ts imports ../lab/query directly and never fetches /api/lab." } },
|
|
310
|
+
{ method: "POST", path: "/api/lab/automation/runs/{id}/cancel", module: "server/management/lab-automation-routes", mutates: true, mechanism: "regex", exempt: { reason: "deferred-verb", why: "Lab automation run cancellation has no CLI verb yet. A local SQLite read cannot drive it, so local-transport does not apply.", owner: "wp7", ownerDoc: "devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md" } },
|
|
311
|
+
];
|
|
@@ -68,6 +68,14 @@ export interface LiveProxy {
|
|
|
68
68
|
hostname?: string;
|
|
69
69
|
/** Whether the successful probe used runtime-port metadata or the configured listen port. */
|
|
70
70
|
source: "runtime" | "config";
|
|
71
|
+
/**
|
|
72
|
+
* Version the live proxy reported on `/healthz`, when it reported one.
|
|
73
|
+
*
|
|
74
|
+
* Carried so a stale `ocx` on PATH can be detected without a second request: the
|
|
75
|
+
* identity probe already parsed and validated this body. Absent for a legacy proxy whose
|
|
76
|
+
* healthz body predates the field.
|
|
77
|
+
*/
|
|
78
|
+
version?: string;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
/**
|
|
@@ -99,7 +107,7 @@ export async function proxyIdentityAt(
|
|
|
99
107
|
port: number,
|
|
100
108
|
opts: { hostname?: string; expectedPid?: number } = {},
|
|
101
109
|
io: LivenessIo = {},
|
|
102
|
-
): Promise<{ pid: number | null } | null> {
|
|
110
|
+
): Promise<{ pid: number | null; version?: string } | null> {
|
|
103
111
|
const fetchFn = io.fetchFn ?? directLocalHttpFetch;
|
|
104
112
|
const sleepFn = io.sleepFn ?? ((ms: number) => new Promise<void>(r => setTimeout(r, ms)));
|
|
105
113
|
const nowFn = io.nowFn ?? Date.now;
|
|
@@ -122,7 +130,9 @@ export async function proxyIdentityAt(
|
|
|
122
130
|
if (!isOpencodexHealthz(body)) return null;
|
|
123
131
|
const pid = typeof body?.pid === "number" ? body.pid : null;
|
|
124
132
|
if (opts.expectedPid !== undefined && pid !== null && pid !== opts.expectedPid) return null;
|
|
125
|
-
|
|
133
|
+
// Guarded the same way `pid` is: a non-string version is absent, not coerced.
|
|
134
|
+
const version = typeof body?.version === "string" ? body.version : undefined;
|
|
135
|
+
return version === undefined ? { pid } : { pid, version };
|
|
126
136
|
} catch {
|
|
127
137
|
// Transport failure (timeout / refused) — retry while budget remains; a proxy that
|
|
128
138
|
// has only just begun listening can miss a single short probe (#764).
|
|
@@ -180,7 +190,13 @@ export async function findLiveProxy(io: LivenessIo = {}): Promise<LiveProxy | nu
|
|
|
180
190
|
// healthz confirmed the pid itself → trusted; a pidless legacy body did not,
|
|
181
191
|
// so the cheap pid must pass full identity verification before it is returned.
|
|
182
192
|
const trusted = identity.pid === pid ? pid : killablePid(pid);
|
|
183
|
-
return {
|
|
193
|
+
return {
|
|
194
|
+
pid: trusted,
|
|
195
|
+
port: runtime.port,
|
|
196
|
+
hostname: runtime.hostname,
|
|
197
|
+
source: "runtime",
|
|
198
|
+
...(identity.version === undefined ? {} : { version: identity.version }),
|
|
199
|
+
};
|
|
184
200
|
}
|
|
185
201
|
}
|
|
186
202
|
}
|
|
@@ -197,7 +213,13 @@ export async function findLiveProxy(io: LivenessIo = {}): Promise<LiveProxy | nu
|
|
|
197
213
|
// (its process dead, the port reused by a pidless legacy proxy) — synthesizing it
|
|
198
214
|
// would hand destructive callers (stopProxy → kill fallback) a reusable pid.
|
|
199
215
|
if (identity) {
|
|
200
|
-
return {
|
|
216
|
+
return {
|
|
217
|
+
pid: verifiedReportedPid(identity.pid),
|
|
218
|
+
port: record.port,
|
|
219
|
+
hostname: record.hostname,
|
|
220
|
+
source: "runtime",
|
|
221
|
+
...(identity.version === undefined ? {} : { version: identity.version }),
|
|
222
|
+
};
|
|
201
223
|
}
|
|
202
224
|
}
|
|
203
225
|
|
|
@@ -211,6 +233,7 @@ export async function findLiveProxy(io: LivenessIo = {}): Promise<LiveProxy | nu
|
|
|
211
233
|
port,
|
|
212
234
|
hostname: config.hostname,
|
|
213
235
|
source: "config",
|
|
236
|
+
...(identity.version === undefined ? {} : { version: identity.version }),
|
|
214
237
|
};
|
|
215
238
|
}
|
|
216
239
|
return null;
|
|
@@ -63,6 +63,13 @@ export interface RequestLogContext {
|
|
|
63
63
|
* product: widening that enum would merge Responses and Chat Completions,
|
|
64
64
|
* since both leave it undefined. */
|
|
65
65
|
inboundProtocol?: "responses" | "chat" | "messages";
|
|
66
|
+
/**
|
|
67
|
+
* Set when an adapter answered the turn locally and no upstream request was made
|
|
68
|
+
* (`ProviderAdapter.localTerminal`). A fixed identifier naming the code path, never
|
|
69
|
+
* conversation-derived: it exists so a request log showing zero sends is explainable
|
|
70
|
+
* rather than looking like a lost request.
|
|
71
|
+
*/
|
|
72
|
+
localTerminalReason?: string;
|
|
66
73
|
/** Stable non-PII Codex Pool account identity for durable usage attribution. */
|
|
67
74
|
accountLogLabel?: string;
|
|
68
75
|
requestedModel?: string;
|
|
@@ -133,6 +140,12 @@ export interface RequestLogEntry {
|
|
|
133
140
|
/** TTFT: ms from request start to the first non-empty model output delta; unset for non-streaming/tool-only. */
|
|
134
141
|
firstOutputMs?: number;
|
|
135
142
|
surface?: "claude" | "claude-desktop" | "grok";
|
|
143
|
+
/**
|
|
144
|
+
* Set when the proxy answered this turn locally and sent nothing upstream. Without it a zero-send
|
|
145
|
+
* row is indistinguishable from a request that vanished. A fixed adapter-supplied identifier,
|
|
146
|
+
* never conversation-derived.
|
|
147
|
+
*/
|
|
148
|
+
localTerminalReason?: string;
|
|
136
149
|
/** The matched configured key's id. Set ONLY for admissionKind "configured" —
|
|
137
150
|
* never a sentinel, so a hand-edited entry whose id happens to be "loopback"
|
|
138
151
|
* cannot absorb unrelated traffic. */
|
|
@@ -942,6 +955,7 @@ export function addFinalRequestLog(
|
|
|
942
955
|
logCtx.usage,
|
|
943
956
|
logCtx.usageLogInputTokens,
|
|
944
957
|
contextWindowForModel(logCtx.providerAdapter ?? logCtx.provider, logCtx.model),
|
|
958
|
+
logCtx.localTerminalReason !== undefined,
|
|
945
959
|
);
|
|
946
960
|
const attempts = logCtx.attempts?.map(attempt => ({
|
|
947
961
|
...attempt,
|
|
@@ -969,6 +983,9 @@ export function addFinalRequestLog(
|
|
|
969
983
|
...(logCtx.apiKeyId ? { apiKeyId: logCtx.apiKeyId } : {}),
|
|
970
984
|
...(logCtx.admissionKind ? { admissionKind: logCtx.admissionKind } : {}),
|
|
971
985
|
...(logCtx.inboundProtocol ? { inboundProtocol: logCtx.inboundProtocol } : {}),
|
|
986
|
+
...(logCtx.localTerminalReason
|
|
987
|
+
? { localTerminalReason: sanitizeLogMetadataString(logCtx.localTerminalReason) }
|
|
988
|
+
: {}),
|
|
972
989
|
...(isCodexUsageAccountLogLabel(logCtx.accountLogLabel)
|
|
973
990
|
? { accountLogLabel: logCtx.accountLogLabel }
|
|
974
991
|
: {}),
|
|
@@ -1033,6 +1050,15 @@ export function filterRequestLogs(logs: RequestLogEntry[], params: URLSearchPara
|
|
|
1033
1050
|
if (conversationId) {
|
|
1034
1051
|
filtered = filtered.filter(entry => matchesLogConversationId(entry.conversationId, conversationId));
|
|
1035
1052
|
}
|
|
1053
|
+
// #2704: there was no `model` clause at all, so `?model=x` was ACCEPTED and silently
|
|
1054
|
+
// ignored -- worse than an error, because it yields wrong conclusions from output that
|
|
1055
|
+
// looks correct. Attempts are matched for the same reason `provider` matches them: a
|
|
1056
|
+
// request that failed over should be findable by the model that actually served it.
|
|
1057
|
+
const model = params.get("model")?.trim();
|
|
1058
|
+
if (model) {
|
|
1059
|
+
filtered = filtered.filter(entry => entry.model === model
|
|
1060
|
+
|| entry.attempts?.some(attempt => attempt.model === model));
|
|
1061
|
+
}
|
|
1036
1062
|
const status = params.get("status")?.trim().toLowerCase();
|
|
1037
1063
|
if (status) {
|
|
1038
1064
|
filtered = /^[1-5]xx$/.test(status)
|
|
@@ -1102,6 +1128,7 @@ function finalizedUsage(
|
|
|
1102
1128
|
usage: OcxUsage | undefined,
|
|
1103
1129
|
inputTokenEstimate: number | undefined,
|
|
1104
1130
|
contextWindow: number | undefined,
|
|
1131
|
+
locallyAnswered = false,
|
|
1105
1132
|
): FinalizedUsageResult {
|
|
1106
1133
|
// The ESTIMATE itself is capped at the model's context window (codex-router PR #140). The
|
|
1107
1134
|
// combined value below keeps its max(inputTokens, estimate) behavior — a provider-reported
|
|
@@ -1111,7 +1138,7 @@ function finalizedUsage(
|
|
|
1111
1138
|
&& inputTokenEstimate >= 0
|
|
1112
1139
|
? capEstimateAtContextWindow(inputTokenEstimate, contextWindow)
|
|
1113
1140
|
: undefined;
|
|
1114
|
-
const finalUsage = usageForFinalLog(adapter, usage);
|
|
1141
|
+
const finalUsage = usageForFinalLog(adapter, usage, locallyAnswered);
|
|
1115
1142
|
const usageFallback = !finalUsage && estimate !== undefined
|
|
1116
1143
|
? { inputTokens: estimate, outputTokens: 0, estimated: true }
|
|
1117
1144
|
: undefined;
|
|
@@ -1211,6 +1238,7 @@ export function finishRequestAttempt(
|
|
|
1211
1238
|
usage ?? attempt.usage,
|
|
1212
1239
|
attempt.inputTokenEstimate,
|
|
1213
1240
|
contextWindowForModel(attempt.adapter, attempt.model),
|
|
1241
|
+
attempt.locallyAnswered === true,
|
|
1214
1242
|
);
|
|
1215
1243
|
attempt.status = status;
|
|
1216
1244
|
attempt.durationMs = Math.max(0, durationMs);
|