agl 41.0.0 → 41.1.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/changelog.md +18 -0
- package/dist_serve/bundle.js +881 -881
- package/dist_serve/bundle.js.map +2 -2
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/aglapi/aglapisurface.document.js +6 -2
- package/dist_ts/classes.authmodels.d.ts +15 -0
- package/dist_ts/classes.authmodels.js +78 -7
- package/dist_ts/classes.authstore.js +23 -6
- package/dist_ts/classes.cli.js +8 -3
- package/dist_ts/classes.controller.d.ts +13 -0
- package/dist_ts/classes.controller.js +52 -15
- package/dist_ts/classes.controllermanagement.d.ts +53 -6
- package/dist_ts/classes.controllermanagement.js +180 -72
- package/dist_ts/classes.inferencegateway.d.ts +6 -6
- package/dist_ts/classes.inferencegateway.js +25 -13
- package/dist_ts/classes.mcpreexec.js +4 -4
- package/dist_ts/classes.processinspection.d.ts +3 -1
- package/dist_ts/classes.processinspection.js +4 -2
- package/dist_ts/classes.proxycontrol.d.ts +22 -7
- package/dist_ts/classes.proxycontrol.js +5 -3
- package/dist_ts/classes.proxycontrolclient.d.ts +4 -3
- package/dist_ts/classes.proxycontrolclient.js +7 -5
- package/dist_ts/classes.proxydaemon.d.ts +6 -4
- package/dist_ts/classes.proxydaemon.js +42 -17
- package/dist_ts/classes.proxyleaseholder.d.ts +11 -2
- package/dist_ts/classes.proxyleaseholder.js +25 -2
- package/dist_ts/classes.proxystore.d.ts +3 -2
- package/dist_ts/classes.proxystore.js +5 -2
- package/dist_ts/classes.upgradecoordinator.d.ts +2 -1
- package/dist_ts/classes.upgradecoordinator.js +13 -10
- package/dist_ts/classes.upgradetransaction.d.ts +6 -0
- package/dist_ts/classes.upgradetransaction.js +38 -13
- package/dist_ts/classes.workercallers.d.ts +2 -0
- package/dist_ts/classes.workercallers.js +11 -8
- package/dist_ts/functions.controllermanagementsurface.d.ts +37 -0
- package/dist_ts/functions.controllermanagementsurface.js +110 -0
- package/dist_ts/functions.inferencecli.d.ts +6 -3
- package/dist_ts/functions.inferencecli.js +9 -5
- package/dist_ts/functions.inferencecliarguments.d.ts +1 -1
- package/dist_ts/functions.inferencecliarguments.js +19 -7
- package/dist_ts/functions.inferencelaunch.d.ts +30 -9
- package/dist_ts/functions.inferencelaunch.js +49 -23
- package/dist_ts/functions.workerprocessproof.d.ts +12 -3
- package/dist_ts/functions.workerprocessproof.js +17 -6
- package/dist_ts/interfaces.auth.d.ts +14 -3
- package/dist_ts/interfaces.auth.js +4 -3
- package/dist_ts_interfaces/index.d.ts +1 -0
- package/dist_ts_interfaces/index.js +2 -1
- package/dist_ts_interfaces/interfaces.d.ts +5 -0
- package/dist_ts_interfaces/interfaces.js +1 -1
- package/dist_ts_interfaces/management.d.ts +33 -0
- package/dist_ts_interfaces/management.js +35 -0
- package/package.json +2 -1
- package/readme.md +118 -31
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/aglapi/aglapisurface.document.ts +5 -1
- package/ts/classes.authmodels.ts +74 -5
- package/ts/classes.authstore.ts +24 -4
- package/ts/classes.cli.ts +9 -2
- package/ts/classes.controller.ts +99 -60
- package/ts/classes.controllermanagement.ts +284 -107
- package/ts/classes.inferencegateway.ts +28 -17
- package/ts/classes.mcpreexec.ts +3 -3
- package/ts/classes.processinspection.ts +2 -1
- package/ts/classes.proxycontrol.ts +25 -9
- package/ts/classes.proxycontrolclient.ts +7 -4
- package/ts/classes.proxydaemon.ts +43 -17
- package/ts/classes.proxyleaseholder.ts +36 -7
- package/ts/classes.proxystore.ts +8 -3
- package/ts/classes.upgradecoordinator.ts +20 -11
- package/ts/classes.upgradetransaction.ts +53 -2
- package/ts/classes.workercallers.ts +12 -7
- package/ts/functions.controllermanagementsurface.ts +149 -0
- package/ts/functions.inferencecli.ts +15 -7
- package/ts/functions.inferencecliarguments.ts +16 -6
- package/ts/functions.inferencelaunch.ts +57 -23
- package/ts/functions.workerprocessproof.ts +17 -3
- package/ts/interfaces.auth.ts +14 -3
- package/ts_interfaces/index.ts +1 -0
- package/ts_interfaces/interfaces.ts +5 -0
- package/ts_interfaces/management.ts +52 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -274,6 +274,11 @@ export interface IControllerStatus {
|
|
|
274
274
|
packageVersion: string;
|
|
275
275
|
protocolVersion: typeof controllerProtocolVersion;
|
|
276
276
|
upgradeManagementVersion: number;
|
|
277
|
+
/**
|
|
278
|
+
* The protocol of the controller's loopback management endpoint (`controllerManagementProtocolVersion`); absent
|
|
279
|
+
* from a controller older than the endpoint, which is reached over its socket.
|
|
280
|
+
*/
|
|
281
|
+
managementProtocolVersion?: number;
|
|
277
282
|
controllerPid: number;
|
|
278
283
|
processGroupId: number;
|
|
279
284
|
processFingerprint: string;
|
|
@@ -210,4 +210,4 @@ export const controllerEventTypes = [
|
|
|
210
210
|
'sessionfeed.changed',
|
|
211
211
|
'usage.changed',
|
|
212
212
|
];
|
|
213
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
213
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX2ludGVyZmFjZXMvaW50ZXJmYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFPQSxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxFQUFXLENBQUM7QUFDckQsTUFBTSxDQUFDLE1BQU0sa0NBQWtDLEdBQUcsQ0FBVSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLEtBQWMsQ0FBQztBQUNwRCx5R0FBeUc7QUFDekcsTUFBTSxDQUFDLE1BQU0seUJBQXlCLEdBQUcsd0JBQWlDLENBQUM7QUFDM0UsTUFBTSxDQUFDLE1BQU0sMkJBQTJCLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNyRCxNQUFNLENBQUMsTUFBTSxrQ0FBa0MsR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLHFDQUFxQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFDL0QsTUFBTSxDQUFDLE1BQU0sbUNBQW1DLEdBQUcsRUFBRSxDQUFDO0FBQ3RELE1BQU0sQ0FBQyxNQUFNLCtCQUErQixHQUFHLEdBQUcsQ0FBQztBQUNuRCxNQUFNLENBQUMsTUFBTSxnQ0FBZ0MsR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDO0FBQzNEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSwrQkFBK0IsR0FDMUMsOERBQXVFLENBQUM7QUFDMUUsTUFBTSxDQUFDLE1BQU0sMkJBQTJCLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNyRCxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRyxDQUFDLENBQUM7QUFDL0MsTUFBTSxDQUFDLE1BQU0saUNBQWlDLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDbEUsTUFBTSxDQUFDLE1BQU0sc0NBQXNDLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDdkUsTUFBTSxD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
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { IReq_ControllerStatus } from './requests.status.js';
|
|
2
|
+
import type { IReq_ControllerUpgradeFinalize, IReq_ControllerUpgradeLaunch, IReq_ControllerUpgradePrepareBegin } from './requests.upgrade.js';
|
|
3
|
+
/**
|
|
4
|
+
* The controller's loopback management endpoint, on the controller's own port: one TypedRequest envelope
|
|
5
|
+
* (`method`, `request`, `response`, `correlation`, `requestInstanceId`) per HTTP POST of `application/json`, with
|
|
6
|
+
* `Host: 127.0.0.1:<port>`. The local CLI and the upgrade worker read the status and drive an upgrade through it,
|
|
7
|
+
* so an upgrade worker of one release can finalize the controller of the next whatever transport the browser
|
|
8
|
+
* speaks. Every later release keeps this path, these methods, their request and response shapes and the envelope
|
|
9
|
+
* (`test/test.controllermanagementwire.node.ts` pins the bytes an AGL 41 client sends and what it accepts).
|
|
10
|
+
*/
|
|
11
|
+
export declare const controllerManagementTypedRequestPath: '/management/typedrequest';
|
|
12
|
+
/**
|
|
13
|
+
* The protocol of this loopback management endpoint: its path, methods, envelope and their request and response
|
|
14
|
+
* shapes. 1 is the endpoint as its first release ships it; only an incompatible change to that contract raises it.
|
|
15
|
+
* A release declares it twice: as `controllerManagementProtocolVersion` in its `package.json`, which a client reads
|
|
16
|
+
* before the release's controller runs, and as `managementProtocolVersion` in that controller's status. A client
|
|
17
|
+
* reaches a controller over this endpoint only when it declares 1 or more, and over the controller socket otherwise
|
|
18
|
+
* (an older release has no endpoint).
|
|
19
|
+
*
|
|
20
|
+
* It is not `controllerUpgradeManagementVersion` (2), the upgrade contract: every upgrade worker requires exactly
|
|
21
|
+
* that number in its target's `package.json` and status, and upgrade transactions record it, so it cannot move
|
|
22
|
+
* without breaking the online upgrade of every earlier release. This number says only how the management methods
|
|
23
|
+
* are reached.
|
|
24
|
+
*/
|
|
25
|
+
export declare const controllerManagementProtocolVersion: 1;
|
|
26
|
+
/** The methods the endpoint answers; every other method is refused before it is routed. */
|
|
27
|
+
export declare const controllerManagementMethods: readonly ['controller.status', 'controller.upgrade.launch', 'controller.upgrade.prepare.begin', 'controller.upgrade.finalize'];
|
|
28
|
+
export type TControllerManagementMethod = typeof controllerManagementMethods[number];
|
|
29
|
+
export type TControllerManagementTypedRequest = IReq_ControllerStatus | IReq_ControllerUpgradeLaunch | IReq_ControllerUpgradePrepareBegin | IReq_ControllerUpgradeFinalize;
|
|
30
|
+
/** A management request carries a token, a version and a mode at most. */
|
|
31
|
+
export declare const controllerManagementRequestMaximumBodyBytes: number;
|
|
32
|
+
/** The status document with every harness profile and upgrade blocker it may report. */
|
|
33
|
+
export declare const controllerManagementResponseMaximumBytes: number;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The controller's loopback management endpoint, on the controller's own port: one TypedRequest envelope
|
|
3
|
+
* (`method`, `request`, `response`, `correlation`, `requestInstanceId`) per HTTP POST of `application/json`, with
|
|
4
|
+
* `Host: 127.0.0.1:<port>`. The local CLI and the upgrade worker read the status and drive an upgrade through it,
|
|
5
|
+
* so an upgrade worker of one release can finalize the controller of the next whatever transport the browser
|
|
6
|
+
* speaks. Every later release keeps this path, these methods, their request and response shapes and the envelope
|
|
7
|
+
* (`test/test.controllermanagementwire.node.ts` pins the bytes an AGL 41 client sends and what it accepts).
|
|
8
|
+
*/
|
|
9
|
+
export const controllerManagementTypedRequestPath = '/management/typedrequest';
|
|
10
|
+
/**
|
|
11
|
+
* The protocol of this loopback management endpoint: its path, methods, envelope and their request and response
|
|
12
|
+
* shapes. 1 is the endpoint as its first release ships it; only an incompatible change to that contract raises it.
|
|
13
|
+
* A release declares it twice: as `controllerManagementProtocolVersion` in its `package.json`, which a client reads
|
|
14
|
+
* before the release's controller runs, and as `managementProtocolVersion` in that controller's status. A client
|
|
15
|
+
* reaches a controller over this endpoint only when it declares 1 or more, and over the controller socket otherwise
|
|
16
|
+
* (an older release has no endpoint).
|
|
17
|
+
*
|
|
18
|
+
* It is not `controllerUpgradeManagementVersion` (2), the upgrade contract: every upgrade worker requires exactly
|
|
19
|
+
* that number in its target's `package.json` and status, and upgrade transactions record it, so it cannot move
|
|
20
|
+
* without breaking the online upgrade of every earlier release. This number says only how the management methods
|
|
21
|
+
* are reached.
|
|
22
|
+
*/
|
|
23
|
+
export const controllerManagementProtocolVersion = 1;
|
|
24
|
+
/** The methods the endpoint answers; every other method is refused before it is routed. */
|
|
25
|
+
export const controllerManagementMethods = [
|
|
26
|
+
'controller.status',
|
|
27
|
+
'controller.upgrade.launch',
|
|
28
|
+
'controller.upgrade.prepare.begin',
|
|
29
|
+
'controller.upgrade.finalize',
|
|
30
|
+
];
|
|
31
|
+
/** A management request carries a token, a version and a mode at most. */
|
|
32
|
+
export const controllerManagementRequestMaximumBodyBytes = 16 * 1024;
|
|
33
|
+
/** The status document with every harness profile and upgrade blocker it may report. */
|
|
34
|
+
export const controllerManagementResponseMaximumBytes = 1024 * 1024;
|
|
35
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFuYWdlbWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX2ludGVyZmFjZXMvbWFuYWdlbWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFPQTs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sb0NBQW9DLEdBQUcsMEJBQW1DLENBQUM7QUFFeEY7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUNBQW1DLEdBQUcsQ0FBVSxDQUFDO0FBRTlELDJGQUEyRjtBQUMzRixNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBRztJQUN6QyxtQkFBbUI7SUFDbkIsMkJBQTJCO0lBQzNCLGtDQUFrQztJQUNsQyw2QkFBNkI7Q0FDckIsQ0FBQztBQVVYLDBFQUEwRTtBQUMxRSxNQUFNLENBQUMsTUFBTSwyQ0FBMkMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ3JFLHdGQUF3RjtBQUN4RixNQUFNLENBQUMsTUFBTSx3Q0FBd0MsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agl",
|
|
3
|
-
"version": "41.
|
|
3
|
+
"version": "41.1.0",
|
|
4
4
|
"controllerUpgradeManagementVersion": 2,
|
|
5
|
+
"controllerManagementProtocolVersion": 1,
|
|
5
6
|
"private": false,
|
|
6
7
|
"description": "Agent Gateway Layer for OpenCode, FlexHarness, and Codex sessions",
|
|
7
8
|
"main": "dist_ts/index.js",
|
package/readme.md
CHANGED
|
@@ -331,6 +331,7 @@ agl proxy run claude -- # Claude Code on Claude accounts
|
|
|
331
331
|
agl proxy run claude --provider openai --model <catalog-model> --
|
|
332
332
|
agl proxy run opencode --model <catalog-model> -- run 'Explain this repository'
|
|
333
333
|
agl proxy confined run -- docker run --rm --user "$(id -u):$(id -g)" -i <image> claude -p # Claude Code in a container
|
|
334
|
+
agl proxy confined run --provider openai -- docker run --rm --user "$(id -u):$(id -g)" -i <image> claude -p
|
|
334
335
|
agl proxy confined end <run id>
|
|
335
336
|
```
|
|
336
337
|
|
|
@@ -444,7 +445,8 @@ listener, a Unix socket that serves confined runs alone (see [Confined container
|
|
|
444
445
|
- A ChatGPT pool serves `/openai/v1/responses` and `/openai/v1/models` (the pooled Codex catalog in Codex's
|
|
445
446
|
own shape when Codex names its `client_version`), and `/anthropic/v1/messages` and `/anthropic/v1/models`
|
|
446
447
|
for Claude Code on ChatGPT models. Raw Responses requests retain their storage and streaming choices.
|
|
447
|
-
Claude Code's Messages requests are translated by the shared provider package
|
|
448
|
+
Claude Code's Messages requests are translated by the shared provider package (see
|
|
449
|
+
[Claude Code on the ChatGPT pool](#claude-code-on-the-chatgpt-pool));
|
|
448
450
|
`/anthropic/v1/messages/count_tokens` answers 404 there by design, on which Claude Code estimates the count
|
|
449
451
|
itself. Other OpenAI endpoints, such as Chat Completions, and WebSocket inference are not served.
|
|
450
452
|
- A Claude pool serves Claude Code's own requests: `/anthropic/v1/messages`,
|
|
@@ -706,45 +708,112 @@ proxy's binding with it, and the sessions it served move at their next request.
|
|
|
706
708
|
pool, 16 requests in flight per session, 256 per client credential, 512 in all, 32 MiB per request and 8 MiB
|
|
707
709
|
per inspected response event (or non-streaming JSON response).
|
|
708
710
|
|
|
711
|
+
#### Claude Code on the ChatGPT pool
|
|
712
|
+
|
|
713
|
+
Claude Code on the ChatGPT pool -- `agl proxy run claude --provider openai`, a Claude terminal on ChatGPT models, a client
|
|
714
|
+
moved there with `agl proxy credential <client> --provider openai`, or a confined run with `--provider openai` -- sends
|
|
715
|
+
Anthropic Messages requests. The proxy translates each for the ChatGPT backend (`https://chatgpt.com/backend-api/codex/responses`,
|
|
716
|
+
a Responses endpoint) with the Messages translation of `@modelprofile.com/flexharness-providers` and sends it on the
|
|
717
|
+
chosen ChatGPT account's own credential.
|
|
718
|
+
|
|
719
|
+
What the translation adds to a request is what the backend requires, and no text of AGL's own:
|
|
720
|
+
|
|
721
|
+
- Claude Code's system prompt becomes the request's `instructions`, which the backend requires at its top; a system
|
|
722
|
+
message later in the conversation stays in its place as a `developer` message.
|
|
723
|
+
- `store: false` (the backend keeps no response), `stream: true` (an answer is assembled whole for a client that did not
|
|
724
|
+
ask to stream) and, for a model that reasons, `include: ["reasoning.encrypted_content"]`: the model's reasoning comes
|
|
725
|
+
back encrypted and travels in the thinking block's signature, which the proxy signs with a key of its own for the
|
|
726
|
+
account it came from. A thinking block whose signature this proxy did not write for that account is not sent back;
|
|
727
|
+
the answer counts them in `x-flexharness-thinking-dropped`.
|
|
728
|
+
- `prompt_cache_key`, Claude Code's session id (`x-claude-code-session-id`), so the backend's prompt cache keys on the
|
|
729
|
+
conversation; parallel tool calls turned off when Claude Code turns them off; Claude Code's tools as function tools,
|
|
730
|
+
a name of more than 64 characters under a short alias.
|
|
731
|
+
- The account's credential and account id. None of Claude Code's own headers go on, and never the client's credential.
|
|
732
|
+
|
|
733
|
+
What it drops: from a request, `max_tokens` (the backend takes no output limit; the answer says
|
|
734
|
+
`x-flexharness-max-tokens: not-sent`), a thinking token budget (`budget_tokens`, which has no equivalent: the effort
|
|
735
|
+
sets the depth), and Anthropic's own hints -- prompt cache markers, `metadata`, `service_tier`, citations -- which are
|
|
736
|
+
accepted without effect. What cannot be translated faithfully is refused with a 400 that names it: server tools (Claude
|
|
737
|
+
Code then continues without them), server-side context management, code execution containers, remote MCP servers and
|
|
738
|
+
deferred tool loading, whose betas `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` keeps Claude Code from sending. Of an
|
|
739
|
+
answer Claude Code gets the model's text, its tool calls, its reasoning summary as thinking, the stop reason and the
|
|
740
|
+
token counts (input, output, cache reads); the backend's response id, its own headers and the rest of its response do
|
|
741
|
+
not reach it.
|
|
742
|
+
|
|
743
|
+
Reasoning effort: Claude Code sends its effort as `output_config.effort` (`low` to `max`, chosen with `/effort`,
|
|
744
|
+
`--effort` or `CLAUDE_CODE_EFFORT_LEVEL`), together with adaptive thinking; Claude Code 2.1.288, the release of AGL's
|
|
745
|
+
Agent SDK, sends `high` when none is chosen, for a ChatGPT model's id as well. The translation sends it as the request's
|
|
746
|
+
`reasoning.effort`, fitted to the efforts the model's catalog lists: a model without the requested one gets the nearest
|
|
747
|
+
lower, and the answer names the fit in `x-flexharness-effort-fitted` (`max=>xhigh`). Thinking asks for the reasoning's
|
|
748
|
+
summary (`reasoning.summary: auto`). A request without an effort sends none, which leaves the depth to the backend.
|
|
749
|
+
|
|
750
|
+
The ChatGPT account's own side: OpenAI documents Codex's personalization as its client's -- personal instructions in
|
|
751
|
+
the Codex home's global `AGENTS.md`, memories in a local store under the Codex home (off by default) -- and ChatGPT's
|
|
752
|
+
own memory as ChatGPT on the web's (learn.chatgpt.com, *Personalize ChatGPT* and *Memories*, read 2026-10-26). On this
|
|
753
|
+
route no Codex client and no Codex home take part: a request carries Claude Code's own system prompt and messages, names
|
|
754
|
+
no personality and no stored response, and carries nothing of the account but its credential and id. Whether the
|
|
755
|
+
ChatGPT backend adds anything of the account's side -- custom instructions, memory or other personalization -- to a
|
|
756
|
+
request on its own side cannot be seen in the request or its answer, so AGL cannot know it beyond what OpenAI documents.
|
|
757
|
+
|
|
709
758
|
#### Confined containers
|
|
710
759
|
|
|
711
760
|
`agl proxy confined run` runs Claude Code in a Docker container that reaches AGL through one route only: the proxy's
|
|
712
761
|
confined listener, a Unix socket alone in a directory of its own (`agl-proxy-confined/proxy.sock` beside the proxy's
|
|
713
|
-
control socket; `agl proxy status` names it).
|
|
762
|
+
control socket; `agl proxy status` names it). Each run is served from one pool, chosen when it starts: the Claude pool,
|
|
763
|
+
or with `--provider openai` the ChatGPT pool. The listener serves the three endpoints Claude Code uses --
|
|
714
764
|
`GET /anthropic/v1/models`, `POST /anthropic/v1/messages` and `POST /anthropic/v1/messages/count_tokens` -- exactly as
|
|
715
|
-
the loopback listener serves
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
765
|
+
the loopback listener serves Claude Code on the run's pool: on the Claude pool sent on to Anthropic as Claude Code sent
|
|
766
|
+
them, on the ChatGPT pool translated (see [Claude Code on the ChatGPT pool](#claude-code-on-the-chatgpt-pool), where
|
|
767
|
+
`count_tokens` answers 404 and Claude Code estimates). It refuses every other path and method with 404 before anything
|
|
768
|
+
is read or routed, every `/openai/v1/` path of a ChatGPT-pool run among them; nothing else of AGL is reachable through
|
|
769
|
+
it. It accepts confined runs' credentials alone: a request without a credential, with one the proxy does not know, or
|
|
770
|
+
with a credential of the loopback listener (any client's or lease's) is refused with 401, and a confined run's
|
|
771
|
+
credential is refused on the loopback listener alike.
|
|
719
772
|
|
|
720
773
|
```sh
|
|
721
774
|
agl proxy confined run -- docker run --rm --user "$(id -u):$(id -g)" -i <image> \
|
|
722
775
|
claude -p --model claude-opus-5-5 --output-format json < prompt.txt
|
|
723
|
-
agl proxy
|
|
776
|
+
agl proxy confined run --provider openai -- docker run --rm --user "$(id -u):$(id -g)" -i <image> \
|
|
777
|
+
claude -p --output-format json < prompt.txt # on the ChatGPT pool's default model
|
|
778
|
+
agl proxy status # names the confined listener and lists the runs by id, with their pools
|
|
724
779
|
agl proxy confined end <run id> # ends a run's credential at once
|
|
725
780
|
```
|
|
726
781
|
|
|
727
782
|
Before the container starts, the command asks the proxy for the run's credential: one for this run alone, a lease of
|
|
728
|
-
the
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
783
|
+
the run's pool whose sessions are those of the client `confined` on that pool, so its requests are placed, moved and
|
|
784
|
+
counted (token usage under `confined`) like any other client's of that pool. Without `--provider`, or with
|
|
785
|
+
`--provider anthropic`, it is a lease of the Claude pool (the Claude accounts first, then the Anthropic API keys),
|
|
786
|
+
exactly as before ChatGPT-pool runs. With `--provider openai` it is a lease of the ChatGPT pool as
|
|
787
|
+
`agl proxy run claude --provider openai` takes one: its translated Messages requests go to the pool's ChatGPT accounts
|
|
788
|
+
alone, never to an OpenAI API key (see [ChatGPT accounts first, then OpenAI API keys](#chatgpt-accounts-first-then-openai-api-keys)),
|
|
789
|
+
and the proxy hands the run the pool's models with it. Right after `docker run` the command adds the listener's
|
|
790
|
+
directory as a read-only mount at `/run/agl-proxy` and what Claude Code inside needs to reach it, each variable passed
|
|
791
|
+
by name so the credential appears on no command line:
|
|
732
792
|
|
|
733
793
|
- `ANTHROPIC_UNIX_SOCKET=/run/agl-proxy/proxy.sock`: Claude Code sends its API requests through the socket, as plain
|
|
734
794
|
HTTP, so no relay runs in the container (Claude Code's own setting, which `claude ssh` uses; verified with Claude Code
|
|
735
795
|
2.1.288, the release of AGL's Agent SDK);
|
|
736
796
|
- `ANTHROPIC_BASE_URL=http://localhost/anthropic`: the path Claude Code puts on its requests; the host is not used;
|
|
737
797
|
- `ANTHROPIC_AUTH_TOKEN`: the run's credential;
|
|
738
|
-
- `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1` and `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, as
|
|
739
|
-
claude` sets them
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
798
|
+
- on the Claude pool, `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1` and `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, as
|
|
799
|
+
`agl proxy run claude` sets them;
|
|
800
|
+
- on the ChatGPT pool, the variables `agl proxy run claude --provider openai` sets: the pool's default model as
|
|
801
|
+
`ANTHROPIC_MODEL` and for every alias (`ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`,
|
|
802
|
+
`ANTHROPIC_DEFAULT_HAIKU_MODEL`), the context window the catalog states for it (`CLAUDE_CODE_MAX_CONTEXT_TOKENS`),
|
|
803
|
+
`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1`, `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` and
|
|
804
|
+
`CLAUDE_CODE_ATTRIBUTION_HEADER=0`. Two things of that command stay on the host: its Claude Code configuration
|
|
805
|
+
directory, which lies in the AGL home, and its `/model` picker of the pool's models, an option of Claude Code's
|
|
806
|
+
command line, which the launcher leaves as the container's command names it. Name another of the pool's models with
|
|
807
|
+
Claude Code's own `--model` in the container's command.
|
|
808
|
+
|
|
809
|
+
It prints the run's id on standard error (`AGL proxy confined run <id>: …`; a ChatGPT-pool run's line says
|
|
810
|
+
`served from the ChatGPT pool`), passes its standard input and output through, and exits with the container's status. The
|
|
811
|
+
container never receives an account token, the control socket or any other part of the AGL home: the mount holds the
|
|
812
|
+
socket alone, and the variables that would give Claude Code another login or route (`CLAUDE_CODE_OAUTH_TOKEN`,
|
|
813
|
+
`ANTHROPIC_API_KEY`, …), and on the ChatGPT pool every model variable above, are removed from the environment `docker`
|
|
814
|
+
runs with, so a `-e NAME` of one of them passes nothing. What the launcher names itself after `docker run` is its own:
|
|
815
|
+
it must not set the variables above. The command takes `docker run` and `docker container run` only, with the image and
|
|
816
|
+
its command after the options.
|
|
748
817
|
|
|
749
818
|
The socket and its directory are the proxy user's alone (0600 and 0700), so the container's processes connect as that
|
|
750
819
|
user: `--user` with its uid and gid under a Docker daemon that runs as root, or the container's own root under rootless
|
|
@@ -760,10 +829,12 @@ serves it again at once while the command takes it back. `agl proxy confined end
|
|
|
760
829
|
flight end, the next one is refused, and the command stops the container (`docker run` passes its SIGTERM on) and exits
|
|
761
830
|
with the reason; it does the same when the proxy does not hand the credential back after a restart.
|
|
762
831
|
|
|
763
|
-
Downgrade: an earlier proxy refuses to start while its store keeps a confined run's credential
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
832
|
+
Downgrade: an earlier proxy refuses to start while its store keeps a confined run's credential its check of the kept
|
|
833
|
+
leases does not know: any confined run's for a release before confined runs, a ChatGPT-pool run's for one before
|
|
834
|
+
ChatGPT-pool runs (41.0.1 and earlier). Let every confined run end before downgrading; if an earlier proxy's start
|
|
835
|
+
already failed, start this release once with no confined run going and wait out the 60-second reclaim window, which
|
|
836
|
+
ends the kept credential, then downgrade. A launcher older than ChatGPT-pool runs still runs Claude-pool runs on this
|
|
837
|
+
proxy; an older proxy refuses this release's `--provider openai`, as it issues no ChatGPT-pool run.
|
|
767
838
|
|
|
768
839
|
## Native Codex Harness
|
|
769
840
|
|
|
@@ -1875,11 +1946,14 @@ that worker, not as the driver or anonymously:
|
|
|
1875
1946
|
When the inventory shows the worker's session as another job, or the fleet no longer binds
|
|
1876
1947
|
the worker to it (archived, moved to another session), the launch is revoked for good.
|
|
1877
1948
|
An upgrade moves every bridge in place onto the new build (`process.execve`: the same PID and
|
|
1878
|
-
start, a new image from the new install's path), and the
|
|
1879
|
-
its command is the one recorded with the launch or the one
|
|
1880
|
-
itself from its configured `agl` MCP entry, never one the
|
|
1881
|
-
kept with the binding's next revision.
|
|
1882
|
-
|
|
1949
|
+
start, a new image `node <new cli.js> mcp --port <port>` from the new install's path), and the
|
|
1950
|
+
image it became is proved afresh: its command is the one recorded with the launch or the one
|
|
1951
|
+
the restarted controller resolves itself from its configured `agl` MCP entry, never one the
|
|
1952
|
+
bridge names, and a new command is kept with the binding's next revision. Its arguments are
|
|
1953
|
+
that command's or exactly `mcp --port <port>` with the port the controller is configured
|
|
1954
|
+
with. Each reason an enrollment stays unverified is written once to the controller log,
|
|
1955
|
+
naming the worker and its session, never the launch ID or a credential; a bridge refused
|
|
1956
|
+
under both commands has both reasons, the recorded command's first.
|
|
1883
1957
|
|
|
1884
1958
|
The bridge exchanges credentials only through the private controller endpoint after Linux
|
|
1885
1959
|
direct-loopback socket-inode ownership, approved executable/argv and current native
|
|
@@ -2833,6 +2907,7 @@ The upgrade worker:
|
|
|
2833
2907
|
- Restarts `agl-proxy.service` onto the new release once the target controller is up, when the unit is installed and running and runs the pnpm-global `agl` shim, and waits until the proxy answers as the new release and its credential launcher, run with an empty environment as a harness runs it, runs the new release too. A unit that is not installed or not running is left alone. A restart that fails does not fail the upgrade: it is logged as a warning and appended to the completion message (`message` in `--json` output).
|
|
2834
2908
|
- Moves the accounts service onto the authswitch the new AGL brings, with authswitch's own `authority service stop`, `install` and `start`: the service is stopped before the package changes and started again, on the new release, when it ran before. A release that declares no `authswitch` command is refused before anything is stopped. A host without the service's unit keeps it that way, and the service's store is never touched. Stopping the service also stops the managed Codex runtimes it runs; they start again when next asked for. The log names what was stopped and started; a failed upgrade restores the previous AGL first, then the running service, and each step is recorded in the upgrade transaction, so recovering a lost worker finishes or undoes it.
|
|
2835
2909
|
- Preserves its transaction across worker or controller failure.
|
|
2910
|
+
- Reads the controller's status, prepares it and finalizes it through the controller's loopback management endpoint (an HTTP `POST` of one TypedRequest to `/management/typedrequest` on the controller's port, addressed to `127.0.0.1:<port>`), never through the browser's WebSocket, so the worker of one release finalizes the controller of the next whatever transport that release's browser speaks. `agl status` and `agl upgrade` read their own release's controller the same way. The endpoint is used only for a controller that declares it: before the controller runs, by `controllerManagementProtocolVersion` in the `package.json` of the installed release that starts it, and once it runs, by `managementProtocolVersion` in its status. A controller that declares neither, as every release before the endpoint, such as a source an upgrade recovery restores, is reached over its controller socket, which keeps serving the status read and the upgrade methods for the workers of earlier releases. Nothing is tried over one transport and then the other.
|
|
2836
2911
|
|
|
2837
2912
|
The cohort is every conversation with an AGL prompt that is queued or still running when the upgrade starts. AGL seals prompt admission, so no new message is accepted and no queue starts a turn, and stops each of them, as **Stop** does:
|
|
2838
2913
|
|
|
@@ -2854,6 +2929,16 @@ Upgrade coordination lives under `$AGL_HOME/upgrade`. Private logs live under `$
|
|
|
2854
2929
|
|
|
2855
2930
|
A failure reference from the Errors dialog appears in those logs on the line the controller wrote when the failure happened, together with the operation and the cause. The journal the dialog reads it from is in memory only, so it is gone after a controller restart while the log line remains.
|
|
2856
2931
|
|
|
2932
|
+
### Rolling back from a later release
|
|
2933
|
+
|
|
2934
|
+
A later release keeps a port's trusted-proxy secret sealed in the controller's database and never exports it, and `agl remote set` there can store gateway sources, which 41.1.x cannot enforce. To roll back a port that a later release serves behind a TLS reverse proxy:
|
|
2935
|
+
|
|
2936
|
+
1. On the later release, stop the controller and run `agl remote unset --port <port>`. It moves the port back to `http://localhost:<port>` and removes its passkey, so the next start prints a setup code. A 41.1.x controller refuses to start on a configuration with gateway sources, and its refusal names this command.
|
|
2937
|
+
2. Roll back and start the controller.
|
|
2938
|
+
3. To serve 41.1.x behind the proxy again, generate a new secret and set it both on the proxy route (`X-Harness-Controller-Proxy-Secret`) and in `AGL_TRUSTED_PROXY_SECRET`, as in [Remote Browser Access](#remote-browser-access). 41.1.x fixes a port's public origin and TLS mode on its first start, so that is a port that has not been started yet.
|
|
2939
|
+
|
|
2940
|
+
Audit events of types a later release wrote stay in the audit log and are pruned with the others.
|
|
2941
|
+
|
|
2857
2942
|
### Migrating From `hcon`
|
|
2858
2943
|
|
|
2859
2944
|
Starting from `@modelprofile.com/harness-controller@20.0.2`, run the active command twice:
|
|
@@ -2986,13 +3071,15 @@ Backend isolation is mandatory. Proxy mode binds the controller to all interface
|
|
|
2986
3071
|
- Local mode binds the controller to loopback only.
|
|
2987
3072
|
- The private OpenCode server always remains on loopback and uses a generated per-process password.
|
|
2988
3073
|
- Application RPC starts only after the exact TypedSocket package-major handshake.
|
|
3074
|
+
- The management endpoint (`/management/typedrequest`, used by the CLI and the upgrade worker) answers only an HTTP `POST` of `application/json` from a loopback peer (`127.0.0.1` or `::1`), addressed to `127.0.0.1:<port>`, with no `Origin` and no forwarding header (`Forwarded`, `X-Forwarded-*`, `X-Real-IP`, `X-Harness-Controller-Proxy-Secret`), so a browser or a request a proxy forwards is refused, also in proxy mode. It serves only the status read and the upgrade methods; each upgrade method authorises itself against the upgrade token and its one-time grant, as it does over the socket, and every upgrade request counts against the controller's global unauthenticated preflight limit.
|
|
2989
3075
|
- Client-managed connection tags are disabled; authentication and routing use server-owned peer state.
|
|
2990
3076
|
- Setup attempts are globally rate limited before enrollment.
|
|
2991
3077
|
- Permission auto-accept is an explicit server policy, never an assumption based on an open browser.
|
|
2992
3078
|
- Questions are never auto-answered.
|
|
2993
3079
|
- Unknown, malformed, concurrent, or unverifiable lifecycle state fails closed.
|
|
2994
3080
|
- The inference proxy's confined listener is a Unix socket of the proxy's user alone; it accepts only confined runs'
|
|
2995
|
-
credentials and serves them only
|
|
3081
|
+
credentials and serves them only Claude Code's three endpoints of the run's pool, the Claude pool or, translated, the
|
|
3082
|
+
ChatGPT pool's ChatGPT accounts.
|
|
2996
3083
|
|
|
2997
3084
|
The global **auto-accept permissions (yolo)** setting replies once to pending permissions across registered projects and writes those replies to the audit log. Each conversation also has a run-local Ask/Yolo switch. For an active managed OpenCode root, that switch covers its currently active direct children under the same exact family authority. For an active managed Flex root, it covers admitted descendants through their exact active parent-run authority chain; a relationship alone grants nothing. Terminal children are never auto-accepted. Disabling either policy seals new automatic replies and drains already admitted replies before returning. Neither mode creates persistent broad provider grants.
|
|
2998
3085
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3770,6 +3770,10 @@ export const AGL_API_SURFACE: plugins.flexTypedRequest.IFlexTypedRequestSurface
|
|
|
3770
3770
|
"upgradeManagementVersion": {
|
|
3771
3771
|
"type": "number"
|
|
3772
3772
|
},
|
|
3773
|
+
"managementProtocolVersion": {
|
|
3774
|
+
"type": "number",
|
|
3775
|
+
"description": "The protocol of the controller's loopback management endpoint (`controllerManagementProtocolVersion`); absent from a controller older than the endpoint, which is reached over its socket."
|
|
3776
|
+
},
|
|
3773
3777
|
"controllerPid": {
|
|
3774
3778
|
"type": "number"
|
|
3775
3779
|
},
|
|
@@ -3887,7 +3891,7 @@ export const AGL_API_SURFACE: plugins.flexTypedRequest.IFlexTypedRequestSurface
|
|
|
3887
3891
|
],
|
|
3888
3892
|
"additionalProperties": false
|
|
3889
3893
|
},
|
|
3890
|
-
"declaration": "interface IControllerStatus {\n packageName: string;\n packageVersion: string;\n protocolVersion: 46;\n upgradeManagementVersion: number;\n controllerPid: number;\n processGroupId: number;\n processFingerprint: string;\n processMode: TControllerProcessMode;\n lifecycleState: TControllerLifecycleState;\n startedAt: number;\n setupRequired: boolean;\n /** Present when the controller runs as the systemd user unit (`agl service install`); absent for a detached or foreground one. */\n serviceUnit?: \"agl-controller.service\";\n /** The TLS mode of the runtime configuration the controller runs with; absent until it is resolved. */\n tlsMode?: TControllerTlsMode;\n upgrade?: IControllerUpgradeStatus;\n /** AGL's inference proxy, by state only; details need a signed-in owner (`controller.proxy.status.get`). */\n proxy?: { state: TControllerProxyState };\n harnesses: Array<{\n harnessId: TControllerSessionHarnessId;\n pid?: number;\n state: TControllerHarnessLifecycleState;\n healthy: boolean;\n version?: string;\n supportsLocalAttachments?: boolean;\n connectionMode?: 'local' | 'shared' | 'remote';\n diagnostic?: IControllerCodexDiagnostic;\n /** Codex only: automatic reconnection of this entry's profile to the local shared app-server. */\n rejoin?: IControllerCodexRejoinStatus;\n /**\n * Codex only, on the controller status: every connected or required profile. The entry's\n * `healthy` is then true only when each of them is, apart from a profile on an account Codex\n * cannot run on now, listed `stopped` with an `account_unavailable` diagnostic that says why;\n * its other members describe the default profile.\n */\n profiles?: IControllerCodexProfileStatus[];\n }>;\n}"
|
|
3894
|
+
"declaration": "interface IControllerStatus {\n packageName: string;\n packageVersion: string;\n protocolVersion: 46;\n upgradeManagementVersion: number;\n /**\n * The protocol of the controller's loopback management endpoint (`controllerManagementProtocolVersion`); absent\n * from a controller older than the endpoint, which is reached over its socket.\n */\n managementProtocolVersion?: number;\n controllerPid: number;\n processGroupId: number;\n processFingerprint: string;\n processMode: TControllerProcessMode;\n lifecycleState: TControllerLifecycleState;\n startedAt: number;\n setupRequired: boolean;\n /** Present when the controller runs as the systemd user unit (`agl service install`); absent for a detached or foreground one. */\n serviceUnit?: \"agl-controller.service\";\n /** The TLS mode of the runtime configuration the controller runs with; absent until it is resolved. */\n tlsMode?: TControllerTlsMode;\n upgrade?: IControllerUpgradeStatus;\n /** AGL's inference proxy, by state only; details need a signed-in owner (`controller.proxy.status.get`). */\n proxy?: { state: TControllerProxyState };\n harnesses: Array<{\n harnessId: TControllerSessionHarnessId;\n pid?: number;\n state: TControllerHarnessLifecycleState;\n healthy: boolean;\n version?: string;\n supportsLocalAttachments?: boolean;\n connectionMode?: 'local' | 'shared' | 'remote';\n diagnostic?: IControllerCodexDiagnostic;\n /** Codex only: automatic reconnection of this entry's profile to the local shared app-server. */\n rejoin?: IControllerCodexRejoinStatus;\n /**\n * Codex only, on the controller status: every connected or required profile. The entry's\n * `healthy` is then true only when each of them is, apart from a profile on an account Codex\n * cannot run on now, listed `stopped` with an `account_unavailable` diagnostic that says why;\n * its other members describe the default profile.\n */\n profiles?: IControllerCodexProfileStatus[];\n }>;\n}"
|
|
3891
3895
|
},
|
|
3892
3896
|
{
|
|
3893
3897
|
"name": "IControllerTerminalResource",
|
package/ts/classes.authmodels.ts
CHANGED
|
@@ -69,6 +69,41 @@ const credentialTransports = new Set([
|
|
|
69
69
|
'usb',
|
|
70
70
|
]);
|
|
71
71
|
const auditEventTypes: ReadonlySet<string> = new Set(controllerAuditEventTypes);
|
|
72
|
+
/**
|
|
73
|
+
* Any release's audit type, as the known ones are spelled: two or more dot-separated segments of lowercase letters,
|
|
74
|
+
* digits and inner hyphens, each starting with a letter (`session.pendingprompt.edit`,
|
|
75
|
+
* `provider.connection.activate-opencode`), at most 128 characters.
|
|
76
|
+
*/
|
|
77
|
+
const auditEventTypePattern = /^[a-z](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z](?:[a-z0-9-]*[a-z0-9])?)+$/;
|
|
78
|
+
const auditEventTypeMaxLength = 128;
|
|
79
|
+
const auditDetailsMaxDepth = 8;
|
|
80
|
+
const auditDetailsMaxBytes = 4096;
|
|
81
|
+
|
|
82
|
+
/** Plain JSON: plain objects, dense arrays, strings, finite numbers, booleans and null, nested at most `depthArg` deep. */
|
|
83
|
+
const isBoundedPlainJson = (valueArg: unknown, depthArg: number): boolean => {
|
|
84
|
+
if (valueArg === null || typeof valueArg === 'string' || typeof valueArg === 'boolean') return true;
|
|
85
|
+
if (typeof valueArg === 'number') return Number.isFinite(valueArg);
|
|
86
|
+
if (depthArg < 1) return false;
|
|
87
|
+
if (Array.isArray(valueArg)) {
|
|
88
|
+
if (Object.getPrototypeOf(valueArg) !== Array.prototype) return false;
|
|
89
|
+
for (let index = 0; index < valueArg.length; index++) {
|
|
90
|
+
if (!Object.hasOwn(valueArg, index) || !isBoundedPlainJson(valueArg[index], depthArg - 1)) return false;
|
|
91
|
+
}
|
|
92
|
+
return Object.keys(valueArg).length === valueArg.length;
|
|
93
|
+
}
|
|
94
|
+
return isPlainObject(valueArg)
|
|
95
|
+
&& Object.getOwnPropertySymbols(valueArg).length === 0
|
|
96
|
+
&& Object.values(valueArg).every((entryArg) => isBoundedPlainJson(entryArg, depthArg - 1));
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* An audit event's `details`: this release never writes or reads them; a later release records what its own types
|
|
101
|
+
* add there. Bounded plain JSON: an object at most eight levels deep and 4 KiB serialized.
|
|
102
|
+
*/
|
|
103
|
+
const isAuditDetails = (valueArg: unknown): valueArg is Record<string, unknown> =>
|
|
104
|
+
isPlainObject(valueArg)
|
|
105
|
+
&& isBoundedPlainJson(valueArg, auditDetailsMaxDepth)
|
|
106
|
+
&& Buffer.byteLength(JSON.stringify(valueArg), 'utf8') <= auditDetailsMaxBytes;
|
|
72
107
|
const auditOutcomes = new Set(['attempted', 'succeeded', 'denied', 'failed']);
|
|
73
108
|
const auditRetentionSeconds = 90 * 24 * 60 * 60;
|
|
74
109
|
|
|
@@ -206,10 +241,29 @@ const isStoredCredential = (valueArg: unknown): valueArg is IStoredPasskeyCreden
|
|
|
206
241
|
&& isBase64Url(valueArg.counterUpdateId, 32);
|
|
207
242
|
};
|
|
208
243
|
|
|
244
|
+
/**
|
|
245
|
+
* The stored runtime configuration names `gatewaySources`, which only a later release writes (`agl remote set` with
|
|
246
|
+
* gateway sources). This release cannot enforce them, so it refuses the document; the message names the one remedy.
|
|
247
|
+
*/
|
|
248
|
+
export class ControllerRuntimeConfigFromLaterReleaseError extends Error {
|
|
249
|
+
constructor(controllerPortArg: unknown) {
|
|
250
|
+
const port = isValidPort(controllerPortArg) ? String(controllerPortArg) : '<port>';
|
|
251
|
+
super(
|
|
252
|
+
`The stored runtime configuration of controller:${port} names gatewaySources, which a later AGL release set `
|
|
253
|
+
+ 'and this release cannot enforce, so it does not start. Run '
|
|
254
|
+
+ `\`agl remote unset --port ${port}\` on the later release before rolling back.`,
|
|
255
|
+
);
|
|
256
|
+
this.name = 'ControllerRuntimeConfigFromLaterReleaseError';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
209
260
|
export const assertControllerAuthDocument = (
|
|
210
261
|
valueArg: unknown,
|
|
211
262
|
): asserts valueArg is IControllerAuthDocument => {
|
|
212
263
|
if (!isPlainObject(valueArg)) throw new Error('Invalid controller authentication document.');
|
|
264
|
+
if (isPlainObject(valueArg.config) && Object.hasOwn(valueArg.config, 'gatewaySources')) {
|
|
265
|
+
throw new ControllerRuntimeConfigFromLaterReleaseError(valueArg.config.controllerPort);
|
|
266
|
+
}
|
|
213
267
|
if (
|
|
214
268
|
!hasExactKeys(valueArg, [
|
|
215
269
|
'id',
|
|
@@ -315,36 +369,48 @@ export const assertWebAuthnCeremonyDocument = (
|
|
|
315
369
|
}
|
|
316
370
|
};
|
|
317
371
|
|
|
372
|
+
/**
|
|
373
|
+
* A stored audit event. The log is shared with later releases, so a type this release does not know (a later
|
|
374
|
+
* release's, read back after a rollback) is accepted when it is spelled as an audit type: its envelope is checked as
|
|
375
|
+
* strictly as a known type's, and only the rules that tie a field to particular types (`senderLabel` to
|
|
376
|
+
* `session.message`, `localCli` to `configuration.*`) are left to the release that knows the type. A known type keeps
|
|
377
|
+
* every check. `details` is bounded plain JSON on any type.
|
|
378
|
+
*/
|
|
318
379
|
export const assertControllerAuditEventDocument = (
|
|
319
380
|
valueArg: unknown,
|
|
320
381
|
): asserts valueArg is IControllerAuditEvent => {
|
|
321
382
|
if (!isPlainObject(valueArg)) throw new Error('Invalid controller audit event.');
|
|
383
|
+
const type = valueArg.type;
|
|
384
|
+
if (typeof type !== 'string' || type.length > auditEventTypeMaxLength || !auditEventTypePattern.test(type)) {
|
|
385
|
+
throw new Error('Invalid controller audit event.');
|
|
386
|
+
}
|
|
387
|
+
const known = auditEventTypes.has(type);
|
|
322
388
|
if (
|
|
323
389
|
!hasExactKeys(
|
|
324
390
|
valueArg,
|
|
325
391
|
['id', 'controllerId', 'timestamp', 'type', 'outcome'],
|
|
326
|
-
['operationId', 'peerId', 'credentialId', 'sessionId', 'requestId', 'senderLabel', 'localCli'],
|
|
392
|
+
['operationId', 'peerId', 'credentialId', 'sessionId', 'requestId', 'senderLabel', 'localCli', 'details'],
|
|
327
393
|
)
|
|
328
394
|
|| !isBase64Url(valueArg.id, 32)
|
|
329
395
|
|| !isNonEmptyString(valueArg.controllerId)
|
|
330
396
|
|| !/^controller:[1-9][0-9]{0,4}$/.test(valueArg.controllerId)
|
|
331
397
|
|| Number(valueArg.controllerId.slice('controller:'.length)) > 65535
|
|
332
398
|
|| !isValidDate(valueArg.timestamp)
|
|
333
|
-
|| !auditEventTypes.has(valueArg.type as string)
|
|
334
399
|
|| !auditOutcomes.has(valueArg.outcome as string)
|
|
335
400
|
|| (
|
|
336
401
|
Object.hasOwn(valueArg, 'operationId')
|
|
337
|
-
&& !isBase64Url(valueArg.operationId,
|
|
402
|
+
&& !isBase64Url(valueArg.operationId, type === 'resource.browser.operation' ? 18 : 24)
|
|
338
403
|
)
|
|
339
404
|
|| (
|
|
340
405
|
Object.hasOwn(valueArg, 'senderLabel')
|
|
341
|
-
&& (
|
|
406
|
+
&& ((known && type !== 'session.message') || !isControllerAgentMessageSender(valueArg.senderLabel))
|
|
342
407
|
)
|
|
408
|
+
|| (Object.hasOwn(valueArg, 'details') && !isAuditDetails(valueArg.details))
|
|
343
409
|
) {
|
|
344
410
|
throw new Error('Invalid controller audit event.');
|
|
345
411
|
}
|
|
346
412
|
if (Object.hasOwn(valueArg, 'localCli')) {
|
|
347
|
-
if (!
|
|
413
|
+
if (known && !type.startsWith('configuration.')) throw new Error('Invalid local configuration audit event.');
|
|
348
414
|
assertConfigurationLocalCliActor(valueArg.localCli);
|
|
349
415
|
}
|
|
350
416
|
for (const optionalKey of [
|
|
@@ -1919,4 +1985,7 @@ export class ControllerAuditEventModel
|
|
|
1919
1985
|
|
|
1920
1986
|
@plugins.nosqldb.svDb()
|
|
1921
1987
|
public localCli?: IControllerAuditEvent['localCli'];
|
|
1988
|
+
|
|
1989
|
+
@plugins.nosqldb.svDb()
|
|
1990
|
+
public details?: IControllerAuditEvent['details'];
|
|
1922
1991
|
}
|