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.
- package/changelog.md +43 -0
- package/dist_serve/bundle.js +3514 -3357
- package/dist_serve/bundle.js.map +2 -2
- package/dist_serve/bundle.js.third-party-notices.json +58 -32
- package/dist_serve/bundle.js.third-party-notices.txt +108 -33
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/aglapi/aglapisurface.document.js +64 -37
- package/dist_ts/classes.aglhome.d.ts +5 -0
- package/dist_ts/classes.aglhome.js +2 -1
- package/dist_ts/classes.authstore.js +5 -1
- package/dist_ts/classes.controller.d.ts +8 -0
- package/dist_ts/classes.controller.js +119 -22
- package/dist_ts/classes.controllereventdelivery.d.ts +72 -0
- package/dist_ts/classes.controllereventdelivery.js +233 -0
- package/dist_ts/classes.flexmodels.public.js +4 -1
- package/dist_ts/classes.flexprojectionreader.d.ts +6 -0
- package/dist_ts/classes.flexprojectionreader.js +29 -5
- package/dist_ts/classes.flexservice.d.ts +2 -1
- package/dist_ts/classes.flexservice.js +34 -6
- package/dist_ts/classes.flexstore.d.ts +41 -4
- package/dist_ts/classes.flexstore.js +266 -203
- package/dist_ts/classes.inferencegateway.d.ts +35 -1
- package/dist_ts/classes.inferencegateway.js +86 -26
- package/dist_ts/classes.inferenceservice.d.ts +4 -2
- package/dist_ts/classes.inferenceservice.js +5 -4
- package/dist_ts/classes.proxycontrol.d.ts +58 -4
- package/dist_ts/classes.proxycontrol.js +23 -44
- package/dist_ts/classes.proxycontrolclient.d.ts +15 -5
- package/dist_ts/classes.proxycontrolclient.js +67 -17
- package/dist_ts/classes.proxydaemon.d.ts +22 -3
- package/dist_ts/classes.proxydaemon.js +73 -12
- package/dist_ts/classes.proxyleaseholder.d.ts +43 -14
- package/dist_ts/classes.proxyleaseholder.js +66 -22
- package/dist_ts/classes.proxystore.d.ts +9 -0
- package/dist_ts/classes.proxystore.js +15 -7
- package/dist_ts/classes.ticketnotifier.js +4 -2
- package/dist_ts/classes.ticketstore.d.ts +50 -2
- package/dist_ts/classes.ticketstore.js +195 -78
- package/dist_ts/classes.workercallerbindings.d.ts +66 -0
- package/dist_ts/classes.workercallerbindings.js +211 -0
- package/dist_ts/classes.workercallers.d.ts +63 -2
- package/dist_ts/classes.workercallers.js +430 -65
- package/dist_ts/driver/classes.drivereventrelay.d.ts +13 -1
- package/dist_ts/driver/classes.drivereventrelay.js +99 -6
- package/dist_ts/driver/functions.driverinstructions.d.ts +1 -1
- package/dist_ts/driver/functions.driverinstructions.js +1 -1
- package/dist_ts/driver/functions.drivertools.d.ts +11 -2
- package/dist_ts/driver/functions.drivertools.js +19 -9
- package/dist_ts/fleet/classes.boardpump.d.ts +8 -2
- package/dist_ts/fleet/classes.boardpump.js +38 -20
- package/dist_ts/fleet/classes.ticketeffects.d.ts +17 -2
- package/dist_ts/fleet/classes.ticketeffects.js +16 -10
- package/dist_ts/fleet/classes.workerlaunches.d.ts +12 -5
- package/dist_ts/fleet/classes.workerlaunches.js +25 -19
- package/dist_ts/fleet/classes.workers.d.ts +21 -0
- package/dist_ts/fleet/classes.workers.js +110 -17
- package/dist_ts/fleet/functions.workervalidation.js +2 -2
- package/dist_ts/functions.flexprivatesnapshot.d.ts +102 -0
- package/dist_ts/functions.flexprivatesnapshot.js +461 -0
- package/dist_ts/functions.inferencecli.d.ts +3 -2
- package/dist_ts/functions.inferencecli.js +67 -5
- package/dist_ts/functions.inferencecliarguments.d.ts +6 -2
- package/dist_ts/functions.inferencecliarguments.js +29 -2
- package/dist_ts/functions.inferencelaunch.d.ts +24 -0
- package/dist_ts/functions.inferencelaunch.js +42 -2
- package/dist_ts/functions.mcptickettools.d.ts +3 -0
- package/dist_ts/functions.mcptickettools.js +2 -2
- package/dist_ts/functions.proxycontract.d.ts +11 -0
- package/dist_ts/functions.proxycontract.js +12 -1
- package/dist_ts/functions.proxysocket.d.ts +6 -0
- package/dist_ts/functions.proxysocket.js +46 -0
- package/dist_ts/functions.ticketcli.d.ts +1 -1
- package/dist_ts/functions.ticketcli.js +8 -4
- package/dist_ts/functions.ticketvalidation.d.ts +3 -0
- package/dist_ts/functions.ticketvalidation.js +7 -4
- package/dist_ts/functions.workerprocessproof.d.ts +6 -0
- package/dist_ts/functions.workerprocessproof.js +28 -4
- package/dist_ts/interfaces.flexipc.d.ts +21 -2
- package/dist_ts/interfaces.flexipc.js +33 -16
- package/dist_ts_interfaces/fleetworkers.d.ts +6 -0
- package/dist_ts_interfaces/interfaces.d.ts +1 -1
- package/dist_ts_interfaces/interfaces.js +1 -1
- package/dist_ts_interfaces/requests.tickets.d.ts +8 -12
- package/dist_ts_interfaces/tickets.d.ts +37 -9
- package/dist_ts_interfaces/tickets.js +7 -1
- package/dist_ts_interfaces/workercallers.d.ts +43 -4
- package/package.json +7 -7
- package/readme.md +156 -23
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/aglapi/aglapisurface.document.ts +63 -36
- package/ts/classes.aglhome.ts +6 -0
- package/ts/classes.authstore.ts +4 -0
- package/ts/classes.controller.ts +141 -32
- package/ts/classes.controllereventdelivery.ts +289 -0
- package/ts/classes.flexmodels.public.ts +10 -0
- package/ts/classes.flexprojectionreader.ts +30 -3
- package/ts/classes.flexservice.ts +33 -5
- package/ts/classes.flexstore.ts +381 -246
- package/ts/classes.inferencegateway.ts +104 -23
- package/ts/classes.inferenceservice.ts +6 -3
- package/ts/classes.proxycontrol.ts +76 -39
- package/ts/classes.proxycontrolclient.ts +72 -20
- package/ts/classes.proxydaemon.ts +85 -15
- package/ts/classes.proxyleaseholder.ts +84 -28
- package/ts/classes.proxystore.ts +16 -5
- package/ts/classes.ticketnotifier.ts +3 -1
- package/ts/classes.ticketstore.ts +201 -75
- package/ts/classes.workercallerbindings.ts +147 -0
- package/ts/classes.workercallers.ts +410 -59
- package/ts/driver/classes.drivereventrelay.ts +89 -4
- package/ts/driver/functions.driverinstructions.ts +1 -1
- package/ts/driver/functions.drivertools.ts +22 -8
- package/ts/fleet/classes.boardpump.ts +51 -19
- package/ts/fleet/classes.ticketeffects.ts +25 -11
- package/ts/fleet/classes.workerlaunches.ts +23 -15
- package/ts/fleet/classes.workers.ts +79 -17
- package/ts/fleet/functions.workervalidation.ts +1 -1
- package/ts/functions.flexprivatesnapshot.ts +601 -0
- package/ts/functions.inferencecli.ts +61 -7
- package/ts/functions.inferencecliarguments.ts +32 -1
- package/ts/functions.inferencelaunch.ts +49 -2
- package/ts/functions.mcptickettools.ts +1 -1
- package/ts/functions.proxycontract.ts +12 -0
- package/ts/functions.proxysocket.ts +39 -0
- package/ts/functions.ticketcli.ts +7 -3
- package/ts/functions.ticketvalidation.ts +7 -3
- package/ts/functions.workerprocessproof.ts +27 -4
- package/ts/interfaces.flexipc.ts +50 -21
- package/ts_interfaces/fleetworkers.ts +6 -0
- package/ts_interfaces/interfaces.ts +1 -1
- package/ts_interfaces/requests.tickets.ts +9 -6
- package/ts_interfaces/tickets.ts +32 -1
- package/ts_interfaces/workercallers.ts +32 -4
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements.harnesscontrollerapp.ts +19 -8
- package/ts_web/elements.ticketboard.ts +16 -4
- 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:
|
|
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 =
|
|
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,
|
|
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
|
|
58
|
-
*
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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": "
|
|
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.
|
|
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.
|
|
36
|
-
"@modelprofile.com/flexharness": "16.
|
|
37
|
-
"@modelprofile.com/flexharness-agent": "16.
|
|
38
|
-
"@modelprofile.com/flexharness-models": "16.
|
|
39
|
-
"@modelprofile.com/flexharness-providers": "16.
|
|
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.
|
|
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.
|
|
1536
|
-
|
|
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
|
|
1557
|
-
driver's
|
|
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.
|
|
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
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
are
|
|
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
|
|
1757
|
-
|
|
1758
|
-
|
|
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
|
-
-
|
|
1764
|
-
|
|
1765
|
-
|
|
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
|
-
|
|
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
|