@stratusagent/gateway 0.14.7 → 0.14.9

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 (53) hide show
  1. package/dist/agent-source.d.ts +10 -0
  2. package/dist/agent-source.d.ts.map +1 -0
  3. package/dist/agent-source.js +7 -0
  4. package/dist/agent-source.js.map +1 -0
  5. package/dist/approvals.d.ts +38 -0
  6. package/dist/approvals.d.ts.map +1 -0
  7. package/dist/approvals.js +330 -0
  8. package/dist/approvals.js.map +1 -0
  9. package/dist/credential-requests.d.ts +24 -0
  10. package/dist/credential-requests.d.ts.map +1 -0
  11. package/dist/credential-requests.js +452 -0
  12. package/dist/credential-requests.js.map +1 -0
  13. package/dist/dispatch.d.ts +34 -0
  14. package/dist/dispatch.d.ts.map +1 -0
  15. package/dist/dispatch.js +461 -0
  16. package/dist/dispatch.js.map +1 -0
  17. package/dist/index.d.ts +4 -1290
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +202 -2546
  20. package/dist/index.js.map +1 -1
  21. package/dist/lifecycle.d.ts +130 -0
  22. package/dist/lifecycle.d.ts.map +1 -0
  23. package/dist/lifecycle.js +147 -0
  24. package/dist/lifecycle.js.map +1 -0
  25. package/dist/merge-batch.d.ts +60 -0
  26. package/dist/merge-batch.d.ts.map +1 -0
  27. package/dist/merge-batch.js +59 -0
  28. package/dist/merge-batch.js.map +1 -0
  29. package/dist/recovery.d.ts +49 -0
  30. package/dist/recovery.d.ts.map +1 -0
  31. package/dist/recovery.js +487 -0
  32. package/dist/recovery.js.map +1 -0
  33. package/dist/session-keys.d.ts +101 -0
  34. package/dist/session-keys.d.ts.map +1 -0
  35. package/dist/session-keys.js +148 -0
  36. package/dist/session-keys.js.map +1 -0
  37. package/dist/steering.d.ts +24 -0
  38. package/dist/steering.d.ts.map +1 -0
  39. package/dist/steering.js +108 -0
  40. package/dist/steering.js.map +1 -0
  41. package/dist/sub-agents.d.ts +20 -0
  42. package/dist/sub-agents.d.ts.map +1 -0
  43. package/dist/sub-agents.js +263 -0
  44. package/dist/sub-agents.js.map +1 -0
  45. package/dist/turn-dispatch.d.ts +34 -0
  46. package/dist/turn-dispatch.d.ts.map +1 -0
  47. package/dist/turn-dispatch.js +179 -0
  48. package/dist/turn-dispatch.js.map +1 -0
  49. package/dist/types.d.ts +1103 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +2 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +7 -7
