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.
Files changed (82) hide show
  1. package/changelog.md +18 -0
  2. package/dist_serve/bundle.js +881 -881
  3. package/dist_serve/bundle.js.map +2 -2
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/aglapi/aglapisurface.document.js +6 -2
  6. package/dist_ts/classes.authmodels.d.ts +15 -0
  7. package/dist_ts/classes.authmodels.js +78 -7
  8. package/dist_ts/classes.authstore.js +23 -6
  9. package/dist_ts/classes.cli.js +8 -3
  10. package/dist_ts/classes.controller.d.ts +13 -0
  11. package/dist_ts/classes.controller.js +52 -15
  12. package/dist_ts/classes.controllermanagement.d.ts +53 -6
  13. package/dist_ts/classes.controllermanagement.js +180 -72
  14. package/dist_ts/classes.inferencegateway.d.ts +6 -6
  15. package/dist_ts/classes.inferencegateway.js +25 -13
  16. package/dist_ts/classes.mcpreexec.js +4 -4
  17. package/dist_ts/classes.processinspection.d.ts +3 -1
  18. package/dist_ts/classes.processinspection.js +4 -2
  19. package/dist_ts/classes.proxycontrol.d.ts +22 -7
  20. package/dist_ts/classes.proxycontrol.js +5 -3
  21. package/dist_ts/classes.proxycontrolclient.d.ts +4 -3
  22. package/dist_ts/classes.proxycontrolclient.js +7 -5
  23. package/dist_ts/classes.proxydaemon.d.ts +6 -4
  24. package/dist_ts/classes.proxydaemon.js +42 -17
  25. package/dist_ts/classes.proxyleaseholder.d.ts +11 -2
  26. package/dist_ts/classes.proxyleaseholder.js +25 -2
  27. package/dist_ts/classes.proxystore.d.ts +3 -2
  28. package/dist_ts/classes.proxystore.js +5 -2
  29. package/dist_ts/classes.upgradecoordinator.d.ts +2 -1
  30. package/dist_ts/classes.upgradecoordinator.js +13 -10
  31. package/dist_ts/classes.upgradetransaction.d.ts +6 -0
  32. package/dist_ts/classes.upgradetransaction.js +38 -13
  33. package/dist_ts/classes.workercallers.d.ts +2 -0
  34. package/dist_ts/classes.workercallers.js +11 -8
  35. package/dist_ts/functions.controllermanagementsurface.d.ts +37 -0
  36. package/dist_ts/functions.controllermanagementsurface.js +110 -0
  37. package/dist_ts/functions.inferencecli.d.ts +6 -3
  38. package/dist_ts/functions.inferencecli.js +9 -5
  39. package/dist_ts/functions.inferencecliarguments.d.ts +1 -1
  40. package/dist_ts/functions.inferencecliarguments.js +19 -7
  41. package/dist_ts/functions.inferencelaunch.d.ts +30 -9
  42. package/dist_ts/functions.inferencelaunch.js +49 -23
  43. package/dist_ts/functions.workerprocessproof.d.ts +12 -3
  44. package/dist_ts/functions.workerprocessproof.js +17 -6
  45. package/dist_ts/interfaces.auth.d.ts +14 -3
  46. package/dist_ts/interfaces.auth.js +4 -3
  47. package/dist_ts_interfaces/index.d.ts +1 -0
  48. package/dist_ts_interfaces/index.js +2 -1
  49. package/dist_ts_interfaces/interfaces.d.ts +5 -0
  50. package/dist_ts_interfaces/interfaces.js +1 -1
  51. package/dist_ts_interfaces/management.d.ts +33 -0
  52. package/dist_ts_interfaces/management.js +35 -0
  53. package/package.json +2 -1
  54. package/readme.md +118 -31
  55. package/ts/00_commitinfo_data.ts +1 -1
  56. package/ts/aglapi/aglapisurface.document.ts +5 -1
  57. package/ts/classes.authmodels.ts +74 -5
  58. package/ts/classes.authstore.ts +24 -4
  59. package/ts/classes.cli.ts +9 -2
  60. package/ts/classes.controller.ts +99 -60
  61. package/ts/classes.controllermanagement.ts +284 -107
  62. package/ts/classes.inferencegateway.ts +28 -17
  63. package/ts/classes.mcpreexec.ts +3 -3
  64. package/ts/classes.processinspection.ts +2 -1
  65. package/ts/classes.proxycontrol.ts +25 -9
  66. package/ts/classes.proxycontrolclient.ts +7 -4
  67. package/ts/classes.proxydaemon.ts +43 -17
  68. package/ts/classes.proxyleaseholder.ts +36 -7
  69. package/ts/classes.proxystore.ts +8 -3
  70. package/ts/classes.upgradecoordinator.ts +20 -11
  71. package/ts/classes.upgradetransaction.ts +53 -2
  72. package/ts/classes.workercallers.ts +12 -7
  73. package/ts/functions.controllermanagementsurface.ts +149 -0
  74. package/ts/functions.inferencecli.ts +15 -7
  75. package/ts/functions.inferencecliarguments.ts +16 -6
  76. package/ts/functions.inferencelaunch.ts +57 -23
  77. package/ts/functions.workerprocessproof.ts +17 -3
  78. package/ts/interfaces.auth.ts +14 -3
  79. package/ts_interfaces/index.ts +1 -0
  80. package/ts_interfaces/interfaces.ts +5 -0
  81. package/ts_interfaces/management.ts +52 -0
  82. 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX2ludGVyZmFjZXMvaW50ZXJmYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFPQSxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxFQUFXLENBQUM7QUFDckQsTUFBTSxDQUFDLE1BQU0sa0NBQWtDLEdBQUcsQ0FBVSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLEtBQWMsQ0FBQztBQUNwRCx5R0FBeUc7QUFDekcsTUFBTSxDQUFDLE1BQU0seUJBQXlCLEdBQUcsd0JBQWlDLENBQUM7QUFDM0UsTUFBTSxDQUFDLE1BQU0sMkJBQTJCLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNyRCxNQUFNLENBQUMsTUFBTSxrQ0FBa0MsR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLHFDQUFxQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFDL0QsTUFBTSxDQUFDLE1BQU0sbUNBQW1DLEdBQUcsRUFBRSxDQUFDO0FBQ3RELE1BQU0sQ0FBQyxNQUFNLCtCQUErQixHQUFHLEdBQUcsQ0FBQztBQUNuRCxNQUFNLENBQUMsTUFBTSxnQ0FBZ0MsR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDO0FBQzNEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSwrQkFBK0IsR0FDMUMsOERBQXVFLENBQUM7QUFDMUUsTUFBTSxDQUFDLE1BQU0sMkJBQTJCLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNyRCxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRyxDQUFDLENBQUM7QUFDL0MsTUFBTSxDQUFDLE1BQU0saUNBQWlDLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDbEUsTUFBTSxDQUFDLE1BQU0sc0NBQXNDLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDdkUsTUFBTSxD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
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.0.0",
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). The listener serves the Claude pool's three endpoints Claude Code uses --
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 a Claude client, and refuses every other path, method and pool with 404 before anything is
716
- read or routed; nothing else of AGL is reachable through it. It accepts confined runs' credentials alone: a request
717
- without a credential, with one the proxy does not know, or with a credential of the loopback listener (any client's or
718
- lease's) is refused with 401, and a confined run's credential is refused on the loopback listener alike.
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 status # names the confined listener and lists the runs by id
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 Claude pool (the Claude accounts first, then the Anthropic API keys) whose sessions are those of the client
729
- `confined`, so its requests are placed, moved and counted (token usage under `confined`) like any other Claude
730
- client's. Right after `docker run` it adds the listener's directory as a read-only mount at `/run/agl-proxy` and what
731
- Claude Code inside needs to reach it, each variable passed by name so the credential appears on no command line:
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 `agl proxy run
739
- claude` sets them.
740
-
741
- It prints the run's id on standard error (`AGL proxy confined run <id>: …`), passes its standard input and output
742
- through, and exits with the container's status. The container never receives an account token, the control socket or
743
- any other part of the AGL home: the mount holds the socket alone, and the variables that would give Claude Code another
744
- login or route (`CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`, …) are removed from the environment `docker` runs with,
745
- so a `-e NAME` of one of them passes nothing. What the launcher names itself after `docker run` is its own: it must not
746
- set the variables above. The command takes `docker run` and `docker container run` only, with the image and its
747
- command after the options.
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, which its check of the
764
- kept leases does not know. Let every confined run end before downgrading; if an earlier proxy's start already failed,
765
- start this release once with no confined run going and wait out the 60-second reclaim window, which ends the kept
766
- credential, then downgrade.
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 image it became is proved afresh:
1879
- its command is the one recorded with the launch or the one the restarted controller resolves
1880
- itself from its configured `agl` MCP entry, never one the bridge names, and a new command is
1881
- kept with the binding's next revision. Each reason an enrollment stays unverified is written once to the controller log,
1882
- naming the worker and its session, never the launch ID or a credential.
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 the Claude pool's three Claude Code endpoints.
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
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: 'agl',
6
- version: '41.0.0',
6
+ version: '41.1.0',
7
7
  description: 'Agent Gateway Layer for OpenCode, FlexHarness, and Codex sessions'
8
8
  }
@@ -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",
@@ -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, valueArg.type === 'resource.browser.operation' ? 18 : 24)
402
+ && !isBase64Url(valueArg.operationId, type === 'resource.browser.operation' ? 18 : 24)
338
403
  )
339
404
  || (
340
405
  Object.hasOwn(valueArg, 'senderLabel')
341
- && (valueArg.type !== 'session.message' || !isControllerAgentMessageSender(valueArg.senderLabel))
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 (!(valueArg.type as string).startsWith('configuration.')) throw new Error('Invalid local configuration audit event.');
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
  }