agl 40.5.1 → 41.0.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 (137) hide show
  1. package/changelog.md +43 -0
  2. package/dist_serve/bundle.js +3514 -3357
  3. package/dist_serve/bundle.js.map +2 -2
  4. package/dist_serve/bundle.js.third-party-notices.json +58 -32
  5. package/dist_serve/bundle.js.third-party-notices.txt +108 -33
  6. package/dist_ts/00_commitinfo_data.js +1 -1
  7. package/dist_ts/aglapi/aglapisurface.document.js +64 -37
  8. package/dist_ts/classes.aglhome.d.ts +5 -0
  9. package/dist_ts/classes.aglhome.js +2 -1
  10. package/dist_ts/classes.authstore.js +5 -1
  11. package/dist_ts/classes.controller.d.ts +8 -0
  12. package/dist_ts/classes.controller.js +119 -22
  13. package/dist_ts/classes.controllereventdelivery.d.ts +72 -0
  14. package/dist_ts/classes.controllereventdelivery.js +233 -0
  15. package/dist_ts/classes.flexmodels.public.js +4 -1
  16. package/dist_ts/classes.flexprojectionreader.d.ts +6 -0
  17. package/dist_ts/classes.flexprojectionreader.js +29 -5
  18. package/dist_ts/classes.flexservice.d.ts +2 -1
  19. package/dist_ts/classes.flexservice.js +34 -6
  20. package/dist_ts/classes.flexstore.d.ts +41 -4
  21. package/dist_ts/classes.flexstore.js +266 -203
  22. package/dist_ts/classes.inferencegateway.d.ts +35 -1
  23. package/dist_ts/classes.inferencegateway.js +86 -26
  24. package/dist_ts/classes.inferenceservice.d.ts +4 -2
  25. package/dist_ts/classes.inferenceservice.js +5 -4
  26. package/dist_ts/classes.proxycontrol.d.ts +58 -4
  27. package/dist_ts/classes.proxycontrol.js +23 -44
  28. package/dist_ts/classes.proxycontrolclient.d.ts +15 -5
  29. package/dist_ts/classes.proxycontrolclient.js +67 -17
  30. package/dist_ts/classes.proxydaemon.d.ts +22 -3
  31. package/dist_ts/classes.proxydaemon.js +73 -12
  32. package/dist_ts/classes.proxyleaseholder.d.ts +43 -14
  33. package/dist_ts/classes.proxyleaseholder.js +66 -22
  34. package/dist_ts/classes.proxystore.d.ts +9 -0
  35. package/dist_ts/classes.proxystore.js +15 -7
  36. package/dist_ts/classes.ticketnotifier.js +4 -2
  37. package/dist_ts/classes.ticketstore.d.ts +50 -2
  38. package/dist_ts/classes.ticketstore.js +195 -78
  39. package/dist_ts/classes.workercallerbindings.d.ts +66 -0
  40. package/dist_ts/classes.workercallerbindings.js +211 -0
  41. package/dist_ts/classes.workercallers.d.ts +63 -2
  42. package/dist_ts/classes.workercallers.js +430 -65
  43. package/dist_ts/driver/classes.drivereventrelay.d.ts +13 -1
  44. package/dist_ts/driver/classes.drivereventrelay.js +99 -6
  45. package/dist_ts/driver/functions.driverinstructions.d.ts +1 -1
  46. package/dist_ts/driver/functions.driverinstructions.js +1 -1
  47. package/dist_ts/driver/functions.drivertools.d.ts +11 -2
  48. package/dist_ts/driver/functions.drivertools.js +19 -9
  49. package/dist_ts/fleet/classes.boardpump.d.ts +8 -2
  50. package/dist_ts/fleet/classes.boardpump.js +38 -20
  51. package/dist_ts/fleet/classes.ticketeffects.d.ts +17 -2
  52. package/dist_ts/fleet/classes.ticketeffects.js +16 -10
  53. package/dist_ts/fleet/classes.workerlaunches.d.ts +12 -5
  54. package/dist_ts/fleet/classes.workerlaunches.js +25 -19
  55. package/dist_ts/fleet/classes.workers.d.ts +21 -0
  56. package/dist_ts/fleet/classes.workers.js +110 -17
  57. package/dist_ts/fleet/functions.workervalidation.js +2 -2
  58. package/dist_ts/functions.flexprivatesnapshot.d.ts +102 -0
  59. package/dist_ts/functions.flexprivatesnapshot.js +461 -0
  60. package/dist_ts/functions.inferencecli.d.ts +3 -2
  61. package/dist_ts/functions.inferencecli.js +67 -5
  62. package/dist_ts/functions.inferencecliarguments.d.ts +6 -2
  63. package/dist_ts/functions.inferencecliarguments.js +29 -2
  64. package/dist_ts/functions.inferencelaunch.d.ts +24 -0
  65. package/dist_ts/functions.inferencelaunch.js +42 -2
  66. package/dist_ts/functions.mcptickettools.d.ts +3 -0
  67. package/dist_ts/functions.mcptickettools.js +2 -2
  68. package/dist_ts/functions.proxycontract.d.ts +11 -0
  69. package/dist_ts/functions.proxycontract.js +12 -1
  70. package/dist_ts/functions.proxysocket.d.ts +6 -0
  71. package/dist_ts/functions.proxysocket.js +46 -0
  72. package/dist_ts/functions.ticketcli.d.ts +1 -1
  73. package/dist_ts/functions.ticketcli.js +8 -4
  74. package/dist_ts/functions.ticketvalidation.d.ts +3 -0
  75. package/dist_ts/functions.ticketvalidation.js +7 -4
  76. package/dist_ts/functions.workerprocessproof.d.ts +6 -0
  77. package/dist_ts/functions.workerprocessproof.js +28 -4
  78. package/dist_ts/interfaces.flexipc.d.ts +21 -2
  79. package/dist_ts/interfaces.flexipc.js +33 -16
  80. package/dist_ts_interfaces/fleetworkers.d.ts +6 -0
  81. package/dist_ts_interfaces/interfaces.d.ts +1 -1
  82. package/dist_ts_interfaces/interfaces.js +1 -1
  83. package/dist_ts_interfaces/requests.tickets.d.ts +8 -12
  84. package/dist_ts_interfaces/tickets.d.ts +37 -9
  85. package/dist_ts_interfaces/tickets.js +7 -1
  86. package/dist_ts_interfaces/workercallers.d.ts +43 -4
  87. package/package.json +7 -7
  88. package/readme.md +156 -23
  89. package/ts/00_commitinfo_data.ts +1 -1
  90. package/ts/aglapi/aglapisurface.document.ts +63 -36
  91. package/ts/classes.aglhome.ts +6 -0
  92. package/ts/classes.authstore.ts +4 -0
  93. package/ts/classes.controller.ts +141 -32
  94. package/ts/classes.controllereventdelivery.ts +289 -0
  95. package/ts/classes.flexmodels.public.ts +10 -0
  96. package/ts/classes.flexprojectionreader.ts +30 -3
  97. package/ts/classes.flexservice.ts +33 -5
  98. package/ts/classes.flexstore.ts +381 -246
  99. package/ts/classes.inferencegateway.ts +104 -23
  100. package/ts/classes.inferenceservice.ts +6 -3
  101. package/ts/classes.proxycontrol.ts +76 -39
  102. package/ts/classes.proxycontrolclient.ts +72 -20
  103. package/ts/classes.proxydaemon.ts +85 -15
  104. package/ts/classes.proxyleaseholder.ts +84 -28
  105. package/ts/classes.proxystore.ts +16 -5
  106. package/ts/classes.ticketnotifier.ts +3 -1
  107. package/ts/classes.ticketstore.ts +201 -75
  108. package/ts/classes.workercallerbindings.ts +147 -0
  109. package/ts/classes.workercallers.ts +410 -59
  110. package/ts/driver/classes.drivereventrelay.ts +89 -4
  111. package/ts/driver/functions.driverinstructions.ts +1 -1
  112. package/ts/driver/functions.drivertools.ts +22 -8
  113. package/ts/fleet/classes.boardpump.ts +51 -19
  114. package/ts/fleet/classes.ticketeffects.ts +25 -11
  115. package/ts/fleet/classes.workerlaunches.ts +23 -15
  116. package/ts/fleet/classes.workers.ts +79 -17
  117. package/ts/fleet/functions.workervalidation.ts +1 -1
  118. package/ts/functions.flexprivatesnapshot.ts +601 -0
  119. package/ts/functions.inferencecli.ts +61 -7
  120. package/ts/functions.inferencecliarguments.ts +32 -1
  121. package/ts/functions.inferencelaunch.ts +49 -2
  122. package/ts/functions.mcptickettools.ts +1 -1
  123. package/ts/functions.proxycontract.ts +12 -0
  124. package/ts/functions.proxysocket.ts +39 -0
  125. package/ts/functions.ticketcli.ts +7 -3
  126. package/ts/functions.ticketvalidation.ts +7 -3
  127. package/ts/functions.workerprocessproof.ts +27 -4
  128. package/ts/interfaces.flexipc.ts +50 -21
  129. package/ts_interfaces/fleetworkers.ts +6 -0
  130. package/ts_interfaces/interfaces.ts +1 -1
  131. package/ts_interfaces/requests.tickets.ts +9 -6
  132. package/ts_interfaces/tickets.ts +32 -1
  133. package/ts_interfaces/workercallers.ts +32 -4
  134. package/ts_web/00_commitinfo_data.ts +1 -1
  135. package/ts_web/elements.harnesscontrollerapp.ts +19 -8
  136. package/ts_web/elements.ticketboard.ts +16 -4
  137. package/ts_web/settings.general.ts +10 -4