@@ -0,0 +1,130 @@
1
+ import { type StateEnvironment } from '@stratusagent/state';
2
+ /** What a turn cut short by an announced restart records as its failure. */
3
+ export declare const RESTARTING_TURN_ERROR = "Run aborted: stratusd is restarting";
4
+ /** The error a turn still running when a stop's drain window closes is aborted with. */
5
+ export declare const STOPPING_TURN_ERROR = "Run aborted: stratusd is stopping";
6
+ /**
7
+ * How long an announced restart lets in-flight turns finish before aborting
8
+ * them. The same as a stop's window (`DEFAULT_STOP_DRAIN_TIMEOUT_MS`), so the
9
+ * two windows an operator can hit agree.
10
+ */
11
+ export declare const DEFAULT_RESTART_DRAIN_TIMEOUT_MS = 30000;
12
+ /** How long a roster reload waits on one channel's `reload()`. */
13
+ export declare const DEFAULT_CHANNEL_RELOAD_TIMEOUT_MS = 30000;
14
+ /**
15
+ * What one channel did with a roster reload. `reloaded`: its `reload()`
16
+ * resolved. `failed`: it rejected, with the reason. `still-reloading`: it
17
+ * had not settled within `channelReloadTimeoutMs`, and finishes on its own.
18
+ */
19
+ export interface ChannelReloadOutcome {
20
+ name: string;
21
+ outcome: 'reloaded' | 'failed' | 'still-reloading';
22
+ reason?: string;
23
+ }
24
+ /**
25
+ * How long a stop lets in-flight turns finish before aborting them. The
26
+ * daemon's documented drain ("up to 30 seconds"), and shorter than the
27
+ * service units' stop timeouts, so the abort happens in this process
28
+ * rather than as the manager's SIGKILL. That matters beyond the turn's
29
+ * record: a shell command runs in its own process group, which launchd's
30
+ * kill of the job does not reach, so only the turn's abort stops it.
31
+ */
32
+ export declare const DEFAULT_STOP_DRAIN_TIMEOUT_MS = 30000;
33
+ export interface RestartRequest {
34
+ /** Why, for the log — "plugin stratus-plugin-github enabled". */
35
+ reason?: string;
36
+ /** Overrides the gateway's drain window for this restart. */
37
+ drainTimeoutMs?: number;
38
+ }
39
+ /** What `restart()` announced: the terms the drain is running under. */
40
+ export interface RestartStatus {
41
+ reason?: string;
42
+ drainTimeoutMs: number;
43
+ /** Turns that were running when the restart was announced. */
44
+ inflight: number;
45
+ }
46
+ /** What the host is handed once the daemon has stopped for a restart. */
47
+ export interface RestartOutcome {
48
+ reason?: string;
49
+ /**
50
+ * Whether everything let go — every turn finished inside the window or
51
+ * after being aborted at it, every channel stopped, and every plugin
52
+ * released what it held. False means a turn ignored its abort for a
53
+ * whole second window, or a channel's stop or a plugin's dispose never
54
+ * settled or rejected, and the gateway shut down around it; a host
55
+ * should exit the process rather than start a new daemon beside a turn
56
+ * still writing, a channel still connected, or a plugin still holding a
57
+ * browser or a socket.
58
+ */
59
+ drained: boolean;
60
+ }
61
+ /** Whether the daemon is taking new work. */
62
+ export interface IntakeState {
63
+ paused: boolean;
64
+ /** What a refused message is answered with. Present only while paused. */
65
+ message?: string;
66
+ /** When the pause began (ISO 8601). Present only while paused. */
67
+ since?: string;
68
+ /**
69
+ * Whether this pause is the one the daemon started in because its intake
70
+ * file could not be read. Reported while paused: true only for that pause,
71
+ * false once a pause written since has replaced it, so `stratus doctor`
72
+ * can tell a startup warning about the file from a pause someone chose.
73
+ * Never written to the file.
74
+ */
75
+ unreadable?: boolean;
76
+ }
77
+ /** What a paused daemon says when nobody gave it a sentence of its own. */
78
+ export declare const DEFAULT_PAUSED_MESSAGE = "I'm paused right now and not taking new work. Please try again later.";
79
+ /** What a held gateway answers a dispatch with, before it is promoted. */
80
+ export declare const HELD_MESSAGE = "I'm starting up and not taking work yet. Please try again in a moment.";
81
+ /** The longest pause message, so a reply to a refused message stays one. */
82
+ export declare const MAX_PAUSED_MESSAGE_LENGTH = 500;
83
+ /** A pause message the gateway will not take: empty, or too long. */
84
+ export declare class PauseMessageError extends Error {
85
+ constructor(message: string);
86
+ }
87
+ /**
88
+ * A dispatch refused because intake is paused. Its message is the sentence
89
+ * to show whoever sent the work, as it is: a channel posts it as the reply,
90
+ * not as a failure.
91
+ */
92
+ export declare class IntakePausedError extends Error {
93
+ constructor(message: string);
94
+ }
95
+ /**
96
+ * A dispatch refused because the daemon is restarting or stopping. Channels
97
+ * stay connected while a restart drains, so they hear people during it, and
98
+ * this is what tells them the message reached a daemon on its way down: a
99
+ * channel can say so and keep the message for the next one. Matched by name
100
+ * there, as channel packages do not import the gateway.
101
+ */
102
+ export declare class GatewayStoppingError extends Error {
103
+ /** True for a restart, which comes back on its own; false for a stop. */
104
+ readonly restarting: boolean;
105
+ constructor(message: string, restarting: boolean);
106
+ }
107
+ /**
108
+ * The intake state a previous process left, or open when there is none.
109
+ *
110
+ * Fails closed: a file that exists but cannot be read or parsed starts the
111
+ * daemon paused, with a warning naming it. A pause exists to stop work (a
112
+ * spending cap, a maintenance window), and a file nobody can read is no
113
+ * evidence that it was lifted.
114
+ */
115
+ export declare const readIntakeState: (env: StateEnvironment, warn: (line: string) => void) => Promise<IntakeState>;
116
+ /**
117
+ * Record a pause durably (0600, replaced in one atomic step), or remove the
118
+ * record when intake reopens: open is the default, so nothing on disk
119
+ * means open.
120
+ */
121
+ export declare const writeIntakeState: (env: StateEnvironment, state: IntakeState) => Promise<void>;
122
+ /**
123
+ * `restart()` on a gateway whose host gave it no way back. Exported so the
124
+ * control API can answer it as "not supported here" rather than as a
125
+ * failure of the daemon.
126
+ */
127
+ export declare class RestartUnsupportedError extends Error {
128
+ constructor();
129
+ }
130
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,qBAAqB,CAAC;AAE7B,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,wCAAwC,CAAC;AAE3E,wFAAwF;AACxF,eAAO,MAAM,mBAAmB,sCAAsC,CAAC;AAEvE;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,QAAS,CAAC;AACvD,kEAAkE;AAClE,eAAO,MAAM,iCAAiC,QAAS,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,iBAAiB,CAAC;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,QAAS,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;OASG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,2EAA2E;AAC3E,eAAO,MAAM,sBAAsB,0EAA0E,CAAC;AAE9G,0EAA0E;AAC1E,eAAO,MAAM,YAAY,2EAA2E,CAAC;AAErG,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,qEAAqE;AACrE,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO;CAKjD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAU,KAAK,gBAAgB,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAG,OAAO,CAAC,WAAW,CA8B9G,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAU,KAAK,gBAAgB,EAAE,OAAO,WAAW,KAAG,OAAO,CAAC,IAAI,CAe9F,CAAC;AAEF;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;;CAQjD"}
@@ -0,0 +1,147 @@
1
+ /*
2
+ * Restart, stop and pause: the errors a cut-short turn records, the drain
3
+ * windows, what a restart or a roster reload reports, and the intake state a
4
+ * paused daemon keeps on disk. Moved out of `index.ts` as is; `index.ts`
5
+ * re-exports the public names, so the package's surface is unchanged.
6
+ */
7
+ import { randomBytes } from 'node:crypto';
8
+ import { mkdir, readFile, rename, rm } from 'node:fs/promises';
9
+ import path from 'node:path';
10
+ import { intakeStatePath, syncDirectory, writeFileSynced, } from '@stratusagent/state';
11
+ /** What a turn cut short by an announced restart records as its failure. */
12
+ export const RESTARTING_TURN_ERROR = 'Run aborted: stratusd is restarting';
13
+ /** The error a turn still running when a stop's drain window closes is aborted with. */
14
+ export const STOPPING_TURN_ERROR = 'Run aborted: stratusd is stopping';
15
+ /**
16
+ * How long an announced restart lets in-flight turns finish before aborting
17
+ * them. The same as a stop's window (`DEFAULT_STOP_DRAIN_TIMEOUT_MS`), so the
18
+ * two windows an operator can hit agree.
19
+ */
20
+ export const DEFAULT_RESTART_DRAIN_TIMEOUT_MS = 30_000;
21
+ /** How long a roster reload waits on one channel's `reload()`. */
22
+ export const DEFAULT_CHANNEL_RELOAD_TIMEOUT_MS = 30_000;
23
+ /**
24
+ * How long a stop lets in-flight turns finish before aborting them. The
25
+ * daemon's documented drain ("up to 30 seconds"), and shorter than the
26
+ * service units' stop timeouts, so the abort happens in this process
27
+ * rather than as the manager's SIGKILL. That matters beyond the turn's
28
+ * record: a shell command runs in its own process group, which launchd's
29
+ * kill of the job does not reach, so only the turn's abort stops it.
30
+ */
31
+ export const DEFAULT_STOP_DRAIN_TIMEOUT_MS = 30_000;
32
+ /** What a paused daemon says when nobody gave it a sentence of its own. */
33
+ export const DEFAULT_PAUSED_MESSAGE = "I'm paused right now and not taking new work. Please try again later.";
34
+ /** What a held gateway answers a dispatch with, before it is promoted. */
35
+ export const HELD_MESSAGE = "I'm starting up and not taking work yet. Please try again in a moment.";
36
+ /** The longest pause message, so a reply to a refused message stays one. */
37
+ export const MAX_PAUSED_MESSAGE_LENGTH = 500;
38
+ /** A pause message the gateway will not take: empty, or too long. */
39
+ export class PauseMessageError extends Error {
40
+ constructor(message) {
41
+ super(message);
42
+ this.name = 'PauseMessageError';
43
+ }
44
+ }
45
+ /**
46
+ * A dispatch refused because intake is paused. Its message is the sentence
47
+ * to show whoever sent the work, as it is: a channel posts it as the reply,
48
+ * not as a failure.
49
+ */
50
+ export class IntakePausedError extends Error {
51
+ constructor(message) {
52
+ super(message);
53
+ this.name = 'IntakePausedError';
54
+ }
55
+ }
56
+ /**
57
+ * A dispatch refused because the daemon is restarting or stopping. Channels
58
+ * stay connected while a restart drains, so they hear people during it, and
59
+ * this is what tells them the message reached a daemon on its way down: a
60
+ * channel can say so and keep the message for the next one. Matched by name
61
+ * there, as channel packages do not import the gateway.
62
+ */
63
+ export class GatewayStoppingError extends Error {
64
+ /** True for a restart, which comes back on its own; false for a stop. */
65
+ restarting;
66
+ constructor(message, restarting) {
67
+ super(message);
68
+ this.name = 'GatewayStoppingError';
69
+ this.restarting = restarting;
70
+ }
71
+ }
72
+ /**
73
+ * The intake state a previous process left, or open when there is none.
74
+ *
75
+ * Fails closed: a file that exists but cannot be read or parsed starts the
76
+ * daemon paused, with a warning naming it. A pause exists to stop work (a
77
+ * spending cap, a maintenance window), and a file nobody can read is no
78
+ * evidence that it was lifted.
79
+ */
80
+ export const readIntakeState = async (env, warn) => {
81
+ const file = intakeStatePath(env);
82
+ let raw;
83
+ try {
84
+ raw = await readFile(file, 'utf8');
85
+ }
86
+ catch (error) {
87
+ if (error.code === 'ENOENT') {
88
+ return { paused: false };
89
+ }
90
+ warn(`intake: ${file} could not be read (${error.message}); starting paused. Resume intake to clear it.`);
91
+ return { paused: true, since: new Date().toISOString(), unreadable: true };
92
+ }
93
+ try {
94
+ const parsed = JSON.parse(raw);
95
+ // Only a pause is ever written here (resuming removes the file), so
96
+ // anything else in it is not a record this build wrote, and the same
97
+ // rule as an unreadable file applies.
98
+ if (typeof parsed !== 'object' || parsed === null || parsed.paused !== true) {
99
+ warn(`intake: ${file} does not hold a pause record; starting paused. Resume intake to clear it.`);
100
+ return { paused: true, since: new Date().toISOString(), unreadable: true };
101
+ }
102
+ return {
103
+ paused: true,
104
+ ...(typeof parsed.message === 'string' && parsed.message.trim().length > 0 ? { message: parsed.message } : {}),
105
+ since: typeof parsed.since === 'string' ? parsed.since : new Date().toISOString(),
106
+ };
107
+ }
108
+ catch {
109
+ warn(`intake: ${file} is not valid JSON; starting paused. Resume intake to clear it.`);
110
+ return { paused: true, since: new Date().toISOString(), unreadable: true };
111
+ }
112
+ };
113
+ /**
114
+ * Record a pause durably (0600, replaced in one atomic step), or remove the
115
+ * record when intake reopens: open is the default, so nothing on disk
116
+ * means open.
117
+ */
118
+ export const writeIntakeState = async (env, state) => {
119
+ const file = intakeStatePath(env);
120
+ if (!state.paused) {
121
+ await rm(file, { force: true });
122
+ return;
123
+ }
124
+ await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
125
+ const staging = `${file}.${randomBytes(8).toString('hex')}`;
126
+ try {
127
+ await writeFileSynced(staging, `${JSON.stringify(state)}\n`, { flag: 'wx', mode: 0o600 });
128
+ await rename(staging, file);
129
+ await syncDirectory(path.dirname(file));
130
+ }
131
+ finally {
132
+ await rm(staging, { force: true });
133
+ }
134
+ };
135
+ /**
136
+ * `restart()` on a gateway whose host gave it no way back. Exported so the
137
+ * control API can answer it as "not supported here" rather than as a
138
+ * failure of the daemon.
139
+ */
140
+ export class RestartUnsupportedError extends Error {
141
+ constructor() {
142
+ super('This daemon cannot restart itself: the host that started it did not say how. '
143
+ + 'Restart it from the service manager (stratus service stop, then start) or from the terminal it runs in.');
144
+ this.name = 'RestartUnsupportedError';
145
+ }
146
+ }
147
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import { type ImageAttachment, type Session } from '@stratusagent/core';
2
+ import type { DispatchInput } from './types.ts';
3
+ /** A dispatch absorbed into a merged turn, waiting for its outcome. */
4
+ export interface MergeJoiner {
5
+ input: DispatchInput;
6
+ resolve: (session: Session) => void;
7
+ reject: (error: unknown) => void;
8
+ }
9
+ export interface MergeBatch {
10
+ agentId: string | undefined;
11
+ /** The dispatch that opened the batch and runs it, unless it is steered. */
12
+ leader: MergeJoiner;
13
+ /**
14
+ * Nothing was queued on the session's chain when it opened, so nothing
15
+ * can be ahead of it: the chain is first in, first out. Only such a batch
16
+ * is steered (card 948); one behind other work waits its turn.
17
+ */
18
+ firstInLine: boolean;
19
+ absorbed: MergeJoiner[];
20
+ /**
21
+ * Taken by the turn running ahead of it (card 948), which settles every
22
+ * caller in it: the leader's place on the chain then runs nothing.
23
+ */
24
+ steered: boolean;
25
+ /** Every image in the batch so far, leader's first. */
26
+ images: ImageAttachment[];
27
+ /** Characters of the merged user message so far, separators included. */
28
+ chars: number;
29
+ }
30
+ /**
31
+ * The most text one merged user message may hold (card 423). The runner's
32
+ * overflow recovery can drop earlier transcript but never part of the
33
+ * newest message, so a batch must not grow that message past what one
34
+ * message could be on its own: this is Slack's own message-length limit.
35
+ * A single message longer than this still runs, alone, as it would have
36
+ * without merging.
37
+ */
38
+ export declare const MERGED_TEXT_BUDGET_CHARS = 40000;
39
+ /** Separator between merged messages; counted against the text budget. */
40
+ export declare const MERGED_SEPARATOR = "\n\n";
41
+ /**
42
+ * Whether `images` all fit the replay budget together. A merged turn's
43
+ * images are one message's, and the runner empties whatever falls outside
44
+ * the budget before the provider sees it, so a batch that would overflow
45
+ * must not take the message that overflows it (card 423).
46
+ */
47
+ export declare const fitsReplayBudget: (images: readonly ImageAttachment[]) => boolean;
48
+ /** A signal that aborts once every one of `signals` has. */
49
+ export declare const allAborted: (signals: readonly AbortSignal[]) => AbortSignal;
50
+ /**
51
+ * `allAborted` for callers that keep joining: a turn that takes in messages
52
+ * at its tool steps (card 948) is cancelled only once every caller in it
53
+ * is, the ones who joined late included. A caller joining with no signal
54
+ * can never cancel, so from then on neither can the turn.
55
+ */
56
+ export declare const joinableSignal: (initial: readonly AbortSignal[]) => {
57
+ signal: AbortSignal;
58
+ join: (signal: AbortSignal | undefined) => void;
59
+ };
60
+ //# sourceMappingURL=merge-batch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-batch.d.ts","sourceRoot":"","sources":["../src/merge-batch.ts"],"names":[],"mappings":"AAOA,OAAO,EAA4B,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,4EAA4E;IAC5E,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,SAAS,eAAe,EAAE,KAAG,OAC+E,CAAC;AAEtJ,4DAA4D;AAC5D,eAAO,MAAM,UAAU,GAAI,SAAS,SAAS,WAAW,EAAE,KAAG,WAA6C,CAAC;AAE3G;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,SAAS,WAAW,EAAE,KAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,KAAK,IAAI,CAAA;CAwBtI,CAAC"}
@@ -0,0 +1,59 @@
1
+ /*
2
+ * A merged turn's batch: the dispatch that opened it and the ones absorbed
3
+ * into it while it waits, and the rules a batch grows by. Moved out of
4
+ * index.ts as is so the dispatch path split out of `createGateway` can name
5
+ * them; index.ts does not re-export them.
6
+ */
7
+ import { imagesWithinReplayBudget } from '@stratusagent/core';
8
+ /**
9
+ * The most text one merged user message may hold (card 423). The runner's
10
+ * overflow recovery can drop earlier transcript but never part of the
11
+ * newest message, so a batch must not grow that message past what one
12
+ * message could be on its own: this is Slack's own message-length limit.
13
+ * A single message longer than this still runs, alone, as it would have
14
+ * without merging.
15
+ */
16
+ export const MERGED_TEXT_BUDGET_CHARS = 40_000;
17
+ /** Separator between merged messages; counted against the text budget. */
18
+ export const MERGED_SEPARATOR = '\n\n';
19
+ /**
20
+ * Whether `images` all fit the replay budget together. A merged turn's
21
+ * images are one message's, and the runner empties whatever falls outside
22
+ * the budget before the provider sees it, so a batch that would overflow
23
+ * must not take the message that overflows it (card 423).
24
+ */
25
+ export const fitsReplayBudget = (images) => images.length === 0 || imagesWithinReplayBudget([{ id: '', createdAt: '', role: 'user', content: '', images: [...images] }]).size === images.length;
26
+ /** A signal that aborts once every one of `signals` has. */
27
+ export const allAborted = (signals) => joinableSignal(signals).signal;
28
+ /**
29
+ * `allAborted` for callers that keep joining: a turn that takes in messages
30
+ * at its tool steps (card 948) is cancelled only once every caller in it
31
+ * is, the ones who joined late included. A caller joining with no signal
32
+ * can never cancel, so from then on neither can the turn.
33
+ */
34
+ export const joinableSignal = (initial) => {
35
+ const controller = new AbortController();
36
+ const signals = [...initial];
37
+ let uncancellable = false;
38
+ const check = () => {
39
+ if (!uncancellable && !controller.signal.aborted && signals.every((signal) => signal.aborted)) {
40
+ controller.abort(signals[signals.length - 1]?.reason);
41
+ }
42
+ };
43
+ for (const signal of signals) {
44
+ signal.addEventListener('abort', check, { once: true });
45
+ }
46
+ check();
47
+ return {
48
+ signal: controller.signal,
49
+ join: (signal) => {
50
+ if (signal === undefined) {
51
+ uncancellable = true;
52
+ return;
53
+ }
54
+ signals.push(signal);
55
+ signal.addEventListener('abort', check, { once: true });
56
+ },
57
+ };
58
+ };
59
+ //# sourceMappingURL=merge-batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-batch.js","sourceRoot":"","sources":["../src/merge-batch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,wBAAwB,EAAsC,MAAM,oBAAoB,CAAC;AAgClG;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAE/C,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAkC,EAAW,EAAE,CAC9E,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,wBAAwB,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC;AAEtJ,4DAA4D;AAC5D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAA+B,EAAe,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;AAE3G;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAA+B,EAA4E,EAAE;IAC1I,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;IAC7B,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,CAAC,aAAa,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9F,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACxD,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE;YACf,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,aAAa,GAAG,IAAI,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { type AgentRunner, type AgentRuntimeContext, type EventBus, type JsonObject, type RunAbortedError, type Session } from '@stratusagent/core';
2
+ import type { AgentSource } from './agent-source.ts';
3
+ import type { SchedulerRuntime } from './schedules.ts';
4
+ import type { ShardedSessionStore } from './sessions.ts';
5
+ import type { DispatchInput } from './types.ts';
6
+ import { type RuntimeConfig } from '@stratusagent/state';
7
+ /** What restart recovery closes over in `createGateway`. */
8
+ export interface RecoveryDeps {
9
+ log: (line: string) => void;
10
+ warn: (line: string) => void;
11
+ bus: EventBus;
12
+ store: ShardedSessionStore;
13
+ scheduler: SchedulerRuntime;
14
+ effectiveApprovalTimeoutMs: number;
15
+ refreshAgent: (agentId: string) => Promise<AgentSource>;
16
+ runtimeContextFor: (source: AgentSource, config: RuntimeConfig, onFallback: boolean, metadata: JsonObject | undefined, subAgent?: boolean) => AgentRuntimeContext;
17
+ runnerFor: (config: RuntimeConfig, agentId: string, subAgent?: boolean) => AgentRunner;
18
+ withWatchdog: <T>(sessionId: string, external: AbortSignal | undefined, idleEnabled: boolean, fallbackStreams: boolean, run: (signal: AbortSignal) => Promise<T>) => Promise<T>;
19
+ isStopping: () => boolean;
20
+ trackTurn: (sessionId: string, controller: AbortController) => void;
21
+ untrackTurn: (sessionId: string, controller: AbortController) => void;
22
+ getAbortingWith: () => string | undefined;
23
+ shutdownAbort: () => RunAbortedError;
24
+ activeTurns: Map<string, string>;
25
+ onTurnRuntime: <T>(sessionId: string, runtime: {
26
+ source: AgentSource;
27
+ config: RuntimeConfig;
28
+ }, work: () => Promise<T>) => Promise<T>;
29
+ getOrphanedAtStart: () => ReadonlySet<string>;
30
+ runtimeForAgent: (source: AgentSource) => Promise<RuntimeConfig>;
31
+ watchdogStreamsFor: (session: Pick<Session, 'metadata'> | undefined, config: RuntimeConfig) => {
32
+ switchedToFallback: boolean;
33
+ effectiveStreams: boolean;
34
+ fallbackStreams: boolean;
35
+ };
36
+ reachesHarness: (config: RuntimeConfig, switchedToFallback: boolean) => boolean;
37
+ onSessionChain: <T>(sessionId: string, work: () => Promise<T>, released?: () => boolean) => Promise<T>;
38
+ }
39
+ export declare const createRecovery: ({ log, warn, bus, store, scheduler, effectiveApprovalTimeoutMs, refreshAgent, runtimeContextFor, runnerFor, withWatchdog, isStopping, trackTurn, untrackTurn, getAbortingWith, shutdownAbort, activeTurns, onTurnRuntime, getOrphanedAtStart, runtimeForAgent, watchdogStreamsFor, reachesHarness, onSessionChain, }: RecoveryDeps) => {
40
+ recoverParkedTurns: (orphaned: ReadonlySet<string>) => Promise<void>;
41
+ listOrphanedDelegations: () => Promise<Set<string>>;
42
+ listAbandonedTurns: () => Promise<string[]>;
43
+ failAbandonedTurns: (abandoned: string[]) => Promise<void>;
44
+ settleRepeat: (sessionId: string, idempotencyKey: string, agentId: string | undefined, turnId: string | undefined, onRepeat: DispatchInput["onRepeat"]) => Promise<Session | undefined>;
45
+ archivedRepeat: (session: Session, idempotencyKey: string) => Promise<Session | undefined>;
46
+ settleUnfinished: (session: Session) => Promise<Session>;
47
+ };
48
+ export type Recovery = ReturnType<typeof createRecovery>;
49
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"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"}