@quantiya/codevibe-core 2.0.6 → 2.0.7
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/README.md +7 -5
- package/dist/agy-cli-contract.d.ts +14 -0
- package/dist/appsync/appsync-client.d.ts +59 -31
- package/dist/appsync/event-throttle.d.ts +33 -0
- package/dist/appsync/index.d.ts +3 -2
- package/dist/appsync/queries.d.ts +4 -2
- package/dist/auth/auth-telemetry.d.ts +26 -0
- package/dist/auth/vendor-key-cli.d.ts +49 -0
- package/dist/companion-mode/index.d.ts +36 -22
- package/dist/companion-mode/persist-preference.d.ts +1 -5
- package/dist/companion-mode/resolve-agent.d.ts +5 -4
- package/dist/config/config.d.ts +0 -5
- package/dist/credential-broker/__tests__/upstream-result-helpers.d.ts +21 -0
- package/dist/credential-broker/broker.d.ts +196 -12
- package/dist/credential-broker/chatgpt-config.d.ts +40 -0
- package/dist/credential-broker/index.d.ts +4 -1
- package/dist/credential-broker/oauth-account-lock.d.ts +104 -0
- package/dist/credential-broker/oauth-refresh.d.ts +72 -0
- package/dist/credential-broker/types.d.ts +165 -17
- package/dist/credential-broker/upstream-client.d.ts +3 -2
- package/dist/credential-broker/vendor-key-store.d.ts +32 -1
- package/dist/crypto/__tests__/encrypt-for-emit.test.d.ts +1 -0
- package/dist/crypto/encrypt-for-emit.d.ts +39 -0
- package/dist/crypto/index.d.ts +2 -0
- package/dist/daemon/__tests__/process-guards.test.d.ts +1 -0
- package/dist/daemon/index.d.ts +2 -0
- package/dist/daemon/process-guards.d.ts +38 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +308 -281
- package/dist/keychain/keychain-manager.d.ts +25 -4
- package/dist/local-executor/__tests__/reviewer-rationale-flag.test.d.ts +1 -0
- package/dist/local-executor/class-a-sign.d.ts +15 -0
- package/dist/local-executor/group-decision-outcome.d.ts +11 -0
- package/dist/local-executor/implementor-argv.d.ts +89 -7
- package/dist/local-executor/local-executor-impl.d.ts +18 -7
- package/dist/local-executor/merge-conflict-resolve.d.ts +19 -0
- package/dist/local-executor/revert-reconcile.d.ts +12 -0
- package/dist/local-executor/reviewer-rationale-flag.d.ts +10 -0
- package/dist/local-executor/types.d.ts +1 -0
- package/dist/local-model/manager.d.ts +1 -1
- package/dist/local-model/ollama.d.ts +40 -0
- package/dist/orchestration/__tests__/detect-agents.test.d.ts +1 -0
- package/dist/orchestration/detect-agents.d.ts +10 -30
- package/dist/orchestration/index.d.ts +1 -2
- package/dist/orchestration-shell/__tests__/a1a-planner-offers.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/a6-cli-surface.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/advisory-attachments-dir-race.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/advisory-multi-mention-panel.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/advisory-panel-attachments.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/advisory-quota-stdout-result.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/agent-turn-context-batch.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/bounded-shutdown.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/brainstorm-quorum-wiring.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/brainstorm-quorum.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/cli-team-session-key.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/command-intent.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/context-compaction.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/context-items-hosted-sync.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/context-items-wiring.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/context-items.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/cp8-full-team-events.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/cp8-team-decomposed-zk.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/explicit-tty-exit.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/mention-suggest.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/route-browse-summarize-recovery.test.d.ts +1 -0
- package/dist/orchestration-shell/attachments.d.ts +58 -2
- package/dist/orchestration-shell/bounded-shutdown.d.ts +16 -0
- package/dist/orchestration-shell/brainstorm-quorum.d.ts +118 -0
- package/dist/orchestration-shell/cli.d.ts +97 -70
- package/dist/orchestration-shell/cli.js +30990 -22426
- package/dist/orchestration-shell/command-intent.d.ts +56 -0
- package/dist/orchestration-shell/components/InputBar.d.ts +6 -0
- package/dist/orchestration-shell/components/OrchestrationApp.d.ts +2 -0
- package/dist/orchestration-shell/context-compaction.d.ts +264 -0
- package/dist/orchestration-shell/context-items-hosted-sync.d.ts +19 -0
- package/dist/orchestration-shell/context-items.d.ts +408 -0
- package/dist/orchestration-shell/context-store.d.ts +162 -0
- package/dist/orchestration-shell/emit-shell-event.d.ts +34 -13
- package/dist/orchestration-shell/explicit-tty-exit.d.ts +18 -0
- package/dist/orchestration-shell/gate-prompts.d.ts +3 -3
- package/dist/orchestration-shell/index.d.ts +306 -37
- package/dist/orchestration-shell/mention-suggest.d.ts +11 -0
- package/dist/orchestration-shell/mode-selection.d.ts +4 -22
- package/dist/orchestration-shell/quorum-loop.d.ts +336 -20
- package/dist/orchestration-shell/task-progress.d.ts +2 -0
- package/dist/orchestration-shell/team-decompose.d.ts +37 -19
- package/dist/orchestration-shell/types.d.ts +71 -4
- package/dist/planner/index.d.ts +1 -1
- package/dist/planner/local-advisory.d.ts +12 -0
- package/dist/planner/local-gemma.d.ts +27 -0
- package/dist/planner/types.d.ts +7 -0
- package/dist/reviewer/index.d.ts +0 -2
- package/dist/reviewer/provider.d.ts +9 -6
- package/dist/reviewer/providers/antigravity.d.ts +2 -3
- package/dist/reviewer/registry.d.ts +9 -4
- package/dist/reviewer/token-usage.d.ts +10 -0
- package/dist/session/__tests__/device-registration-lease.test.d.ts +1 -0
- package/dist/session/__tests__/pending-session-create.test.d.ts +1 -0
- package/dist/session/__tests__/session-rekey-fingerprint.test.d.ts +1 -0
- package/dist/session/__tests__/session-resume-key-reconciliation.test.d.ts +1 -0
- package/dist/session/__tests__/session-resume-pending-first.test.d.ts +1 -0
- package/dist/session/device-key-watcher.d.ts +1 -0
- package/dist/session/index.d.ts +4 -2
- package/dist/session/pending-session-create.d.ts +61 -0
- package/dist/session/register-device-key.d.ts +11 -1
- package/dist/session/session-rekey.d.ts +4 -5
- package/dist/session/session-resume.d.ts +21 -1
- package/dist/substrate/docker.d.ts +7 -0
- package/dist/substrate/sandbox-exec.d.ts +17 -3
- package/dist/substrate/types.d.ts +32 -3
- package/dist/substrate-launch/apikey-bootstrap.d.ts +24 -1
- package/dist/substrate-launch/engage-substrate.d.ts +17 -0
- package/dist/substrate-launch/sanitized-env.d.ts +5 -5
- package/dist/tool-activity/__tests__/consolidator.test.d.ts +1 -0
- package/dist/tool-activity/__tests__/digest.test.d.ts +1 -0
- package/dist/tool-activity/__tests__/flock-rollout-lock.test.d.ts +1 -0
- package/dist/tool-activity/__tests__/ledger.test.d.ts +1 -0
- package/dist/tool-activity/__tests__/outbox.test.d.ts +1 -0
- package/dist/tool-activity/__tests__/replay-cursor.test.d.ts +1 -0
- package/dist/tool-activity/__tests__/session-map.test.d.ts +1 -0
- package/dist/tool-activity/__tests__/socket-rollout-lock.test.d.ts +1 -0
- package/dist/tool-activity/atomic-file.d.ts +27 -0
- package/dist/tool-activity/consolidator.d.ts +253 -0
- package/dist/tool-activity/digest.d.ts +4 -0
- package/dist/tool-activity/flock-rollout-lock.d.ts +68 -0
- package/dist/tool-activity/index.d.ts +9 -0
- package/dist/tool-activity/ledger.d.ts +274 -0
- package/dist/tool-activity/outbox.d.ts +60 -0
- package/dist/tool-activity/replay-cursor.d.ts +156 -0
- package/dist/tool-activity/session-map.d.ts +133 -0
- package/dist/tool-activity/socket-rollout-lock.d.ts +57 -0
- package/dist/tool-activity/types.d.ts +368 -0
- package/dist/types/encryption.d.ts +4 -0
- package/dist/types/events.d.ts +24 -4
- package/dist/types/session.d.ts +32 -2
- package/package.json +5 -4
- package/dist/orchestration/orchestration-cli.d.ts +0 -12
- package/dist/orchestration/setup-bootstrap.d.ts +0 -146
- package/dist/orchestration/setup-failure-recourse.d.ts +0 -23
- package/dist/orchestration/setup-save.d.ts +0 -47
- package/dist/orchestration/setup-seat-picker.d.ts +0 -72
- package/dist/orchestration/setup-telemetry.d.ts +0 -54
- package/dist/orchestration/setup-test-agents.d.ts +0 -108
- package/dist/orchestration/setup-types.d.ts +0 -146
- package/dist/orchestration/setup-wizard.d.ts +0 -57
- package/dist/orchestration-shell/cohort-flag.d.ts +0 -16
- package/dist/reviewer/providers/gemini.d.ts +0 -109
- /package/dist/{companion-mode/__tests__/detect-parallel.test.d.ts → appsync/__tests__/appsync-client-context-items.test.d.ts} +0 -0
- /package/dist/{orchestration-shell/__tests__/cli-stub-session-adapter.test.d.ts → appsync/__tests__/heartbeat-active.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-bootstrap.test.d.ts → appsync/__tests__/orphan-sweep-null-heartbeat.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-failure-recourse.test.d.ts → auth/__tests__/vendor-key-cli.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-save.test.d.ts → companion-mode/__tests__/mode-selected-emit.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-seat-picker.test.d.ts → companion-mode/__tests__/private-launcher-detection.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-telemetry.test.d.ts → companion-mode/__tests__/private-launcher-path.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-test-agents.test.d.ts → credential-broker/__tests__/broker-c4.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-types.test.d.ts → credential-broker/__tests__/broker-streaming.test.d.ts} +0 -0
- /package/dist/{orchestration/__tests__/setup-wizard.test.d.ts → credential-broker/__tests__/chatgpt-config.test.d.ts} +0 -0
- /package/dist/{reviewer/providers/__tests__/gemini-live-smoke.test.d.ts → credential-broker/__tests__/oauth-account-lock.test.d.ts} +0 -0
- /package/dist/{reviewer/providers/__tests__/gemini.test.d.ts → credential-broker/__tests__/oauth-refresh.test.d.ts} +0 -0
|
@@ -1,7 +1,38 @@
|
|
|
1
|
+
import { PassThrough } from "node:stream";
|
|
1
2
|
import { type StrictAuditSink } from "./audit-sink";
|
|
2
3
|
import { getProviderConfig } from "./broker-config";
|
|
3
4
|
import { type RotationHandle } from "./broker-token";
|
|
4
|
-
import { type
|
|
5
|
+
import { type RefreshTransport } from "./oauth-refresh";
|
|
6
|
+
import { OAuthAccountLock } from "./oauth-account-lock";
|
|
7
|
+
import { type BrokerHandled, type BrokerRequest, type BrokerResponse, type BrokerToken, type ModelGatewayBroker, type ResponseSink, type UpstreamClient, type VendorKeyStore, type VendorOAuthBundle, type VendorProvider } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* PHASE-643 §3.3 — the streaming STALL meter: a `PassThrough`-derived stage that
|
|
10
|
+
* OWNS the idle timer (there is NO outer timer variable — agy R5, eliminating
|
|
11
|
+
* the stale-reference leak structurally). It ARMS the timer at construction,
|
|
12
|
+
* RE-ARMS it per chunk, calls `onIdle()` (→ `reg.controller.abort()`) on fire,
|
|
13
|
+
* and DISARMS it in `_destroy()`.
|
|
14
|
+
*
|
|
15
|
+
* Arm-order pin (Stage-1 v10): the timer arms LAZILY in `_construct` — which
|
|
16
|
+
* Node invokes AFTER `new IdleMeter(...)` has returned and been assigned to
|
|
17
|
+
* `meter` — so a constructor throw could never leave an armed timer that
|
|
18
|
+
* `meter?.destroy()` cannot reach. `autoDestroy` is pinned `true` (the Node
|
|
19
|
+
* default; pipeline explicitly skips destroying naturally-completed streams, so
|
|
20
|
+
* `autoDestroy` is what runs `_destroy` — and thus disarms the timer — on a
|
|
21
|
+
* successful stream), guaranteeing no timer survives a completed stream and no
|
|
22
|
+
* spurious late abort can fire.
|
|
23
|
+
*/
|
|
24
|
+
export declare class IdleMeter extends PassThrough {
|
|
25
|
+
private readonly idleMs;
|
|
26
|
+
private readonly onIdle;
|
|
27
|
+
private idleTimer;
|
|
28
|
+
private disarmed;
|
|
29
|
+
constructor(idleMs: number, onIdle: () => void);
|
|
30
|
+
_construct(callback: (err?: Error | null) => void): void;
|
|
31
|
+
_transform(chunk: unknown, _enc: BufferEncoding, cb: (err?: Error | null, data?: unknown) => void): void;
|
|
32
|
+
_destroy(err: Error | null, cb: (err?: Error | null) => void): void;
|
|
33
|
+
private arm;
|
|
34
|
+
private disarm;
|
|
35
|
+
}
|
|
5
36
|
/**
|
|
6
37
|
* Construction-time dependencies. All injectable so W1 unit-tests run
|
|
7
38
|
* against stubs (no network, deterministic audit ACKs). Defaults wire the
|
|
@@ -18,7 +49,38 @@ export interface BrokerDeps {
|
|
|
18
49
|
vendorKeyStore?: VendorKeyStore;
|
|
19
50
|
/** Strict audit sink (defaults to the in-memory stub until W3, §3.5). */
|
|
20
51
|
auditSink?: StrictAuditSink;
|
|
52
|
+
/**
|
|
53
|
+
* PHASE-626 §5 — the C4 refresh transport (token endpoint). Defaults to the
|
|
54
|
+
* hardened `FetchRefreshTransport`. Injected as a stub in tests.
|
|
55
|
+
*/
|
|
56
|
+
refreshTransport?: RefreshTransport;
|
|
57
|
+
/**
|
|
58
|
+
* PHASE-626 §5 — the cross-process OAuth-account lock for the (single) C4
|
|
59
|
+
* provider `openai`. Defaults to the portable `mkdir`-lock at
|
|
60
|
+
* `defaultOAuthLockDir("vendor-oauth-openai")`. Injected in tests.
|
|
61
|
+
*/
|
|
62
|
+
oauthLock?: OAuthAccountLock;
|
|
63
|
+
/** Proactive-refresh age threshold (ms). Test override. */
|
|
64
|
+
proactiveRefreshThresholdMs?: number;
|
|
65
|
+
/** stop() bounded-drain timeout (ms). Test override. */
|
|
66
|
+
stopDrainTimeoutMs?: number;
|
|
67
|
+
/** Injectable clock/delay for the stop drain (tests). */
|
|
68
|
+
now?: () => number;
|
|
69
|
+
delay?: (ms: number) => Promise<void>;
|
|
21
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* LOCAL-ONLY diagnostic shape of a non-200 upstream body. Upstream error
|
|
73
|
+
* bodies are UNTRUSTED and can carry credential material in ANY string —
|
|
74
|
+
* including inside the canonical `error.message`/`type`/`code` fields (a bare
|
|
75
|
+
* JWT matches no I2 pattern; Stage-2 r3 empirically refuted the
|
|
76
|
+
* scrub-the-allow-listed-values variant) — so ZERO upstream-controlled bytes
|
|
77
|
+
* may reach the logger. Reports structure only: which canonical error fields
|
|
78
|
+
* exist (key names come from OUR constant list, never the body) and their
|
|
79
|
+
* byte lengths. Own-property reads only (the W1 R3 prototype-pollution
|
|
80
|
+
* class). Cannot throw: no upstream value is ever serialized or traversed
|
|
81
|
+
* beyond one fixed level.
|
|
82
|
+
*/
|
|
83
|
+
export declare function upstreamErrorShape(body: unknown): Record<string, unknown> | undefined;
|
|
22
84
|
/**
|
|
23
85
|
* The local model-gateway broker (§3.2). One per session on
|
|
24
86
|
* `127.0.0.1:<ephemeral>`. Holds the upstream credential in-process only
|
|
@@ -35,12 +97,34 @@ export declare class LocalModelGatewayBroker implements ModelGatewayBroker, Vend
|
|
|
35
97
|
private readonly brokerId;
|
|
36
98
|
private server;
|
|
37
99
|
private boundAddr;
|
|
38
|
-
/** In-process cache of upstream
|
|
100
|
+
/** In-process cache of the resolved upstream credential (zeroized on stop()). */
|
|
39
101
|
private upstreamKeys;
|
|
40
102
|
/** Latest `credential_issuance_id` per provider (for the model_call FK). */
|
|
41
103
|
private issuanceIds;
|
|
42
104
|
private issuanceCounter;
|
|
105
|
+
private readonly refreshTransport;
|
|
106
|
+
private oauthLock;
|
|
107
|
+
private readonly proactiveThresholdMs;
|
|
108
|
+
private readonly stopDrainTimeoutMs;
|
|
109
|
+
private readonly nowFn;
|
|
110
|
+
private readonly delayFn;
|
|
111
|
+
/**
|
|
112
|
+
* Per-provider credential generation (§5) — advances on EVERY self-refresh
|
|
113
|
+
* AND on adopting a sibling's persisted bundle. A 401 issued under an older
|
|
114
|
+
* generation retries on the current credential WITHOUT re-refreshing.
|
|
115
|
+
*/
|
|
116
|
+
private credentialGeneration;
|
|
117
|
+
/** Intra-instance single-flight: one in-flight refresh per provider. */
|
|
118
|
+
private inflightRefresh;
|
|
119
|
+
/** STOPPING gate — reject new gateway work once set. */
|
|
120
|
+
private stopping;
|
|
121
|
+
/** Monotonic lifecycle generation `stop()` bumps (straggler no-op guard). */
|
|
122
|
+
private lifecycleGeneration;
|
|
123
|
+
/** Registered in-flight credential-bearing outer ops. */
|
|
124
|
+
private readonly ops;
|
|
43
125
|
constructor(deps: BrokerDeps);
|
|
126
|
+
/** Lazily resolve the OAuth-account lock for the (only) C4 provider `openai`. */
|
|
127
|
+
private getOAuthLock;
|
|
44
128
|
/**
|
|
45
129
|
* Start the per-session HTTP gateway on an ephemeral loopback port + mint
|
|
46
130
|
* the session's broker token. Resolves the bound `127.0.0.1:<port>`.
|
|
@@ -53,8 +137,29 @@ export declare class LocalModelGatewayBroker implements ModelGatewayBroker, Vend
|
|
|
53
137
|
/**
|
|
54
138
|
* Tear down: close the HTTP server, ZEROIZE the in-process upstream
|
|
55
139
|
* credentials, and clear the broker token. Idempotent.
|
|
140
|
+
*
|
|
141
|
+
* PHASE-626 §5 stop lifecycle (Stage-2 R1/R2/R3/R4 Codex): today's naive
|
|
142
|
+
* "clear the map immediately" lets an in-flight keychain load or refresh
|
|
143
|
+
* completing AFTER `stop()` silently REPOPULATE credentials post-stop. The
|
|
144
|
+
* new lifecycle: (1) enter STOPPING (reject new work) + bump the lifecycle
|
|
145
|
+
* generation so any straggler resuming post-stop aborts as a no-op; (2) await
|
|
146
|
+
* a BOUNDED drain of in-flight credential-bearing ops; (3) ABORT every
|
|
147
|
+
* registered signal (cancels abortable inner ops: the model-forward fetch +
|
|
148
|
+
* refresh transport) AND await the SETTLEMENT of every registered outer task
|
|
149
|
+
* (non-cancellable host ops — keychain, `mkdir`-lock, audit-sink — are awaited
|
|
150
|
+
* to natural settlement, never falsely raced); (4) drop ALL credential +
|
|
151
|
+
* in-flight-promise references as the FINAL step.
|
|
56
152
|
*/
|
|
57
153
|
stop(): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Register an in-flight credential-bearing OUTER operation SYNCHRONOUSLY
|
|
156
|
+
* (before its first `await`) so `stop()` can abort + await it. Closes the
|
|
157
|
+
* started-but-unregistered pre-registration race.
|
|
158
|
+
*/
|
|
159
|
+
private registerOp;
|
|
160
|
+
private deregisterOp;
|
|
161
|
+
/** True once `stop()` has bumped past the op's captured generation. */
|
|
162
|
+
private isStale;
|
|
58
163
|
/** The current broker token (for the agent's apiKeyHelper bootstrap). */
|
|
59
164
|
currentBrokerToken(): BrokerToken | null;
|
|
60
165
|
/**
|
|
@@ -92,25 +197,104 @@ export declare class LocalModelGatewayBroker implements ModelGatewayBroker, Vend
|
|
|
92
197
|
rollbackBrokerTokenRotation(handle?: RotationHandle): void;
|
|
93
198
|
getVendorKey(provider: VendorProvider): Promise<string | null>;
|
|
94
199
|
setVendorKey(provider: VendorProvider, key: string): Promise<void>;
|
|
200
|
+
clearVendorKey(provider: VendorProvider): Promise<boolean>;
|
|
201
|
+
getVendorOAuthBundle(provider: VendorProvider): Promise<VendorOAuthBundle | null>;
|
|
202
|
+
setVendorOAuthBundle(provider: VendorProvider, bundle: VendorOAuthBundle): Promise<void>;
|
|
203
|
+
clearVendorOAuthBundle(provider: VendorProvider): Promise<boolean>;
|
|
95
204
|
/**
|
|
96
205
|
* Handle one broker request (§3.2). The pure, transport-agnostic core —
|
|
97
206
|
* the HTTP server (`handleHttp`) adapts node req/res onto this. Route
|
|
98
|
-
* table check first (everything else → 403), then
|
|
99
|
-
* the two POSTs (the ONLY 401 is an
|
|
100
|
-
* the per-provider gateway (§3.3/§3.4/§3.5).
|
|
207
|
+
* table check first (everything else → 403), then local metadata/health,
|
|
208
|
+
* then broker-token auth on the two model POSTs (the ONLY 401 is an
|
|
209
|
+
* invalid agent→broker token), then the per-provider gateway (§3.3/§3.4/§3.5).
|
|
101
210
|
*/
|
|
102
211
|
handle(req: BrokerRequest): Promise<BrokerResponse>;
|
|
212
|
+
handle(req: BrokerRequest, sink: ResponseSink): Promise<BrokerResponse | BrokerHandled>;
|
|
213
|
+
/**
|
|
214
|
+
* The credential-bearing OUTER operation (§5 stop lifecycle). Registers on
|
|
215
|
+
* the op-registry SYNCHRONOUSLY (before its first `await`), then runs the
|
|
216
|
+
* gateway body with the registration's abort signal + captured lifecycle
|
|
217
|
+
* generation, and deregisters in `finally`.
|
|
218
|
+
*/
|
|
103
219
|
private gateway;
|
|
220
|
+
private gatewayInner;
|
|
221
|
+
/**
|
|
222
|
+
* PHASE-643 §3.3 — the in-scope streaming write for a 200 `text/event-stream`
|
|
223
|
+
* forward. Runs INSIDE the op scope (the op stays in `this.ops` across the
|
|
224
|
+
* stream → `stop()` phase-3 settles). Pipes the upstream body incrementally
|
|
225
|
+
* into the caller-provided sink, byte-for-byte, forwarding ONLY `content-type`.
|
|
226
|
+
*
|
|
227
|
+
* Teardown principle (R6): NO ownership tracking — a resolving `pipeline()` does
|
|
228
|
+
* NOT prove it adopted the stages (a setup-time rejection, e.g.
|
|
229
|
+
* `ERR_STREAM_UNABLE_TO_PIPE` on an already-destroyed sink, leaves BOTH the
|
|
230
|
+
* source AND the meter live). The unified catch UNCONDITIONALLY + IDEMPOTENTLY
|
|
231
|
+
* tears down EVERY broker-allocated stage — `meter?.destroy()` (disarms its
|
|
232
|
+
* armed idle timer via `_destroy`) FIRST (Stage-2 R6 Codex MED, empirically
|
|
233
|
+
* reproduced on Node ≥22), then a guarded `source` destroy-before-abort with a
|
|
234
|
+
* no-op `'error'` listener (§5.1-D), then `reg.controller.abort()` — on ANY
|
|
235
|
+
* throw OR pipeline rejection in steps 1–3, whether or not the pipeline was
|
|
236
|
+
* invoked or already destroyed the stages. A missed teardown would leave a live
|
|
237
|
+
* credential-bearing fetch (or an armed ref'd timer) that `stop()` can no longer
|
|
238
|
+
* reach after op deregister.
|
|
239
|
+
*/
|
|
240
|
+
private streamForward;
|
|
104
241
|
/**
|
|
105
|
-
*
|
|
106
|
-
* `
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
242
|
+
* Build the upstream `{url, headers}` for a resolved credential (§5). For an
|
|
243
|
+
* `oauth_bundle` (C4) the URL is OVERRIDDEN to the ChatGPT config and its
|
|
244
|
+
* synthesized headers (originator / User-Agent) are merged over the openai
|
|
245
|
+
* canonical headers; the credential headers add `Authorization: Bearer` +
|
|
246
|
+
* `ChatGPT-Account-Id`. For C1/C2/C3 the canonical URL + the C2-beta synthesis
|
|
247
|
+
* are preserved unchanged.
|
|
111
248
|
*/
|
|
112
|
-
private
|
|
249
|
+
private buildUpstreamTarget;
|
|
250
|
+
/**
|
|
251
|
+
* Load the vendor credential (class-discriminated + precedence), caching it
|
|
252
|
+
* in-process, and emit `broker_credential_loaded` (§3.5) — with a
|
|
253
|
+
* `key_fingerprint` (sha256 prefix over the class-appropriate string; for an
|
|
254
|
+
* oauth_bundle the `refresh_token`) and a fresh `credential_issuance_id`,
|
|
255
|
+
* NEVER the raw credential.
|
|
256
|
+
*
|
|
257
|
+
* Precedence (§5): C1/C2/C3 (`vendor-key-<provider>`) WINS over a C4
|
|
258
|
+
* `vendor-oauth-openai` bundle. Explicit key wins; no config knob.
|
|
259
|
+
*/
|
|
260
|
+
private loadVendorCredential;
|
|
261
|
+
private genOf;
|
|
262
|
+
private currentBundle;
|
|
263
|
+
/** Is the C4 bundle's `last_refresh` older than the proactive threshold? */
|
|
264
|
+
private isProactiveDue;
|
|
265
|
+
/**
|
|
266
|
+
* Adopt a bundle into the in-process cache AND advance the local credential
|
|
267
|
+
* generation (§5 — adoption is treated identically to a self-refresh, so a
|
|
268
|
+
* stale-generation 401 recognizes the rotation and does not re-rotate).
|
|
269
|
+
* Respects the lifecycle guard: never repopulates a cleared cache post-stop.
|
|
270
|
+
*/
|
|
271
|
+
private adoptBundle;
|
|
272
|
+
/**
|
|
273
|
+
* Ensure a fresh C4 access token via single-flight + the cross-broker lock.
|
|
274
|
+
* `mode` distinguishes reactive (post-401 — a transient failure fails the
|
|
275
|
+
* call closed) from proactive (token still valid — a transient failure keeps
|
|
276
|
+
* serving the current token). `usedGen` (reactive only) is the generation the
|
|
277
|
+
* failed request rode: a 401 older than the current generation retries on the
|
|
278
|
+
* CURRENT credential WITHOUT refreshing (staggered-stale-401 guard).
|
|
279
|
+
*/
|
|
280
|
+
private refreshOAuth;
|
|
281
|
+
/**
|
|
282
|
+
* The cross-broker critical section (§5): acquire the OAuth-account lock →
|
|
283
|
+
* RE-READ the persisted bundle under the lock → compare-and-skip on
|
|
284
|
+
* `access_token` identity → refresh via the token endpoint → reassert
|
|
285
|
+
* ownership → whole-bundle write-through → advance generation → release.
|
|
286
|
+
* The single-flight promise resolves only AFTER persist + cache update.
|
|
287
|
+
*/
|
|
288
|
+
private doRefreshUnderLock;
|
|
113
289
|
private lastIssuanceId;
|
|
290
|
+
/**
|
|
291
|
+
* The user-visible remediation response for a PERMANENT (`invalid_grant`) C4
|
|
292
|
+
* refresh failure — the refresh token is dead, so no in-broker recovery is
|
|
293
|
+
* possible and the user must re-import (§5 failure classification + remediation
|
|
294
|
+
* scoping). Shared by BOTH the reactive (post-401) and proactive refresh paths
|
|
295
|
+
* so the surfaced instruction cannot drift.
|
|
296
|
+
*/
|
|
297
|
+
private permanentRefreshResponse;
|
|
114
298
|
/**
|
|
115
299
|
* Map an upstream response to the agent-facing status (§3.2). An upstream
|
|
116
300
|
* 401/403 (bad/expired vendor key) → 502 (NOT a pass-through 401, which
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** The C4 ChatGPT-subscription upstream wire facts (LIVE-PINNED, §13 OQ-1). */
|
|
2
|
+
export interface ChatGptUpstreamConfig {
|
|
3
|
+
/** ChatGPT backend origin the C4 Bearer forwards to (origin confinement target). */
|
|
4
|
+
readonly upstreamBaseUrl: string;
|
|
5
|
+
/** The Responses route appended to the base for `/v1/responses`-shaped calls. */
|
|
6
|
+
readonly upstreamPath: string;
|
|
7
|
+
/**
|
|
8
|
+
* Headers the broker SYNTHESIZES on EVERY C4 upstream call (NOT agent-
|
|
9
|
+
* supplied — the canonical constructor drops all agent headers). These are
|
|
10
|
+
* broker-owned constants; the credential-derived `ChatGPT-Account-Id` +
|
|
11
|
+
* `Authorization` are added separately by `credentialHeaders` (they depend
|
|
12
|
+
* on the resolved bundle).
|
|
13
|
+
*/
|
|
14
|
+
readonly synthesizedHeaders: Readonly<Record<string, string>>;
|
|
15
|
+
/**
|
|
16
|
+
* PHASE-643-B §7 (v11) — pinned WIRE FACT: the real chatgpt.com Responses
|
|
17
|
+
* endpoint returns `200` + a genuine SSE body with NO `Content-Type` header at
|
|
18
|
+
* all (probe-proven 2026-07-21 — Cloudflare-fronted, `Transfer-Encoding:
|
|
19
|
+
* chunked`, no content-type either with or without an `Accept` header). So a
|
|
20
|
+
* `200` with an ABSENT/empty content-type on THIS endpoint must be STREAMED as
|
|
21
|
+
* SSE, not buffered. This is per-endpoint pinned knowledge (same class as the
|
|
22
|
+
* `originator`/UA pins), NOT body-sniffing and NOT `Accept` synthesis. Only the
|
|
23
|
+
* flagged (C4/oauth_bundle chatgpt) target sets it; declared non-SSE 200s
|
|
24
|
+
* (`application/json`) on this target STILL buffer.
|
|
25
|
+
*/
|
|
26
|
+
readonly assumeSseOn200: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare const CHATGPT_UPSTREAM_CONFIG: ChatGptUpstreamConfig;
|
|
29
|
+
/** The full pinned C4 model-forward URL (`{base}{route}`). */
|
|
30
|
+
export declare function chatGptUpstreamUrl(): string;
|
|
31
|
+
/** OAuth token-refresh endpoint (LIVE-PINNED). */
|
|
32
|
+
export declare const CHATGPT_TOKEN_ENDPOINT = "https://auth.openai.com/oauth/token";
|
|
33
|
+
/** Public OAuth client_id for the refresh grant (LIVE-PINNED). */
|
|
34
|
+
export declare const CHATGPT_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
35
|
+
/**
|
|
36
|
+
* The request content type for the refresh grant. The body is
|
|
37
|
+
* `grant_type=refresh_token&refresh_token=…&client_id=…` (§5), so it is a
|
|
38
|
+
* standard OAuth2 form-encoded token request.
|
|
39
|
+
*/
|
|
40
|
+
export declare const CHATGPT_REFRESH_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { VendorProvider, BrokerRouteMethod, BrokerRoute, BrokerStatus, BrokerRequest, BrokerResponse, ModelGatewayBroker, VendorKeyStore, BrokerToken, UpstreamResponse, UpstreamClient, } from "./types";
|
|
1
|
+
export type { VendorProvider, BrokerRouteMethod, BrokerRoute, BrokerStatus, BrokerRequest, BrokerResponse, BrokerHandled, ResponseSink, ModelGatewayBroker, VendorKeyStore, VendorOAuthBundle, ResolvedCredential, BrokerToken, UpstreamResult, UpstreamResponse, UpstreamClient, } from "./types";
|
|
2
2
|
export { BROKER_ROUTES } from "./types";
|
|
3
3
|
export { LocalModelGatewayBroker } from "./broker";
|
|
4
4
|
export type { BrokerDeps } from "./broker";
|
|
@@ -7,6 +7,9 @@ export type { StrictAuditSink, AuditKindCp7, RecordedAuditEmit } from "./audit-s
|
|
|
7
7
|
export { StubStrictAuditSink } from "./audit-sink";
|
|
8
8
|
export { KeychainVendorKeyStore } from "./vendor-key-store";
|
|
9
9
|
export { BrokerTokenMinter, type RotationHandle } from "./broker-token";
|
|
10
|
+
export { CHATGPT_UPSTREAM_CONFIG, chatGptUpstreamUrl, CHATGPT_TOKEN_ENDPOINT, CHATGPT_OAUTH_CLIENT_ID, CHATGPT_REFRESH_CONTENT_TYPE, } from "./chatgpt-config";
|
|
11
|
+
export { FetchRefreshTransport, refreshOAuthToken, buildRefreshBody, type RefreshTransport, type RefreshOutcome, } from "./oauth-refresh";
|
|
12
|
+
export { OAuthAccountLock, createOAuthAccountLock, defaultOAuthLockDir, type OAuthAccountLockOptions, type LockHandle, type LockOwner, } from "./oauth-account-lock";
|
|
10
13
|
export { buildCanonicalRequest, selfCredentialGuard, requestSha256, CanonicalRejectError, } from "./canonical";
|
|
11
14
|
export type { CanonicalInput, CanonicalRequest } from "./canonical";
|
|
12
15
|
export { scrubRequestBody } from "./scrubber";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/** Liveness of a recorded lock owner (only `dead` is ever reclaimed). */
|
|
2
|
+
export type OwnerLiveness = "alive" | "dead" | "unknown";
|
|
3
|
+
/** The `owner.json` record written once per acquisition. */
|
|
4
|
+
export interface LockOwner {
|
|
5
|
+
pid: number;
|
|
6
|
+
host: string;
|
|
7
|
+
/** Random per-ACQUISITION nonce — the fence for every destructive/heartbeat op. */
|
|
8
|
+
ownerId: string;
|
|
9
|
+
/** Acquisition time (ISO), for diagnostics. */
|
|
10
|
+
startedAt: string;
|
|
11
|
+
}
|
|
12
|
+
export interface OAuthAccountLockOptions {
|
|
13
|
+
/** The canonical lockdir path (keyed to the OAuth account). */
|
|
14
|
+
lockDir: string;
|
|
15
|
+
/** Staleness TTL (» max critical-section time). Default 30s. */
|
|
16
|
+
ttlMs?: number;
|
|
17
|
+
/** Heartbeat interval (« TTL). Default 5s. */
|
|
18
|
+
heartbeatMs?: number;
|
|
19
|
+
/** Contention deadline. Default 60s. */
|
|
20
|
+
maxWaitMs?: number;
|
|
21
|
+
/** Base retry backoff. Default 25ms (capped 1s). */
|
|
22
|
+
backoffMs?: number;
|
|
23
|
+
now?: () => number;
|
|
24
|
+
delay?: (ms: number) => Promise<void>;
|
|
25
|
+
/** This process's pid (injectable for tests). */
|
|
26
|
+
pid?: number;
|
|
27
|
+
/** This host's id (injectable for tests). */
|
|
28
|
+
hostId?: string;
|
|
29
|
+
/** Liveness probe (injectable for tests). Default = same-host `process.kill(pid,0)`. */
|
|
30
|
+
isProcessAlive?: (pid: number, host: string) => OwnerLiveness;
|
|
31
|
+
/** Per-acquisition nonce generator (injectable for tests). */
|
|
32
|
+
randomId?: () => string;
|
|
33
|
+
/** `utimes(lockdir)` heartbeat op (injectable so tests can observe/spy). */
|
|
34
|
+
utimesFn?: (dir: string) => Promise<void>;
|
|
35
|
+
/** Disable the auto-interval heartbeat (tests drive ticks manually). */
|
|
36
|
+
disableAutoHeartbeat?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** A held lock. `release` is one-shot + idempotent. */
|
|
39
|
+
export interface LockHandle {
|
|
40
|
+
readonly ownerId: string;
|
|
41
|
+
/**
|
|
42
|
+
* Reassert-before-mutation (§5 primary revocation-wins backstop): re-read the
|
|
43
|
+
* canonical `owner.json` and return true iff `ownerId === ours`. The broker
|
|
44
|
+
* calls this IMMEDIATELY before EVERY keychain `setPassword`/`deletePassword`,
|
|
45
|
+
* aborting fail-closed on a false result — independently defeating any
|
|
46
|
+
* displacement that COMPLETED before the re-read.
|
|
47
|
+
*/
|
|
48
|
+
reassertOwnership(): Promise<boolean>;
|
|
49
|
+
/** One-shot release: cancel+join heartbeat, then unlink+rmdir under `ownerId` re-assert. */
|
|
50
|
+
release(): Promise<void>;
|
|
51
|
+
/** TEST-ONLY: run one heartbeat tick synchronously (simulates a queued `utimes`). */
|
|
52
|
+
_tickForTest(): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
/** The portable cross-process OAuth-account lock (§5 / AMEND-1). */
|
|
55
|
+
export declare class OAuthAccountLock {
|
|
56
|
+
private readonly lockDir;
|
|
57
|
+
private readonly ownerFile;
|
|
58
|
+
private readonly reclaimGuardDir;
|
|
59
|
+
private readonly ttlMs;
|
|
60
|
+
private readonly heartbeatMs;
|
|
61
|
+
private readonly maxWaitMs;
|
|
62
|
+
private readonly backoffMs;
|
|
63
|
+
private readonly now;
|
|
64
|
+
private readonly delayFn;
|
|
65
|
+
private readonly pid;
|
|
66
|
+
private readonly hostId;
|
|
67
|
+
private readonly isProcessAlive;
|
|
68
|
+
private readonly randomId;
|
|
69
|
+
private readonly utimesFn;
|
|
70
|
+
private readonly disableAutoHeartbeat;
|
|
71
|
+
constructor(opts: OAuthAccountLockOptions);
|
|
72
|
+
/**
|
|
73
|
+
* Acquire the lock (bounded by `maxWaitMs`). Resolves a {@link LockHandle} on
|
|
74
|
+
* success; REJECTS fail-closed on: an unwritable lockdir root, a contention
|
|
75
|
+
* timeout (live/ambiguous/ownerless holder), or a dead/stranded reclaim guard.
|
|
76
|
+
* ZERO side effects on the persisted bundle on any fail-closed path.
|
|
77
|
+
*/
|
|
78
|
+
acquire(): Promise<LockHandle>;
|
|
79
|
+
/** Whether the EXISTING lockdir holder is reclaimable (dead), or must be waited on. */
|
|
80
|
+
private classifyExistingHolder;
|
|
81
|
+
/**
|
|
82
|
+
* Reclaim a stale, provably-dead lockdir under the SEPARATE reclaim guard.
|
|
83
|
+
* Returns a fresh {@link LockHandle} on success, or `null` if the reclaim was
|
|
84
|
+
* aborted (a sibling already reclaimed, or the guard is held). Throws only if
|
|
85
|
+
* the guard is dead/stranded (fail-closed → offline cleanup).
|
|
86
|
+
*/
|
|
87
|
+
private tryReclaim;
|
|
88
|
+
/** Write `owner.json`, start the heartbeat, and build the handle. */
|
|
89
|
+
private onAcquired;
|
|
90
|
+
private tryMkdir;
|
|
91
|
+
private readOwner;
|
|
92
|
+
private lockDirAge;
|
|
93
|
+
private removeStaleDir;
|
|
94
|
+
private rmdirQuiet;
|
|
95
|
+
private backoff;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Default lockdir path for a provider's OAuth account — a stable per-host path
|
|
99
|
+
* under `~/.codevibe/locks/` (all C4 brokers are same-host, so a shared
|
|
100
|
+
* filesystem path gives cross-process exclusion). NOT under the substrate
|
|
101
|
+
* mount (I1) — this is host-side broker coordination only.
|
|
102
|
+
*/
|
|
103
|
+
export declare function defaultOAuthLockDir(account: string): string;
|
|
104
|
+
export declare function createOAuthAccountLock(opts: OAuthAccountLockOptions): OAuthAccountLock;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { VendorOAuthBundle } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* The injected refresh transport (§5). Separate from the model `UpstreamClient`
|
|
4
|
+
* (different content type, form body, token endpoint). Widened to accept an
|
|
5
|
+
* optional `signal` (the stop-lifecycle end-to-end cancellation contract).
|
|
6
|
+
* Resolves `{status, rawBody, contentType}`; REJECTS only on a transport error
|
|
7
|
+
* OR a 3xx redirect (redirect confinement).
|
|
8
|
+
*/
|
|
9
|
+
export interface RefreshTransport {
|
|
10
|
+
post(args: {
|
|
11
|
+
url: string;
|
|
12
|
+
body: string;
|
|
13
|
+
contentType: string;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}): Promise<RefreshTransportResponse>;
|
|
16
|
+
}
|
|
17
|
+
export interface RefreshTransportResponse {
|
|
18
|
+
status: number;
|
|
19
|
+
/** Raw response text (bounded); parsed by the classifier, NEVER logged. */
|
|
20
|
+
rawBody: string;
|
|
21
|
+
/** Lowercased response `content-type`, if any. */
|
|
22
|
+
contentType?: string;
|
|
23
|
+
/**
|
|
24
|
+
* True iff the body exceeded the bounded-read cap and was NOT materialized
|
|
25
|
+
* (transport streamed at most the cap + cancelled). The classifier treats an
|
|
26
|
+
* oversized body as transient WITHOUT parsing (it was never fully read).
|
|
27
|
+
*/
|
|
28
|
+
oversized?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** The outcome of a refresh attempt — drives the reactive/proactive posture. */
|
|
31
|
+
export type RefreshOutcome = {
|
|
32
|
+
kind: "rotated";
|
|
33
|
+
bundle: VendorOAuthBundle;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "permanent";
|
|
36
|
+
reason: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: "transient";
|
|
39
|
+
reason: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The production fetch-backed {@link RefreshTransport}. Mirrors
|
|
43
|
+
* `FetchUpstreamClient`'s hardened posture for the NEW secret surface.
|
|
44
|
+
*/
|
|
45
|
+
export declare class FetchRefreshTransport implements RefreshTransport {
|
|
46
|
+
private readonly opts;
|
|
47
|
+
constructor(opts?: {
|
|
48
|
+
fetchFn?: typeof fetch;
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
});
|
|
51
|
+
post(args: {
|
|
52
|
+
url: string;
|
|
53
|
+
body: string;
|
|
54
|
+
contentType: string;
|
|
55
|
+
signal?: AbortSignal;
|
|
56
|
+
}): Promise<RefreshTransportResponse>;
|
|
57
|
+
}
|
|
58
|
+
/** Build the OAuth2 refresh-grant form body (`refresh_token` is a secret). */
|
|
59
|
+
export declare function buildRefreshBody(refreshToken: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Perform ONE token refresh against the pinned endpoint and classify the
|
|
62
|
+
* result. On success returns a `rotated` bundle that already applies the §5
|
|
63
|
+
* write-through merge rule (`refresh_token`/`id_token` ?? current;
|
|
64
|
+
* `account_id` from current; `last_refresh` = now) — persistence + cache +
|
|
65
|
+
* generation advance are the caller's job (under the cross-broker lock).
|
|
66
|
+
*
|
|
67
|
+
* `now` supplies the `last_refresh` timestamp (injectable for tests).
|
|
68
|
+
*/
|
|
69
|
+
export declare function refreshOAuthToken(current: VendorOAuthBundle, transport: RefreshTransport, opts?: {
|
|
70
|
+
signal?: AbortSignal;
|
|
71
|
+
now?: () => Date;
|
|
72
|
+
}): Promise<RefreshOutcome>;
|