@@ -304,6 +304,12 @@ export interface IFleetEvent {
304
304
  /** The native session (`harness:nativeId`) a ticket notice addressed. */
305
305
  sessionId?: string;
306
306
  reportId?: string;
307
+ /**
308
+ * The comment on `ticketKey` that holds this event's whole text, by the id the ticket board returned: a worker's report
309
+ * the fleet commented (`worker.report.mid_turn`, `worker.turn.ended`), the board pump's stall notice, a commented
310
+ * `ticket.changed`. Read it with `ticket_get` and `commentId`. Absent when no comment holds the text.
311
+ */
312
+ commentId?: string;
307
313
  turnId?: string;
308
314
  status?: string;
309
315
  text?: string;
@@ -1,6 +1,6 @@
1
1
  import type { ILiveBrowserDialog, ILiveBrowserDialogResponse, ILiveVideoOffer, ILiveVideoDescription, ILiveVideoStatistics, ILiveVideoAcceleration } from '@push.rocks/smartbrowser/web';
2
2
  import type { IControllerBrowserPaintSource, IControllerBrowserPresentationState, TControllerBrowserPaintAvailability, TControllerBrowserPaintTermination } from './browserpaint.js';
3
- export declare const controllerProtocolVersion: 45;
3
+ export declare const controllerProtocolVersion: 46;
4
4
  export declare const controllerUpgradeManagementVersion: 2;
5
5
  export declare const controllerPackageName: 'agl';
6
6
  /** The systemd user unit `agl service install` writes; a controller it runs reports it in its status. */
@@ -1,4 +1,4 @@
1
- export const controllerProtocolVersion = 45;
1
+ export const controllerProtocolVersion = 46;
2
2
  export const controllerUpgradeManagementVersion = 2;
3
3
  export const controllerPackageName = 'agl';
4
4
  /** The systemd user unit `agl service install` writes; a controller it runs reports it in its status. */
@@ -1,5 +1,5 @@
1
1
  import type * as plugins from './plugins.js';
2
- import type { ITicket, ITicketActivity, ITicketAnswer, ITicketAsk, ITicketBoard, ITicketChanges, ITicketComment, ITicketCreate, ITicketFilters, ITicketMove, ITicketQuestion, TTicketLink, TTicketOperation, TTicketReferenceContent, TTicketRequest, TTicketResponse } from './tickets.js';
2
+ import type { ITicket, ITicketAnswer, ITicketAsk, ITicketBoard, ITicketChanges, ITicketComment, ITicketCreate, ITicketDetail, ITicketFilters, ITicketGet, ITicketMove, ITicketQuestion, TTicketLink, TTicketOperation, TTicketReferenceContent, TTicketRequest, TTicketResponse } from './tickets.js';
3
3
  export declare const controllerMcpTicketMethods: readonly ['agl.mcp.ticket.list', 'agl.mcp.ticket.board', 'agl.mcp.ticket.search', 'agl.mcp.ticket.get', 'agl.mcp.ticket.comments', 'agl.mcp.ticket.create', 'agl.mcp.ticket.update', 'agl.mcp.ticket.move', 'agl.mcp.ticket.comment', 'agl.mcp.ticket.link', 'agl.mcp.ticket.ask', 'agl.mcp.ticket.answer'];
4
4
  export type TControllerMcpTicketMethod = (typeof controllerMcpTicketMethods)[number];
5
5
  export type TControllerTicketRequest<TMethod extends TTicketOperation> = {
@@ -54,22 +54,18 @@ export interface IReq_ControllerMcpTicketSearch extends plugins.typedrequestInte
54
54
  };
55
55
  }
56
56
  /**
57
- * Reads a ticket by its `AGL-<n>` key, with its Markdown comments, source, links, questions and
58
- * their answers, and its complete activity log.
57
+ * Reads a ticket by its `AGL-<n>` key with its source, links, all its questions and their answers,
58
+ * and the newest page of its history (up to `limit` entries, default 50, at most 500, within 256 KiB), oldest first: the
59
+ * activity entries and the Markdown comments they name, a `commented` entry naming its comment by
60
+ * id. `historyBefore` is present while older history exists; pass it as `before` for the next page.
61
+ * `commentId` reads that one comment instead (`comments: [it]`, `activity: []`), without `before` or `limit`.
59
62
  */
60
63
  export interface IReq_ControllerMcpTicketGet extends plugins.typedrequestInterfaces.implementsTR<TControllerMcpTicketRequest<'get'>, IReq_ControllerMcpTicketGet> {
61
64
  method: 'agl.mcp.ticket.get';
62
- request: {
63
- ticketId: string;
64
- } & {
65
+ request: ITicketGet & {
65
66
  ownerToken?: string;
66
67
  };
67
- response: {
68
- ticket: ITicket;
69
- comments: ITicketComment[];
70
- activity: ITicketActivity[];
71
- questions: ITicketQuestion[];
72
- };
68
+ response: ITicketDetail;
73
69
  }
74
70
  /** Reads a ticket's comments. */
75
71
  export interface IReq_ControllerMcpTicketComments extends plugins.typedrequestInterfaces.implementsTR<TControllerMcpTicketRequest<'comments'>, IReq_ControllerMcpTicketComments> {
@@ -218,6 +218,40 @@ export interface ITicketMove {
218
218
  columnId: TTicketColumn;
219
219
  beforeTicketId?: string;
220
220
  }
221
+ /**
222
+ * How many history entries one `get` returns unless it names `limit`, the most it may name, and the bytes a page holds
223
+ * at most (its entries plus the comments they name, serialized; a page holds at least one entry). The budget wins over `limit`.
224
+ */
225
+ export declare const ticketHistoryPage: {
226
+ readonly default: 50;
227
+ readonly maximum: 500;
228
+ readonly bytes: number;
229
+ };
230
+ /** A ticket and one page of its history: the newest entries, or those older than the entry `before` names, within `ticketHistoryPage`. */
231
+ export interface ITicketGet {
232
+ ticketId: string;
233
+ /** An activity id, as `historyBefore` returned it: the page holds only older entries. */
234
+ before?: string;
235
+ /** Entries on the page, 1 to 500 (`ticketHistoryPage.maximum`); 50 (`ticketHistoryPage.default`) when absent. */
236
+ limit?: number;
237
+ /**
238
+ * One comment of this ticket, by the id an activity entry or a fleet event names: the response holds the ticket, its
239
+ * questions and that comment alone (`comments: [it]`, `activity: []`, no `historyBefore`). Excludes `before` and `limit`.
240
+ */
241
+ commentId?: string;
242
+ }
243
+ /**
244
+ * A ticket, all its questions and one page of its history, oldest first. `activity` is the page; `comments` are the
245
+ * comments its `commented` entries name, whose `changes.comment` holds only `{ after: { id } }` here (the stored
246
+ * entry keeps the whole comment). `historyBefore` is present while older entries exist: pass it as `before`.
247
+ */
248
+ export interface ITicketDetail {
249
+ ticket: ITicket;
250
+ comments: ITicketComment[];
251
+ activity: ITicketActivity[];
252
+ questions: ITicketQuestion[];
253
+ historyBefore?: string;
254
+ }
221
255
  export interface ITicketBoard {
222
256
  revision: number;
223
257
  columns: {
@@ -250,15 +284,8 @@ export interface ITicketOperations {
250
284
  };
251
285
  };
252
286
  get: {
253
- request: {
254
- ticketId: string;
255
- };
256
- response: {
257
- ticket: ITicket;
258
- comments: ITicketComment[];
259
- activity: ITicketActivity[];
260
- questions: ITicketQuestion[];
261
- };
287
+ request: ITicketGet;
288
+ response: ITicketDetail;
262
289
  };
263
290
  comments: {
264
291
  request: {
@@ -369,6 +396,7 @@ export declare const ticketErrorMessages: {
369
396
  readonly ticket_ask_forbidden: 'Workers may ask questions only on tickets assigned to their verified session.';
370
397
  readonly ticket_answer_forbidden: 'Only the owner or the driver may answer ticket questions.';
371
398
  readonly ticket_question_not_found: 'The requested question does not exist on this ticket.';
399
+ readonly ticket_comment_not_found: 'The requested comment does not exist on this ticket.';
372
400
  readonly ticket_question_answered: 'The question already has a different answer; a second answer is refused. Ask a new question instead.';
373
401
  readonly ticket_answer_invalid: 'An answer chooses existing option ids (one unless multiSelect) and/or gives free text where the question allows it.';
374
402
  };
@@ -43,6 +43,11 @@ export const ticketAnswerFromCard = (questionArg, answersArg) => {
43
43
  return { choices: questionArg.options.filter((optionArg) => choices.includes(optionArg.id)).map((optionArg) => optionArg.id),
44
44
  ...(texts.length ? { text: texts.join('\n\n') } : {}) };
45
45
  };
46
+ /**
47
+ * How many history entries one `get` returns unless it names `limit`, the most it may name, and the bytes a page holds
48
+ * at most (its entries plus the comments they name, serialized; a page holds at least one entry). The budget wins over `limit`.
49
+ */
50
+ export const ticketHistoryPage = { default: 50, maximum: 500, bytes: 256 * 1024 };
46
51
  export const ticketErrorMessages = {
47
52
  ticket_revision_conflict: 'The ticket changed since editing began. Reload it before editing again.',
48
53
  ticket_identity_required: 'A verified MCP caller is required for this ticket operation. Unidentified callers may only list or get tickets.',
@@ -59,9 +64,10 @@ export const ticketErrorMessages = {
59
64
  ticket_ask_forbidden: 'Workers may ask questions only on tickets assigned to their verified session.',
60
65
  ticket_answer_forbidden: 'Only the owner or the driver may answer ticket questions.',
61
66
  ticket_question_not_found: 'The requested question does not exist on this ticket.',
67
+ ticket_comment_not_found: 'The requested comment does not exist on this ticket.',
62
68
  ticket_question_answered: 'The question already has a different answer; a second answer is refused. Ask a new question instead.',
63
69
  ticket_answer_invalid: 'An answer chooses existing option ids (one unless multiSelect) and/or gives free text where the question allows it.',
64
70
  };
65
71
  export const ticketClientErrorMessages = Object.fromEntries(Object.entries(ticketErrorMessages)
66
72
  .map(([codeArg, messageArg]) => [codeArg.toUpperCase(), messageArg]));
67
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGlja2V0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX2ludGVyZmFjZXMvdGlja2V0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUc7SUFDM0IsU0FBUyxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsVUFBVTtDQUN4RSxDQUFDO0FBaURYLHNGQUFzRjtBQUN0RixNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLGNBQWMsQ0FBVSxDQUFDO0FBRTFELE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLENBQUMsUUFBZ0IsRUFBZ0MsRUFBRSxDQUFDLENBQ2xGLGdCQUFzQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FDM0QsQ0FBQztBQUtGLE1BQU0sQ0FBQyxNQUFNLDBCQUEwQixHQUFHLENBQUMsUUFBaUIsRUFBdUMsRUFBRTtJQUNuRyxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxRQUFRLEtBQUssSUFBSTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ3hFLE1BQU0sS0FBSyxHQUFHLFFBQTRDLENBQUM7SUFDM0QsT0FBTyxLQUFLLENBQUMsSUFBSSxLQUFLLDBCQUEwQixJQUFJLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxJQUFJLEtBQUssQ0FBQyxlQUFnQixJQUFJLENBQUM7UUFDNUgsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLDBCQUEwQixFQUFFLGVBQWUsRUFBRSxLQUFLLENBQUMsZUFBZ0IsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDaEcsQ0FBQyxDQUFDO0FBMkVGLDhGQUE4RjtBQUM5RixNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxDQUFDLFdBQTRCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUUsRUFBRSxFQUFFLFdBQVcsQ0FBQyxFQUFFO0lB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
73
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGlja2V0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX2ludGVyZmFjZXMvdGlja2V0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUc7SUFDM0IsU0FBUyxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsVUFBVTtDQUN4RSxDQUFDO0FBaURYLHNGQUFzRjtBQUN0RixNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLGNBQWMsQ0FBVSxDQUFDO0FBRTFELE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLENBQUMsUUFBZ0IsRUFBZ0MsRUFBRSxDQUFDLENBQ2xGLGdCQUFzQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FDM0QsQ0FBQztBQUtGLE1BQU0sQ0FBQyxNQUFNLDBCQUEwQixHQUFHLENBQUMsUUFBaUIsRUFBdUMsRUFBRTtJQUNuRyxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxRQUFRLEtBQUssSUFBSTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ3hFLE1BQU0sS0FBSyxHQUFHLFFBQTRDLENBQUM7SUFDM0QsT0FBTyxLQUFLLENBQUMsSUFBSSxLQUFLLDBCQUEwQixJQUFJLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxJQUFJLEtBQUssQ0FBQyxlQUFnQixJQUFJLENBQUM7UUFDNUgsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLDBCQUEwQixFQUFFLGVBQWUsRUFBRSxLQUFLLENBQUMsZUFBZ0IsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDaEcsQ0FBQyxDQUFDO0FBMkVGLDhGQUE4RjtBQUM5RixNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxDQUFDLFdBQTRCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUUsRUFBRSxFQUFFLFdBQVcsQ0FBQyxFQUFFO0lB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
@@ -4,6 +4,8 @@ export declare const workerCallerLaunchGenerationEnvironmentVariable = "AGL_MCP_
4
4
  export interface IFleetWorkerCallerLaunchBase {
5
5
  launchId: string;
6
6
  projectId: string;
7
+ /** The fleet worker the launch identifies, `${projectId}:${name}`: a worker holds one current launch. */
8
+ workerId: string;
7
9
  }
8
10
  /**
9
11
  * The Codex app-server connection that created the worker's thread: a controller connection profile, or the fleet's
@@ -28,12 +30,18 @@ export interface IFleetClaudeProcess {
28
30
  pid: number;
29
31
  startTicks: string;
30
32
  }
31
- export type TFleetWorkerCallerBinding = IFleetWorkerCallerLaunchBase & {
33
+ export type TFleetWorkerCallerBinding = Omit<IFleetWorkerCallerLaunchBase, 'workerId'> & {
32
34
  nativeSessionId: string;
33
35
  expectedBindingRevision: number;
34
- } & ({
36
+ } & (
37
+ /**
38
+ * A Claude launch binds to its background job and to the job's current process; a job whose process already exited
39
+ * binds without one, and the next process of the same job proves itself when its bridge enrolls.
40
+ */
41
+ {
35
42
  harnessId: 'claude';
36
- claudeProcess: IFleetClaudeProcess;
43
+ claudeJobId: string;
44
+ claudeProcess?: IFleetClaudeProcess;
37
45
  } | {
38
46
  harnessId: 'codex';
39
47
  });
@@ -58,11 +66,42 @@ export interface IFleetWorkerCallerHooks {
58
66
  ticketSessionId: string;
59
67
  }>;
60
68
  getWorkerCallerStatus(launchId: string): IWorkerCallerReadiness;
61
- revokeWorkerCaller(request: {
69
+ /**
70
+ * Revokes one launch, or the current launch of a worker, also one the controller kept from before a restart. Every
71
+ * reason but `generation_change` revokes it for good: its kept binding is deleted with it.
72
+ */
73
+ revokeWorkerCaller(request: ({
62
74
  launchId: string;
75
+ } | {
76
+ workerId: string;
77
+ }) & {
63
78
  reason: 'failure' | 'stop' | 'replacement' | 'generation_change' | 'cancelled';
64
79
  }): Promise<void>;
65
80
  }
81
+ /** The background job a Claude worker's launch is bound to, as the controller asks the fleet about it. */
82
+ export interface IWorkerClaudeJobRequest {
83
+ workerId: string;
84
+ projectId: string;
85
+ nativeSessionId: string;
86
+ jobId: string;
87
+ }
88
+ /**
89
+ * What a fresh Claude inventory shows of that job: `running` in one process, `settled` without a process, `ended` for
90
+ * this worker (the worker is gone or archived, continues in another session, or the session continues as another job),
91
+ * or `unproven` when the registry or the inventory could not be read or did not list the session once.
92
+ */
93
+ export type TWorkerClaudeJobObservation = {
94
+ state: 'running';
95
+ process: IFleetClaudeProcess;
96
+ } | {
97
+ state: 'settled';
98
+ } | {
99
+ state: 'ended';
100
+ reason: string;
101
+ } | {
102
+ state: 'unproven';
103
+ reason: string;
104
+ };
66
105
  export interface IWorkerCallerSubject {
67
106
  kind: 'worker_launch';
68
107
  launchId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agl",
3
- "version": "40.5.1",
3
+ "version": "41.0.0",
4
4
  "controllerUpgradeManagementVersion": 2,
5
5
  "private": false,
6
6
  "description": "Agent Gateway Layer for OpenCode, FlexHarness, and Codex sessions",
@@ -24,7 +24,7 @@
24
24
  "@api.global/typedsocket": "8.10.0",
25
25
  "@apiclient.xyz/docker": "10.1.3",
26
26
  "@aws-sdk/client-s3": "^3.1136.0",
27
- "@design.estate/dees-catalog": "^23.1.0",
27
+ "@design.estate/dees-catalog": "^23.4.0",
28
28
  "@design.estate/dees-comms": "^2.1.0",
29
29
  "@design.estate/dees-element": "4.0.2",
30
30
  "@jitl/quickjs-ng-wasmfile-release-sync": "0.32.0",
@@ -32,11 +32,11 @@
32
32
  "@lossless.org/nosqldb": "10.8.0",
33
33
  "@lossless.org/objectstorage": "10.7.1",
34
34
  "@modelcontextprotocol/sdk": "1.30.0",
35
- "@modelprofile.com/catalog": "^1.0.0",
36
- "@modelprofile.com/flexharness": "16.3.0",
37
- "@modelprofile.com/flexharness-agent": "16.3.0",
38
- "@modelprofile.com/flexharness-models": "16.3.0",
39
- "@modelprofile.com/flexharness-providers": "16.3.0",
35
+ "@modelprofile.com/catalog": "^1.1.0",
36
+ "@modelprofile.com/flexharness": "16.4.0",
37
+ "@modelprofile.com/flexharness-agent": "16.4.0",
38
+ "@modelprofile.com/flexharness-models": "16.4.0",
39
+ "@modelprofile.com/flexharness-providers": "16.4.0",
40
40
  "@modelprofile.com/mcp-crossharness": "5.9.0",
41
41
  "@modelprofile.com/mcp-system": "3.1.0",
42
42
  "@opencode-ai/sdk": "1.18.15",
package/readme.md CHANGED
@@ -76,6 +76,8 @@ The browser receives normalized AGL data. It never receives the OpenCode server
76
76
 
77
77
  Controller protocol v28 sends bounded live Assistant text and reasoning deltas when the browser already owns the exact preceding transcript revision. Every delta carries its stream epoch, revision, and UTF-8 coordinates; Flex deltas also carry the harness's authoritative transcript order. The browser applies only contiguous, correctly sized updates to the canonical transcript; a gap, stale owner, invalid coordinate, or queue overflow blocks further deltas from that epoch and requests authoritative session-detail hydration instead. Only adjacent compatible pending deltas may be coalesced, so interleaved updates retain their original order.
78
78
 
79
+ Each browser receives controller events through its own ordered queue, one event in flight at a time, so a tab that acknowledges slowly or not at all delays only itself. A queue that passes 512 events or 16 MiB collapses for that browser alone: it keeps the newest event of each state, drops live text, reasoning and tool content, and ends with a conversation refresh for each harness whose live content it dropped. A streamed Flex reply notifies a conversation change at its parts' starts and ends and its run's lifecycle, not with every delta.
80
+
79
81
  Streaming text is rendered without Markdown parsing. AGL refreshes the affected messages and enables normal Markdown rendering after the harness reports terminal state or authoritative hydration settles the transcript. This keeps long responses responsive without creating a second browser-side message graph.
80
82
 
81
83
  Consecutive Assistant messages containing only reasoning share one header and time range. Each thought keeps its own disclosure and active thinking state; user messages, text, tools and attention requests end the group.
@@ -328,6 +330,8 @@ agl proxy run codex --model <catalog-model> -- exec 'Explain this repository'
328
330
  agl proxy run claude -- # Claude Code on Claude accounts
329
331
  agl proxy run claude --provider openai --model <catalog-model> --
330
332
  agl proxy run opencode --model <catalog-model> -- run 'Explain this repository'
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 end <run id>
331
335
  ```
332
336
 
333
337
  Every client -- `codex`, `claude`, `opencode`, or a name of your own -- has one credential. The proxy keeps it
@@ -434,7 +438,8 @@ pool and keeps its credential. `agl proxy run claude` uses Claude accounts unles
434
438
  ChatGPT models behind Claude Code; `run codex` and `run opencode` use ChatGPT accounts.
435
439
 
436
440
  The listener binds only `127.0.0.1` and requires the exact host and port, no browser Origin, and
437
- `Authorization: Bearer <credential>` (or `x-api-key`).
441
+ `Authorization: Bearer <credential>` (or `x-api-key`). Claude Code in a container reaches the proxy through a second
442
+ listener, a Unix socket that serves confined runs alone (see [Confined containers](#confined-containers)).
438
443
 
439
444
  - A ChatGPT pool serves `/openai/v1/responses` and `/openai/v1/models` (the pooled Codex catalog in Codex's
440
445
  own shape when Codex names its `client_version`), and `/anthropic/v1/messages` and `/anthropic/v1/models`
@@ -701,6 +706,65 @@ proxy's binding with it, and the sessions it served move at their next request.
701
706
  pool, 16 requests in flight per session, 256 per client credential, 512 in all, 32 MiB per request and 8 MiB
702
707
  per inspected response event (or non-streaming JSON response).
703
708
 
709
+ #### Confined containers
710
+
711
+ `agl proxy confined run` runs Claude Code in a Docker container that reaches AGL through one route only: the proxy's
712
+ 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 --
714
+ `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.
719
+
720
+ ```sh
721
+ agl proxy confined run -- docker run --rm --user "$(id -u):$(id -g)" -i <image> \
722
+ 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
724
+ agl proxy confined end <run id> # ends a run's credential at once
725
+ ```
726
+
727
+ 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:
732
+
733
+ - `ANTHROPIC_UNIX_SOCKET=/run/agl-proxy/proxy.sock`: Claude Code sends its API requests through the socket, as plain
734
+ HTTP, so no relay runs in the container (Claude Code's own setting, which `claude ssh` uses; verified with Claude Code
735
+ 2.1.288, the release of AGL's Agent SDK);
736
+ - `ANTHROPIC_BASE_URL=http://localhost/anthropic`: the path Claude Code puts on its requests; the host is not used;
737
+ - `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.
748
+
749
+ The socket and its directory are the proxy user's alone (0600 and 0700), so the container's processes connect as that
750
+ user: `--user` with its uid and gid under a Docker daemon that runs as root, or the container's own root under rootless
751
+ Docker, which maps it to that user; another uid, or a container whose user namespace maps it elsewhere, cannot connect.
752
+ Root in a container of a rootful daemon connects as well while it keeps Docker's default capabilities, since
753
+ `CAP_DAC_OVERRIDE` passes the socket's mode (with `--cap-drop ALL` it is refused): the mode keeps the host's other users
754
+ out, and only the containers a run started have the mount at all. The directory, not the socket file, is mounted, so a
755
+ proxy restart, which replaces the socket, does not cut the container off.
756
+
757
+ The credential lives as long as the command. When the container exits the command ends it, and the proxy refuses it
758
+ from then on and forgets it. Like a lease it survives a restart of the proxy, which keeps only its SHA-256 digest and
759
+ serves it again at once while the command takes it back. `agl proxy confined end <run id>` ends it at once: requests in
760
+ flight end, the next one is refused, and the command stops the container (`docker run` passes its SIGTERM on) and exits
761
+ with the reason; it does the same when the proxy does not hand the credential back after a restart.
762
+
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.
767
+
704
768
  ## Native Codex Harness
705
769
 
706
770
  Choose **New → Chat session** and pick the Codex harness to use Codex inside AGL. Native threads support text and reasoning streaming, tool activity, the Codex model and reasoning-effort catalog, command and file approvals, ordinary user-input questions, interruption, history pagination, rename, archive, and deletion. AGL Ask/Yolo controls apply to the exact pending native approval request; approvals never outlive its server request.
@@ -1212,7 +1276,12 @@ native completion; archive requires a settled worker. A worker whose start was r
1212
1276
  native session, and archiving it touches no harness. Claude archive marks only the AGL
1213
1277
  registry, preserving the transcript and native worktree. Codex archives its native thread.
1214
1278
  Watch retains native `reportId` alongside `workerId` so consumers can correlate reports
1215
- and updates without treating each watch cursor as a new report. A failed observation, a
1279
+ and updates without treating each watch cursor as a new report. An event whose whole text is a
1280
+ ticket comment names it as `commentId`: the fleet comments a ticketed worker's report before it
1281
+ publishes the report's event (`worker.report.mid_turn`, `worker.turn.ended`), the board pump's
1282
+ `ticket.stalled` names its notice, and a commented `ticket.changed` its comment. Read it with
1283
+ `ticket_get` and `commentId` (`agl ticket get AGL-<n> --comment <id>`), however long the
1284
+ ticket's history is. A report whose comment was not written names none. A failed observation, a
1216
1285
  transcript gap for example, is published once as `worker.observation.failed`; the fleet then
1217
1286
  requests a fresh report itself: the worker's next poll starts from a new baseline, and its
1218
1287
  newest report, when it has one, is published as `worker.report.mid_turn` and commented on its
@@ -1532,8 +1601,13 @@ While the board changed within the last hour, the driver keeps its prompt cache
1532
1601
  next turn reads the cached prefix instead of writing it again. Every board change (a ticket
1533
1602
  created, changed, moved, commented, answered or assigned) sets the driver session's warm
1534
1603
  deadline to one hour from then, and Flex runs a warm-up turn before the provider's cache would
1535
- expire. An hour after the last board change, warming stops until the next one. Warm-up turns
1536
- add nothing to the driver's conversation: no message in its transcript, no `working` status,
1604
+ expire. On a Claude model the driver's requests ask Anthropic for one-hour cache breakpoints
1605
+ (`cache: { retention: '1h' }`), so an idle hour costs one warm-up turn, a minute before the hour
1606
+ ends, instead of one every four minutes; writing an hour's breakpoint costs 2× the input price
1607
+ instead of 1.25×. Other sessions keep the five-minute default for their newest breakpoint. On a
1608
+ ChatGPT account the cache lives in memory for about five minutes and takes no retention, so
1609
+ warm-up turns still come every four minutes. An hour after the last board change, warming stops until the next one. Warm-up turns
1610
+ add nothing to the driver's conversation: nothing its model sees on later turns, no message in its transcript, no `working` status,
1537
1611
  no unread turn and no batch of fleet events. They leave the session's state as its last real
1538
1612
  turn left it, so a driver whose last turn failed still shows that error. The controller logs each
1539
1613
  warm-up turn with the tokens it read from the cache. `keepCacheWarm: false` turns warming off
@@ -1553,8 +1627,14 @@ and `driver_notes_update`, the shell and file tools
1553
1627
  every Flex session has, and the [AGL API](#agl-api-for-flex-sessions) without its ticket methods. The controller runs each of these tool calls as the driver: actor
1554
1628
  `driver:<controller id>` with the ticket role `driver`. A tool that changes fleet or ticket
1555
1629
  state first asks for the `agl.driver` permission, which the session can remember. The notes
1556
- are the driver session's scratchpad. They are added to its system prompt on every run, so the
1557
- driver's working state survives compaction and restarts.
1630
+ are the driver session's scratchpad, so the driver's working state survives compaction and
1631
+ restarts. They are not part of the driver's system prompt, which holds its instructions only:
1632
+ they reach the model as Flex's pinned context `driver-notes`, named by the scratchpad's
1633
+ revision. A turn whose context does not show the current revision gets it right before its
1634
+ prompt, and a compaction ends its summary with it, so the driver sees its notes when they change
1635
+ and after a compaction, and a notes update leaves the cached prompt in front of it unchanged.
1636
+ Empty notes are pinned as a note that they are empty, so clearing them replaces the text saved
1637
+ before.
1558
1638
 
1559
1639
  `ticket_question_answer` records the owner's answer to an open ticket question as the driver,
1560
1640
  with the schema of `agl ticket answer`; the MCP server has no such tool, so a worker cannot
@@ -1583,7 +1663,15 @@ finished or failed releases, failed or unknown ticket effects, others' ticket ch
1583
1663
  (`ticket.notification.failed`, naming the session), stalled tickets the board pump found
1584
1664
  (`ticket.stalled`) and Claude Code releases newer than the verified one
1585
1665
  (`harness.release.discovered`). Worker status changes, host-load samples,
1586
- applied ticket effects and delivered ticket notices are not delivered. A batch waits 15
1666
+ applied ticket effects and delivered ticket notices are not delivered. Each event reads as one
1667
+ header line -- `- [event <id>] <time> <kind>` with its ticket, worker, session, report id, turn id,
1668
+ status, repo, version and operation -- then its error and its text, quoted. A text longer than
1669
+ 3 KiB is relayed as a digest: every report marker line (`result:`, `needs input:`, `owner
1670
+ question:` and the others the fleet reads as a report), each at most 1 KiB, then the text's end,
1671
+ in order, with `[… N lines …]` where lines were left out. A note then says where the whole text
1672
+ is: a ticketed worker's report is the fleet comment of that report id on its ticket, and a
1673
+ stalled ticket's notice or another's change is on its ticket, all read with `ticket_get`. A
1674
+ report longer than 32 KiB is kept by the fleet from its end only, which the note says. A batch waits 15
1587
1675
  seconds for further events and is delivered at most once a minute. It waits while the driver runs a turn, including permission waits, using the current session state.
1588
1676
  If a batch is still queued when further events arrive, they merge into that same message,
1589
1677
  keeping its position and sender. The merged text is stored before the merge is acknowledged,
@@ -1636,6 +1724,10 @@ as `ticket.notified` when the answers reached the worker, and reaches the driver
1636
1724
  relay. The pump never starts a worker, never moves a ticket and never answers a question, and its
1637
1725
  comments are not relayed to the assignee as ticket notices.
1638
1726
 
1727
+ Each pass reads the board list once. A ticket's history and questions are read again only when its
1728
+ revision moved since the pass that last read them; every comment, question, answer and other change
1729
+ moves it, so a pass over an unchanged board reads no ticket's history.
1730
+
1639
1731
  The pump is on by default; configure it in the Configuration master:
1640
1732
 
1641
1733
  ```json
@@ -1668,8 +1760,8 @@ has full access; the CLI reuses `agl config`'s short-lived local-owner capabilit
1668
1760
  is no automatic dispatch. Attachment URLs must use HTTP or HTTPS; AGL stores the
1669
1761
  reference and does not fetch its payload.
1670
1762
 
1671
- Unidentified local MCP callers may use `tickets_list` and `ticket_get`, including comments
1672
- and activity returned with the ticket. Their Mine filter is empty and every returned ticket
1763
+ Unidentified local MCP callers may use `tickets_list` and `ticket_get`, including the comments
1764
+ and activity of the history page returned with the ticket. Their Mine filter is empty and every returned ticket
1673
1765
  permission is false. Every mutation refuses with `TICKET_IDENTITY_REQUIRED`; neither a
1674
1766
  claimed name nor a native session id supplies identity. Plain Claude background sessions
1675
1767
  started outside AGL stay unidentified. The web board still requires owner authentication.
@@ -1737,11 +1829,12 @@ superseded origins refuse preparation.
1737
1829
  - Revoke on failure, stop, replacement, cancellation or controller-generation change.
1738
1830
  Revocation fences new admissions immediately, removes credentials, and waits for already
1739
1831
  admitted ticket requests. Native-ID uniqueness and binding revision checks fence races.
1740
- Controller restart invalidates every old handle; existing native workers remain
1741
- unidentified until fleet performs an authorized controlled resume/relaunch with a fresh
1742
- overlay. Hooks do not automatically terminate or adopt outside sessions. Revoked launch IDs
1743
- stay recorded so they are never prepared again; the oldest give way once 65,536 launches
1744
- are recorded.
1832
+ Controller restart invalidates every credential. A Claude launch's binding to its job
1833
+ outlives it (below); a Codex launch does not, and its worker is bound again at its next
1834
+ steer. Every reason but a controller-generation change revokes a launch for good and
1835
+ deletes its kept binding. Hooks do not automatically terminate or adopt outside sessions.
1836
+ Revoked launch IDs stay recorded so they are never prepared again; the oldest give way
1837
+ once 65,536 launches are recorded.
1745
1838
 
1746
1839
  Fleet workers use these hooks themselves, so a worker's MCP ticket writes are admitted as
1747
1840
  that worker, not as the driver or anonymously:
@@ -1753,16 +1846,40 @@ that worker, not as the driver or anonymously:
1753
1846
  the worker id), passed as `--mcp-config <file> -- <prompt>`. Claude Code saves that path and reads it
1754
1847
  again at every wake, so the fleet rewrites it before each one; the launch ID and
1755
1848
  generation reach only the `agl` MCP server's environment, never the Claude process's.
1756
- Once the agents listing shows the job's process, the fleet binds the launch to it. A start,
1757
- wake or relaunch that fails at any step revokes its launch. Stop revokes the launch and archive removes the overlay. A start fails closed before Claude
1758
- runs when the Configuration master has no `agl` MCP server.
1849
+ Once the agents listing shows the job's process, the fleet binds the launch to the job and
1850
+ that process; a job that already settled after a quick turn is bound without a process. A
1851
+ start, wake or relaunch that fails at any step revokes its launch. Stop revokes the launch
1852
+ and archive also removes the overlay. A start fails closed before Claude runs when the
1853
+ Configuration master has no `agl` MCP server.
1759
1854
  - Fleet Codex workers prepare the launch on the fleet's own connection before
1760
1855
  `thread/start` and bind it before the first `turn/start`. Before steering a thread whose
1761
1856
  launch is unbound, from an earlier connection or from before a controller restart, the
1762
1857
  fleet loads the thread and binds a fresh launch; a refusal fails the steer undispatched.
1763
- - Launches live in memory. After a controller restart a Claude worker stays unidentified
1764
- until its next fleet start, steer or resume, and a Codex worker is bound again at its next
1765
- steer.
1858
+ - A Claude launch belongs to its background job, not to one process. Claude Code retires an
1859
+ idle job's process and starts a new one for the same job when someone writes to it in
1860
+ Claude Code; that process reads the same overlay. The controller keeps each Claude
1861
+ launch's binding in its store (`agl_worker_caller_bindings`, one record per worker:
1862
+ digests of the launch ID and generation, the job and session IDs, the binding revision, the
1863
+ proved process's PID, start and snapshot digest, and the approved bridge command; never a
1864
+ credential). When a bridge enrolls under another process than the bound one, or after a
1865
+ controller restart, the fleet reads a fresh Claude inventory (one read at a time) and the
1866
+ launch is bound to the bridge's process only when the inventory lists the worker's session,
1867
+ once, as the same background job in exactly that process, the process runs the approved
1868
+ Claude Code executable, and the bridge is its direct child running the recorded command
1869
+ over its own connection. A launch a restarted controller restores is proved the same way
1870
+ before its first write, also when its process still runs. Every other process stays
1871
+ unverified, and a refused bridge is not proved again at every call: for the life of that
1872
+ process image, or for five seconds when the job showed no process yet, the inventory could
1873
+ not be read, or the inventory still named another process for the job (Claude Code may just
1874
+ have replaced it).
1875
+ When the inventory shows the worker's session as another job, or the fleet no longer binds
1876
+ the worker to it (archived, moved to another session), the launch is revoked for good.
1877
+ 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.
1766
1883
 
1767
1884
  The bridge exchanges credentials only through the private controller endpoint after Linux
1768
1885
  direct-loopback socket-inode ownership, approved executable/argv and current native
@@ -1778,7 +1895,7 @@ entry. Ambiguous entries, wrapper commands, static caller credentials and secret
1778
1895
  definitions fail closed. Fleet owns spawning, native observations and hook calls.
1779
1896
 
1780
1897
  MCP tools are `tickets_list` (mine/project/status/label/text filters), `ticket_get` (ticket,
1781
- comments, complete activity and questions), `ticket_create`, `ticket_update`, `ticket_move`,
1898
+ questions and one page of history), `ticket_create`, `ticket_update`, `ticket_move`,
1782
1899
  `ticket_comment`, `ticket_link` and `ticket_question_ask`. `ticket_comment.body` accepts markdown text or the
1783
1900
  kanban's text/reference-content array. A Claude worker's assignment uses
1784
1901
  `claude:<agent-session-id>` and a Codex worker's uses `codex:<native-id>`; the same value
@@ -1794,6 +1911,8 @@ agl ticket list --project agl --json
1794
1911
  agl ticket link AGL-1 --repo modelprofile.com/agl --branch feat/tickets --sha <full-sha> --json
1795
1912
  agl ticket move AGL-1 --column review --json
1796
1913
  agl ticket get AGL-1 --json
1914
+ agl ticket get AGL-1 --limit 20 --before <historyBefore> --json
1915
+ agl ticket get AGL-1 --comment <comment-id> --json
1797
1916
  agl ticket comment AGL-1 --body '**Checks passed**' --json
1798
1917
  agl ticket update AGL-1 --expected-revision <revision-from-get> --changes '{"priority":"high","labels":["backend"]}' --json
1799
1918
  agl ticket ask AGL-1 --text 'Which store?' --options '["Memory",{"label":"Database","description":"Durable"}]' --json
@@ -1801,6 +1920,18 @@ agl ticket list --label openQuestion --json
1801
1920
  agl ticket answer AGL-1 --question Q1 --choice 2 --text 'Keep it **small**.' --json
1802
1921
  ```
1803
1922
 
1923
+ `ticket_get` (`agl ticket get`, `agl.mcp.ticket.get`, `controller.ticket.get`) returns the
1924
+ ticket, all its questions and the newest page of its history, oldest first: `activity` holds
1925
+ up to `limit` entries (default 50, at most 500), cut earlier so that the entries and the comments
1926
+ they name stay within 256 KiB (always at least one entry), and `comments` the comments those
1927
+ entries name. A `commented` entry names its comment as `changes.comment: { after: { id } }`, so
1928
+ each comment body appears once (stored activity keeps the whole comment). While older history exists the response
1929
+ carries `historyBefore`; pass it as `before` (`--before`) to read the next older page. To read the
1930
+ one comment an activity entry or a fleet event names, pass its id as `commentId` (`--comment`)
1931
+ instead: the answer holds the ticket, its questions and that comment alone (`activity` empty, no
1932
+ `historyBefore`); it cannot be combined with `before` or `limit`, and an id that is not a comment
1933
+ of the ticket is refused with `ticket_comment_not_found`. The board in the browser reads every page.
1934
+
1804
1935
  All ten CLI actions accept `--port` and `--json`; `agl ticket --help` documents their
1805
1936
  options. The running controller is required. `update` and `assign` require `--expected-revision`
1806
1937
  from the ticket the editor read; neither command fetches a newer revision before saving.
@@ -2283,7 +2414,7 @@ Deleting a managed OpenCode conversation deletes the selected OpenCode conversat
2283
2414
 
2284
2415
  ### Models And Provider Accounts
2285
2416
 
2286
- AGL stores an independent default model for OpenCode, FlexHarness, Codex and Claude Code. A conversation can override its harness default without changing other conversations.
2417
+ AGL stores an independent default model for OpenCode, FlexHarness, Codex and Claude Code. A conversation can override its harness default without changing other conversations. **Settings → General** shows each harness's default as one button that says the model and its effort (`openai/gpt-5.6-sol · max`, without the harness the row already names) and opens both choices in a speech bubble; a saved default the harness no longer offers gives the button the warning tone.
2287
2418
 
2288
2419
  A stored default names a model as the harness offered it then, so AGL resolves it against the harness's live model list whenever a conversation is created or sent without a model of its own. While the harness offers it, it is sent. A stored default the harness no longer offers is never sent: the harness's own default stands in -- OpenCode's or Codex's configured model, or for Flex the catalog default of the account placement takes -- and when the harness offers no default AGL can name, OpenCode, Codex and Claude Code run their own default unnamed, while Flex, which has none of its own, asks for an account and model in the composer. The stored setting stays as the owner wrote it; `models_list` reports it as `unavailableSetting`, and Settings says under the default model that it is no longer offered and what new conversations run on instead.
2289
2420
 
@@ -2860,6 +2991,8 @@ Backend isolation is mandatory. Proxy mode binds the controller to all interface
2860
2991
  - Permission auto-accept is an explicit server policy, never an assumption based on an open browser.
2861
2992
  - Questions are never auto-answered.
2862
2993
  - Unknown, malformed, concurrent, or unverifiable lifecycle state fails closed.
2994
+ - 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.
2863
2996
 
2864
2997
  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.
2865
2998
 
@@ -2910,7 +3043,7 @@ A run that fails keeps its evidence before its root goes: when a test of the fil
2910
3043
  The suite guards, end to end: opening every harness's conversation across page reloads without a harness child being replaced, recovering the Flex child after `SIGKILL` within 15 seconds, rejoining the shared Codex daemon on its own after that daemon restarts (the conversation opens and answers again within one rejoin backoff step, without **Reconnect**), starting the controller again (as the same and as the next build) with a browser resource attached to a Codex, Flex and OpenCode chat, listing a project's resources right after a browser is attached to each new chat and after a restart, the Flex model list carrying the account's current catalog, a Flex conversation answering, and opening and answering again after a restart, in a project whose id begins with `-` or `_` (the file creates projects until one does), and an `agl mcp` bridge answering tool calls from the same process after the controller moves to the next build. It is the test step of `gitzone release` (`release.preflight` in `.smartconfig.json`) and takes about eight minutes.
2911
3044
 
2912
3045
 
2913
- Host prerequisites: Linux, Chrome (`google-chrome`, `chromium` or `chromium-browser`), `openssl`, `ss` (iproute2, to name the process behind a refused connection) and `codex` 0.156.0 or newer on `PATH`. Set `AGL_E2E_PACKAGE_ROOT` to another installed and built checkout, for example an earlier release, to run a scenario against it and prove it fails there:
3046
+ Host prerequisites: Linux, Chrome (`google-chrome`, `chromium` or `chromium-browser`), `openssl`, `ss` (iproute2, to name the process behind a refused connection) and `codex` 0.156.0 or newer on `PATH`. `e2e.proxyconfined` also runs a Docker container (a daemon this user may use and the `ubuntu:24.04` image already on the host, which the case never pulls) and skips with the missing prerequisite named without them. Set `AGL_E2E_PACKAGE_ROOT` to another installed and built checkout, for example an earlier release, to run a scenario against it and prove it fails there:
2914
3047
 
2915
3048
  ```sh
2916
3049
  AGL_E2E_PACKAGE_ROOT=/path/to/agl-32.15.1 pnpm exec tsrepo test test/e2e/e2e.conversations.node.ts --verbose --timeout 900
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: 'agl',
6
- version: '40.5.1',
6
+ version: '41.0.0',
7
7
  description: 'Agent Gateway Layer for OpenCode, FlexHarness, and Codex sessions'
8
8
  }