@stratusagent/gateway 0.14.7 → 0.14.8

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/dist/index.d.ts CHANGED
@@ -1,10 +1,4 @@
1
- import { EventBus, type AgentDefinition, type CredentialScope, type AlwaysMeans, type ApprovalDelegationOrigin, type ApprovalScheduleDestination, type ApprovalAnswer, type ApprovalOutcome, type ApprovalPolicy, type ImageAttachment, type ObserveEntry, type JsonObject, type Session, type Tool, type ToolCall, type ToolRisk, type UnlistedTools, type TrustLevel } from '@stratusagent/core';
2
- import { type ConversationReader, type ConversationArchiver, type ConversationCreator, type ConversationInviter, type ConversationJoiner, type ParsedSoul, type ScheduleRecord } from '@stratusagent/agents';
3
- import { type MemoryPassDetail } from './memory-passes.ts';
4
- import { type EventIntakeRequest, type EventIntakeResponse, type EventSourceConfig, type EventsSummary } from './event-intake.ts';
5
- import { type EventDeliveryRow } from './events.ts';
6
- import { type SchedulerLimits } from './schedules.ts';
7
- import { ShardedSessionStore } from './sessions.ts';
1
+ import type { Gateway, GatewayOptions } from './types.ts';
8
2
  export { claimHome, homeLockPath, HomeClaimedError, type HomeClaim, } from './lock.ts';
9
3
  export { SqliteSessionStore, ShardedSessionStore, FleetSessionIndex, SessionIdTakenError, tightenSqliteFile, type SqliteSessionStoreOptions, type ShardedSessionStoreOptions, type SessionIndexRow, type SessionListRow, type SessionReconcileReport, } from './sessions.ts';
10
4
  export { SqliteScheduleStore, createSchedulerRuntime, isScheduleSessionId, SCHEDULE_SESSION_ID_PREFIX, type PreviousRun, type ScheduleStoreLike, type SchedulerLimits, type SchedulerRuntime, type SchedulerRuntimeOptions, } from './schedules.ts';
@@ -12,8 +6,6 @@ export { SqliteEventStore, EVENT_BLOCK_MAX_BYTES, EVENT_DEDUP_WINDOW_MS, EVENT_D
12
6
  export { type MemoryPassDetail } from './memory-passes.ts';
13
7
  export { REVIEW_RULES, readReviewReply, reviewCaseText, type ReviewCaseInput } from './review.ts';
14
8
  export { createEventIntake, mapGithubDelivery, parseStandardWebhookEvent, verifyGithubSignature, verifyStandardWebhookSignature, DEFAULT_EVENT_MAX_BODY_BYTES, EVENT_SOURCE_REQUESTS_PER_MINUTE, type EventIntake, type EventIntakeCounters, type EventIntakeOptions, type EventIntakeRequest, type EventIntakeResponse, type EventSourceConfig, type EventSourceCounters, type EventSourceSummary, type EventsSummary, type EventTrigger, } from './event-intake.ts';
15
- import { type OptionalModuleHost } from '@stratusagent/plugins';
16
- import { type PluginsConfig, type RuntimeSelection, type StateEnvironment, type MemoryPassSummary } from '@stratusagent/state';
17
9
  /**
18
10
  * A soul's provider/model pins, and the daemon-wide defaults they demote.
19
11
  *
@@ -24,1287 +16,6 @@ import { type PluginsConfig, type RuntimeSelection, type StateEnvironment, type
24
16
  * question even when the rule answering it is not gateway code.
25
17
  */
26
18
  export { applySoulPins, type SoulPinContext } from '@stratusagent/state';
27
- /**
28
- * A channel adapter as the gateway sees it: started after the roster
29
- * loads, stopped before the store drains. Structurally identical to
30
- * `ChannelAdapter` in @stratusagent/core, declared here because it
31
- * predates the channel contract moving into core, when the gateway could
32
- * not import it without depending on the channels package.
33
- */
34
- export interface GatewayChannelAdapter {
35
- name: string;
36
- start(gateway: Gateway): Promise<void>;
37
- stop(): Promise<void>;
38
- /**
39
- * Whether the gateway may serve without this channel.
40
- *
41
- * A channel whose `start()` rejects is logged and skipped, which is right
42
- * for a Slack app whose tokens went stale: the rest of the fleet stays
43
- * up. A required channel fails `start()` instead — after stopping the
44
- * channels that did come up, and before the scheduler starts or any
45
- * sweep re-asks an approval, so a daemon refused this way has done no
46
- * work. The control API is the case: a second `stratus serve` on the
47
- * same home lost the port to the first, logged it, and went on serving
48
- * with no channel at all — and the next scheduled firing ran in it,
49
- * where nobody could approve or hear anything. A host that omits this
50
- * gets the skip.
51
- */
52
- required?: boolean;
53
- /**
54
- * Whether this channel starts while the gateway is held (`held: true`).
55
- * Only a surface for the operator should: the control API, so health can
56
- * be asked and `promote` sent. A conversation channel must not, or the
57
- * held daemon would take work. Absent means it waits for `promote()`.
58
- */
59
- servesWhileHeld?: boolean;
60
- /**
61
- * The addressable outbound seam, mirroring `ChannelAdapter.resolveOutbound`
62
- * structurally for the same reason the rest of this interface does. Optional: an
63
- * adapter with no concept of a destination simply lacks it, and the
64
- * gateway treats absence as "this channel cannot be spoken through".
65
- * Implementations MUST reject a destination they could not deliver to,
66
- * with a message fit to show the person who named it.
67
- */
68
- resolveOutbound?(address: {
69
- agentId: string;
70
- to: string;
71
- thread?: string;
72
- speaker?: {
73
- sessionId: string;
74
- trust: TrustLevel;
75
- };
76
- }): Promise<{
77
- post(text: string): Promise<unknown>;
78
- }>;
79
- /**
80
- * The read side of an addressable conversation, mirroring
81
- * `ChannelAdapter.readConversation`. Optional:
82
- * an adapter without it cannot be read through `message.read`.
83
- * Implementations MUST reject a conversation their app may not read,
84
- * with a sentence for the agent.
85
- */
86
- readConversation?(request: {
87
- agentId: string;
88
- conversation: string;
89
- thread?: string;
90
- after?: string;
91
- before?: string;
92
- limit: number;
93
- sessionId?: string;
94
- images?: {
95
- count: number;
96
- bytes: number;
97
- };
98
- }): ReturnType<ConversationReader>;
99
- /**
100
- * Creates a conversation with the agent's own app, mirroring
101
- * `ChannelAdapter.createConversation`. Optional: an adapter without it
102
- * cannot serve `channel.create`. Implementations MUST reject what they
103
- * can tell is invalid before creating anything, and MUST resolve once
104
- * the conversation exists, reporting what did not happen after it.
105
- */
106
- createConversation?(request: Omit<Parameters<ConversationCreator>[0], 'channel'>): ReturnType<ConversationCreator>;
107
- /**
108
- * Joins an existing conversation with the agent's own app, mirroring
109
- * `ChannelAdapter.joinConversation`. Optional: an adapter without it
110
- * cannot serve `channel.join`.
111
- */
112
- joinConversation?(request: Omit<Parameters<ConversationJoiner>[0], 'channel'>): ReturnType<ConversationJoiner>;
113
- /**
114
- * Invites people to an existing conversation through the agent's own
115
- * app, mirroring `ChannelAdapter.inviteToConversation`. Optional: an
116
- * adapter without it cannot serve `channel.invite`. Implementations
117
- * MUST reject only when nobody was invited.
118
- */
119
- inviteToConversation?(request: Omit<Parameters<ConversationInviter>[0], 'channel'>): ReturnType<ConversationInviter>;
120
- /**
121
- * Archives a conversation the agent's own app created, mirroring
122
- * `ChannelAdapter.archiveConversation`. Optional: an adapter without it
123
- * cannot serve `channel.archive`. The gateway has already refused the
124
- * conversation the work was asked from, which it asks the same adapter's
125
- * `sessionConversation`; without that, every archive is refused.
126
- */
127
- archiveConversation?(request: Omit<Parameters<ConversationArchiver>[0], 'channel'>): ReturnType<ConversationArchiver>;
128
- /** See `ChannelAdapter.accountFor`. */
129
- accountFor?(agentId: string): string | undefined;
130
- /** See `ChannelAdapter.conversationKindFor`. */
131
- conversationKindFor?(agentId: string, conversation: string): Promise<'channel' | 'direct' | undefined>;
132
- /** See `ChannelAdapter.sessionConversation`. */
133
- sessionConversation?(agentId: string, sessionId: string): string | undefined;
134
- /**
135
- * Picks up which agents this channel carries without a restart, mirroring
136
- * `ChannelAdapter.reload`. The gateway calls it on every started channel
137
- * after a roster reload, one channel at a time, and stops waiting after
138
- * `channelReloadTimeoutMs`. Optional: a channel without it changes only
139
- * at a restart.
140
- */
141
- reload?(): Promise<void>;
142
- /**
143
- * Shows a person a form for a credential an agent asked for, mirroring
144
- * `ChannelAdapter.requestCredential`. Optional:
145
- * a channel without it cannot take credentials, and a request made in
146
- * its conversations is refused instead of announced. Implementations
147
- * MUST reject when the form reached nobody who can answer it, with a
148
- * sentence for the agent.
149
- */
150
- requestCredential?(request: {
151
- sessionId: string;
152
- agentId: string;
153
- requestId: string;
154
- name: string;
155
- scope: CredentialScope;
156
- reason?: string;
157
- replace?: boolean;
158
- env?: string;
159
- metadata: JsonObject;
160
- }): Promise<void>;
161
- /** See `ChannelAdapter.credentialReplaceForms`. */
162
- credentialReplaceForms?: boolean;
163
- /** See `ChannelAdapter.credentialEnvForms`. */
164
- credentialEnvForms?: boolean;
165
- /** See `ChannelAdapter.say`. */
166
- say?(request: {
167
- sessionId: string;
168
- agentId: string;
169
- text: string;
170
- }): Promise<void>;
171
- /**
172
- * Issues a one-time link to a form for a credential request, for the
173
- * agent to pass on: what a request falls back to when its conversation
174
- * cannot show a form, and what an agent asking `via: 'link'` gets. The
175
- * control API implements it. Optional: a host with no adapter carrying it
176
- * refuses such requests with the machine-side command instead.
177
- *
178
- * The link is the whole credential: whoever opens it can answer the
179
- * request once, through `provideCredential`, which stays add-only and
180
- * grants only the agent that asked.
181
- */
182
- requestCredentialLink?(request: {
183
- sessionId: string;
184
- agentId: string;
185
- agentName: string;
186
- requestId: string;
187
- name: string;
188
- scope: CredentialScope;
189
- reason?: string;
190
- /** The variable the agent's shell will read the key from; the page must say so. */
191
- env?: string;
192
- }): Promise<{
193
- url: string;
194
- expiresAt: string;
195
- /** The link is on an address only the machine itself can open (no `api.publicUrl`). */
196
- localOnly?: boolean;
197
- }>;
198
- }
199
- /**
200
- * Where a session came from, as the gateway reports it. Structural for the
201
- * same reason as the adapter above: core's channel contract declares the
202
- * matching shape (`SessionRouting`) for adapters to consume.
203
- */
204
- export interface SessionRouting {
205
- /** Whose app must do the talking. */
206
- agentId: string;
207
- /** The metadata the dispatching surface attached to the session. */
208
- metadata: JsonObject;
209
- /**
210
- * When this agent last spoke, ISO-8601 — its newest reply's timestamp,
211
- * absent when it has never produced one. Not the session's modification
212
- * time, which a mid-turn save moves; see `SessionRouting` in @stratusagent/core.
213
- */
214
- lastSpokeAt?: string;
215
- /** The latest turn's text (`latestTurnReply`), when it produced any — see `SessionRouting` in @stratusagent/core. */
216
- reply?: string;
217
- /** Whether the turn the session is on is one nobody asked for (`isUnaddressedTurn`) — see `SessionRouting` in @stratusagent/core. */
218
- unaddressed?: boolean;
219
- }
220
- /**
221
- * What the gateway hands a policy that needs a human somewhere else: one
222
- * function that publishes a request and settles on the answer. Handed to a
223
- * factory rather than exposed on the built gateway because the policy has
224
- * to exist before the first dispatch, and a gateway cannot be constructed
225
- * with a policy that needs the gateway.
226
- */
227
- export interface ApprovalTransport {
228
- request: ApprovalRequester;
229
- /**
230
- * Which plugin package contributes a tool right now, or undefined for a
231
- * kernel tool (`@stratusagent/permissions`' `grants.contributorOf` takes
232
- * exactly this shape). Read live from the plugins' tool records rather
233
- * than snapshotted, for the same reason `tools()` is: a bridge registers
234
- * what its server advertises, and a grant compared against a stale map
235
- * would follow the wrong tool.
236
- */
237
- contributorOf(toolName: string): string | undefined;
238
- /**
239
- * The schedule carve-out, for a policy that understands destinations
240
- * (`@stratusagent/permissions`' `destinations` option takes exactly this
241
- * shape). True when the session is a firing of a schedule that still
242
- * exists, belongs to this agent, and was approved with this destination
243
- * — the gateway answers from the schedule row, so cancelling revokes.
244
- */
245
- destinations: {
246
- isPreauthorized(session: Session, destination: string): boolean;
247
- /**
248
- * The workspace the agent's channel for this `channel:to` destination
249
- * is connected to (`ChannelAdapter.accountFor`), so a standing grant
250
- * on one conversation is bound to the workspace it was made in.
251
- */
252
- accountFor(agentId: string, destination: string): string | undefined;
253
- /**
254
- * Whether a `channel:to` destination is a channel or a DM, asked of the
255
- * agent's own app for that channel (`ChannelAdapter.conversationKindFor`).
256
- * Undefined when no adapter can say.
257
- */
258
- conversationKindFor(agentId: string, destination: string): Promise<'channel' | 'direct' | undefined>;
259
- };
260
- /**
261
- * The review posture's reviewer (`@stratusagent/permissions`' `review`
262
- * option wraps it): one bounded model call on the agent's own provider
263
- * and credentials, exactly the model named (the agent's own when none is)
264
- * with no fallback and no tools, counted by the provider-call guards like
265
- * any other call. The case it is shown holds the people's own words and
266
- * the call, never tool output (`review.ts`).
267
- *
268
- * Never rejects: a review that couldn't run answers `failed` with a kind
269
- * a person reads in the log, and the policy asks about the call.
270
- */
271
- review(request: GatewayReviewRequest): Promise<{
272
- reply: Record<string, unknown>;
273
- } | {
274
- failed: string;
275
- }>;
276
- /**
277
- * The values of the stored credentials this agent's soul lists, for the
278
- * review posture's built-in rule against sending one out. Read from disk
279
- * per call, like every credential, so a key set a moment ago counts.
280
- * Never logged; an unreadable store answers nothing.
281
- */
282
- secretsFor(agentId: string): Promise<string[]>;
283
- /**
284
- * Work the policy runs off the call path, such as a shadow review
285
- * (`@stratusagent/permissions`' `review.offPath`), from its start: its
286
- * credential read as well as its reviewer call. A shutdown waits for it
287
- * after the turns have drained, so what it reports isn't lost.
288
- */
289
- offPath(task: Promise<unknown>): void;
290
- }
291
- /** One call put to the reviewer. Mirrors `@stratusagent/permissions`' `ReviewRequest`. */
292
- export interface GatewayReviewRequest {
293
- session: Session;
294
- call: ToolCall;
295
- tool: Tool;
296
- risk: ToolRisk;
297
- command?: string;
298
- origin?: string;
299
- destination?: string;
300
- /** `approvals.review.model`; undefined for the agent's own model. */
301
- model?: string;
302
- signal: AbortSignal;
303
- }
304
- /**
305
- * A parked call, published for anyone who can reach a person. Mirrors
306
- * `@stratusagent/permissions`' `ApprovalRequest` structurally rather than
307
- * importing it: the gateway consumes any `ApprovalPolicy`, and depending on
308
- * one implementation of the seam to describe the seam would invert that.
309
- */
310
- export interface GatewayApprovalRequest {
311
- session: Session;
312
- call: {
313
- id: string;
314
- toolName: string;
315
- input: JsonObject;
316
- };
317
- risk: ToolRisk;
318
- /**
319
- * The site this call would act on, for a tool judged by one
320
- * (`Tool.originFor`). Passed straight through to whatever renders the
321
- * request, because the call's arguments do not say where a click lands.
322
- */
323
- origin?: string;
324
- /**
325
- * True when answering `always` runs this call once and remembers
326
- * nothing, so a renderer can stop offering one. Passed straight through.
327
- */
328
- oneShot?: boolean;
329
- /** What `always` would remember, when it would. Passed straight through. */
330
- always?: AlwaysMeans;
331
- /** Why the review posture is asking. Passed straight through. */
332
- reviewNote?: string;
333
- /**
334
- * When this call first parked, if a restart is re-asking it. The wait is
335
- * measured from here, so a request keeps the window it started with
336
- * instead of winning a fresh one — otherwise a daemon that restarts a
337
- * minute before the deadline grants another full timeout, and one that
338
- * crash-loops keeps a request alive forever.
339
- */
340
- parkedAt?: string;
341
- signal?: AbortSignal;
342
- }
343
- /**
344
- * Settles with the answer and, when a person gave it, who — the policy
345
- * records the actor on a standing grant. Every ending settles: a timeout,
346
- * an abort, and a shutdown are each a `deny` with nobody behind it.
347
- */
348
- export type ApprovalRequester = (request: GatewayApprovalRequest) => Promise<ApprovalOutcome>;
349
- /**
350
- * A parked call as an observer sees it — everything `tool.approval-requested`
351
- * announced, still outstanding.
352
- *
353
- * Listable because an event stream only tells you what happened while you
354
- * were listening. A surface that connects while a turn is already parked
355
- * would otherwise show no pending request at all until the next one, and
356
- * rebuilding the set from the bus means keeping a second copy of state the
357
- * gateway already holds.
358
- */
359
- export interface PendingApproval {
360
- requestId: string;
361
- sessionId: string;
362
- agentId: string;
363
- call: {
364
- id: string;
365
- toolName: string;
366
- input: JsonObject;
367
- };
368
- risk: ToolRisk;
369
- /** The site it would act on, for a tool judged by one. */
370
- origin?: string;
371
- /** True when `always` on this call remembers nothing — see the event. */
372
- oneShot?: boolean;
373
- /** What `always` would remember, when it would — see the event. */
374
- always?: AlwaysMeans;
375
- /** Why the review posture is asking — see the event. */
376
- reviewNote?: string;
377
- /** When this call parked, ISO-8601. */
378
- parkedAt: string;
379
- /**
380
- * When it denies itself, ISO-8601. Absent when it never will — the same
381
- * distinction the announcing event draws.
382
- */
383
- expiresAt?: string;
384
- /** The session's metadata: where the turn is happening. */
385
- metadata?: JsonObject;
386
- /** Where a delegated call's work was asked from — see the event. */
387
- delegatedFrom?: ApprovalDelegationOrigin;
388
- /** Where a scheduled firing's schedule reports — see the event. */
389
- scheduleDestination?: ApprovalScheduleDestination;
390
- }
391
- export interface ResolveApprovalInput {
392
- requestId: string;
393
- answer: ApprovalAnswer;
394
- /**
395
- * The person who decided, in whatever ids their channel uses. Recorded on
396
- * the resolved event; who is *allowed* to decide is the channel's
397
- * question, since the approver set is written in its ids.
398
- */
399
- actor?: string;
400
- /**
401
- * Why, when it was not a person. A channel that cannot deliver a request
402
- * — nobody configured to ask, nowhere to ask — still has to settle it,
403
- * and recording that as `decided` would file a denial nobody made
404
- * alongside the ones somebody did. `timeout` and `cancelled` are not
405
- * offered here: those are the gateway's own endings to declare.
406
- */
407
- reason?: 'decided' | 'undeliverable';
408
- }
409
- /** One registered tool, as an operator's surfaces need to see it. */
410
- export interface GatewayTool {
411
- name: string;
412
- description?: string;
413
- risk: ToolRisk;
414
- /** The plugin package that contributed it; absent for kernel tools. */
415
- package?: string;
416
- /** Whether that package is trusted to declare a tool `safe`. */
417
- trusted?: boolean;
418
- /**
419
- * What a call to it is judged by, when it offers exactly one scope hook
420
- * (`Tool.commandFor`, `originFor`, `destinationFor`): what an
421
- * `approvals.rules` matcher for it can name. Absent for none or several.
422
- */
423
- scopedBy?: 'command' | 'origin' | 'destination';
424
- }
425
- /** One skill this daemon serves — identity and provenance, never the body. */
426
- export interface GatewaySkill {
427
- /** Canonical id: bare for an operator-installed skill, `<package>:<skill>` for a plugin's. */
428
- id: string;
429
- name: string;
430
- description: string;
431
- /** The bare id a plugin's skill also answers to, while no one else claims it. */
432
- alias?: string;
433
- /** The plugin package that contributed it; absent for operator-installed skills. */
434
- package?: string;
435
- /** Where the SKILL.md lives. */
436
- path?: string;
437
- /** Ships with Stratus and is enabled for every agent, whatever its soul's `skills:` says. */
438
- builtin?: boolean;
439
- }
440
- /** What a turn cut short by an announced restart records as its failure. */
441
- export declare const RESTARTING_TURN_ERROR = "Run aborted: stratusd is restarting";
442
- /** The error a turn still running when a stop's drain window closes is aborted with. */
443
- export declare const STOPPING_TURN_ERROR = "Run aborted: stratusd is stopping";
444
- /**
445
- * How long an announced restart lets in-flight turns finish before aborting
446
- * them. The same as a stop's window (`DEFAULT_STOP_DRAIN_TIMEOUT_MS`), so the
447
- * two windows an operator can hit agree.
448
- */
449
- export declare const DEFAULT_RESTART_DRAIN_TIMEOUT_MS = 30000;
450
- /** How long a roster reload waits on one channel's `reload()`. */
451
- export declare const DEFAULT_CHANNEL_RELOAD_TIMEOUT_MS = 30000;
452
- /**
453
- * What one channel did with a roster reload. `reloaded`: its `reload()`
454
- * resolved. `failed`: it rejected, with the reason. `still-reloading`: it
455
- * had not settled within `channelReloadTimeoutMs`, and finishes on its own.
456
- */
457
- export interface ChannelReloadOutcome {
458
- name: string;
459
- outcome: 'reloaded' | 'failed' | 'still-reloading';
460
- reason?: string;
461
- }
462
- /**
463
- * How long a stop lets in-flight turns finish before aborting them. The
464
- * daemon's documented drain ("up to 30 seconds"), and shorter than the
465
- * service units' stop timeouts, so the abort happens in this process
466
- * rather than as the manager's SIGKILL. That matters beyond the turn's
467
- * record: a shell command runs in its own process group, which launchd's
468
- * kill of the job does not reach, so only the turn's abort stops it.
469
- */
470
- export declare const DEFAULT_STOP_DRAIN_TIMEOUT_MS = 30000;
471
- export interface RestartRequest {
472
- /** Why, for the log — "plugin stratus-plugin-github enabled". */
473
- reason?: string;
474
- /** Overrides the gateway's drain window for this restart. */
475
- drainTimeoutMs?: number;
476
- }
477
- /** What `restart()` announced: the terms the drain is running under. */
478
- export interface RestartStatus {
479
- reason?: string;
480
- drainTimeoutMs: number;
481
- /** Turns that were running when the restart was announced. */
482
- inflight: number;
483
- }
484
- /** What the host is handed once the daemon has stopped for a restart. */
485
- export interface RestartOutcome {
486
- reason?: string;
487
- /**
488
- * Whether everything let go — every turn finished inside the window or
489
- * after being aborted at it, every channel stopped, and every plugin
490
- * released what it held. False means a turn ignored its abort for a
491
- * whole second window, or a channel's stop or a plugin's dispose never
492
- * settled or rejected, and the gateway shut down around it; a host
493
- * should exit the process rather than start a new daemon beside a turn
494
- * still writing, a channel still connected, or a plugin still holding a
495
- * browser or a socket.
496
- */
497
- drained: boolean;
498
- }
499
- /** Whether the daemon is taking new work. */
500
- export interface IntakeState {
501
- paused: boolean;
502
- /** What a refused message is answered with. Present only while paused. */
503
- message?: string;
504
- /** When the pause began (ISO 8601). Present only while paused. */
505
- since?: string;
506
- /**
507
- * Whether this pause is the one the daemon started in because its intake
508
- * file could not be read. Reported while paused: true only for that pause,
509
- * false once a pause written since has replaced it, so `stratus doctor`
510
- * can tell a startup warning about the file from a pause someone chose.
511
- * Never written to the file.
512
- */
513
- unreadable?: boolean;
514
- }
515
- /** What a paused daemon says when nobody gave it a sentence of its own. */
516
- export declare const DEFAULT_PAUSED_MESSAGE = "I'm paused right now and not taking new work. Please try again later.";
517
- /** What a held gateway answers a dispatch with, before it is promoted. */
518
- export declare const HELD_MESSAGE = "I'm starting up and not taking work yet. Please try again in a moment.";
519
- /** The longest pause message, so a reply to a refused message stays one. */
520
- export declare const MAX_PAUSED_MESSAGE_LENGTH = 500;
521
- /** A pause message the gateway will not take: empty, or too long. */
522
- export declare class PauseMessageError extends Error {
523
- constructor(message: string);
524
- }
525
- /**
526
- * A dispatch refused because intake is paused. Its message is the sentence
527
- * to show whoever sent the work, as it is: a channel posts it as the reply,
528
- * not as a failure.
529
- */
530
- export declare class IntakePausedError extends Error {
531
- constructor(message: string);
532
- }
533
- /**
534
- * A dispatch refused because the daemon is restarting or stopping. Channels
535
- * stay connected while a restart drains, so they hear people during it, and
536
- * this is what tells them the message reached a daemon on its way down: a
537
- * channel can say so and keep the message for the next one. Matched by name
538
- * there, as channel packages do not import the gateway.
539
- */
540
- export declare class GatewayStoppingError extends Error {
541
- /** True for a restart, which comes back on its own; false for a stop. */
542
- readonly restarting: boolean;
543
- constructor(message: string, restarting: boolean);
544
- }
545
- /**
546
- * `restart()` on a gateway whose host gave it no way back. Exported so the
547
- * control API can answer it as "not supported here" rather than as a
548
- * failure of the daemon.
549
- */
550
- export declare class RestartUnsupportedError extends Error {
551
- constructor();
552
- }
553
- /** A plugin this daemon was asked to load, and what came of it. */
554
- export interface GatewayPluginStatus {
555
- package: string;
556
- name?: string;
557
- trusted?: boolean;
558
- tools?: GatewayTool[];
559
- skills?: GatewaySkill[];
560
- /** Provider names it registered — what a soul's `provider:` can select. */
561
- providers?: string[];
562
- /** Channel kinds it registered, and the agents each carries. */
563
- channels?: Array<{
564
- kind: string;
565
- agents: string[];
566
- }>;
567
- /** Memory store names it registered — what `memoryStore` can select. */
568
- memory?: string[];
569
- /** Executor names it registered — what `executor` can select. */
570
- executors?: string[];
571
- /** Present when the plugin did not load, and the only field that is. */
572
- error?: string;
573
- }
574
- /** One provider a soul's `provider:` can select on this daemon. */
575
- export interface GatewayProviderStatus {
576
- /** The name as a soul writes it: `anthropic`, or a plugin's `ollama`. */
577
- name: string;
578
- /** The plugin package that registered it; absent for a built-in. */
579
- package?: string;
580
- }
581
- export interface GatewayOptions {
582
- env?: StateEnvironment;
583
- /** Gateway-wide provider/model overrides applied beneath per-soul pins. */
584
- selection?: RuntimeSelection;
585
- /** Channel adapters to run (Slack, …). Started on start(), stopped on stop(). */
586
- channels?: GatewayChannelAdapter[];
587
- /**
588
- * Which (kind, agent) pairs the host's own adapters carry, so a plugin
589
- * channel claiming one of them is refused at load rather than started
590
- * beside it. The Slack adapter the CLI wires reports its agents here;
591
- * a host that omits this lets a plugin double up on a host adapter.
592
- */
593
- hostChannelClaims?: Array<{
594
- kind: string;
595
- agents: readonly string[];
596
- }>;
597
- /**
598
- * What an agent whose soul has no `tools:` list may call; see
599
- * `AgentRunnerOptions.unlistedTools`. Omitted, the gateway asks the home
600
- * (`unlistedToolsFor`): every tool on one an older build set up, safe
601
- * tools only on a new one.
602
- */
603
- unlistedTools?: UnlistedTools;
604
- /**
605
- * The policy, or a factory that builds one from the gateway's approval
606
- * transport. Pass a factory whenever the policy parks turns on a human
607
- * reached through a channel — `remote` mode — since that policy needs the
608
- * transport the gateway is still constructing.
609
- *
610
- * Omitted, the runners get core's default: only `safe` tools run, and
611
- * every other call is refused with an error naming this option. Pass
612
- * `AllowAllApprovalPolicy` from `@stratusagent/core` to run every call.
613
- */
614
- approvals?: ApprovalPolicy | ((transport: ApprovalTransport) => ApprovalPolicy);
615
- /**
616
- * How long a parked call waits before it denies itself. 0 waits forever,
617
- * which is only ever right in a test. Default 15 minutes.
618
- */
619
- approvalTimeoutMs?: number;
620
- /**
621
- * How many tool turns one dispatched turn may take before the runner
622
- * wraps it up with a summary. Default 40 (`DEFAULT_MAX_TURNS` in core).
623
- *
624
- * Applies to every runner this gateway builds, so a delegated
625
- * sub-session is held to the same ceiling as the turn that delegated it
626
- * — they are separate dispatches and each gets its own allowance, not a
627
- * share of one. `stratus serve` fills it from the trusted config's
628
- * `maxTurns`.
629
- */
630
- maxTurns?: number;
631
- /**
632
- * One agent's own budget, replacing `maxTurns` for its messages and the
633
- * sub-sessions it delegates to that agent. Undefined means the shared one.
634
- * `stratus serve` fills it from the trusted config's `agentMaxTurns`.
635
- */
636
- maxTurnsFor?: (agentId: string) => number | undefined;
637
- /**
638
- * Whether an agent keeps working past its budget instead of wrapping up
639
- * (`AgentRunnerOptions.autoContinue`): `true` without a cap, a number for
640
- * that many extra allowances per message, undefined for off. `stratus
641
- * serve` fills it from the trusted config's `autoContinue`.
642
- */
643
- autoContinueFor?: (agentId: string) => true | number | undefined;
644
- /**
645
- * The activity watchdog: abort a turn when no event for its session has
646
- * arrived for this long. Progress-based, not wall-clock — any delta, tool
647
- * event, or response resets it. 0 disables. Default 120s.
648
- */
649
- idleTimeoutMs?: number;
650
- /**
651
- * The events listener's sources and body limit (card 590), from the
652
- * trusted config's `events` block. With no sources the event store is
653
- * never opened, and every delivery is to an unknown source.
654
- */
655
- events?: {
656
- sources?: Record<string, EventSourceConfig>;
657
- maxBodyBytes?: number;
658
- };
659
- /**
660
- * Where the per-agent session stores (`agents/<id>/sessions.db`) and the
661
- * fleet database (`fleet.db`, holding the schedules and the session
662
- * index) live. Default `~/.stratus`.
663
- *
664
- * A directory rather than the single database path it replaced: after
665
- * per-agent state there is no one file to point at, which is the
666
- * point — a store is opened on one agent's path, so no query can return
667
- * another agent's rows.
668
- */
669
- stateDir?: string;
670
- /**
671
- * Scheduler limits — the interval floor, the per-agent cap on concurrent
672
- * scheduled turns, and the tick cadence. Defaults are production values;
673
- * tests lower them.
674
- */
675
- schedules?: SchedulerLimits;
676
- /**
677
- * Plugins to load, keyed by package name — the `plugins` block, which the
678
- * caller has already read from a **trusted** config. The gateway does not
679
- * go looking for one: a plugin runs in-process with the daemon, and which
680
- * file may say so is a decision that belongs where the config precedence
681
- * is already understood.
682
- */
683
- plugins?: PluginsConfig;
684
- /**
685
- * Which executor runs tool calls, by the name a plugin registered it
686
- * under (`local`, or absent, is the built-in child-process executor).
687
- * From a **trusted** config only, like `plugins`: an executor is where
688
- * an agent's commands run, and a cloned repository swapping a sandbox
689
- * for the host is the downgrade that rule exists to refuse. A name no
690
- * loaded plugin registered fails `start()` — a daemon silently running
691
- * commands on the host when the operator selected a sandbox is worse
692
- * than one that did not start.
693
- */
694
- executor?: string;
695
- /**
696
- * Host-owned limits for sub-agents (roadmap 24). `maxConcurrentPerTurn`
697
- * caps how many one parent turn runs at once; the rest wait for a slot.
698
- * It must be a whole number of at least 1, or `createGateway` throws.
699
- */
700
- subAgents?: {
701
- maxConcurrentPerTurn?: number;
702
- };
703
- /**
704
- * Which store backs agent memory, by registered name (`file`, or absent,
705
- * is the built-in). Same trust rule and same refusal as `executor`: a
706
- * daemon writing memories into a store the operator did not select is a
707
- * daemon whose agents remember into the wrong place.
708
- */
709
- memoryStore?: string;
710
- /**
711
- * How plugin packages are resolved and imported.
712
- *
713
- * Worth passing, and `stratus serve` does. `import.meta.resolve` answers
714
- * relative to the module that calls it, so the default here asks "is this
715
- * plugin visible from the gateway package" when the question is "is it
716
- * visible from the thing the operator installed". Those differ under any
717
- * layout that is not flat — a pnpm install being the obvious one — and the
718
- * failure is a plugin reported as not installed while sitting right next
719
- * to the CLI.
720
- */
721
- pluginHost?: OptionalModuleHost;
722
- /**
723
- * How this daemon comes back after `restart()`: called once the gateway
724
- * has stopped, by the host that owns the process — `stratus serve`
725
- * starts a fresh daemon and supervises it. The gateway can drain and
726
- * stop; only its host can start a process. A host that omits this gives
727
- * up `restart()`, which then throws `RestartUnsupportedError`.
728
- */
729
- onRestart?: (outcome: RestartOutcome) => void | Promise<void>;
730
- /**
731
- * Asked by {@link Gateway.checkRestart} before a restart is announced,
732
- * while this daemon still serves: a throw refuses the restart with its
733
- * message and leaves the daemon up. `stratus serve` checks here that the
734
- * trusted config will load, because the replacement refuses to start on
735
- * one that will not (#214) — and a restart that drained first would turn
736
- * a half-saved file into an outage. A host that omits this restarts
737
- * unchecked.
738
- */
739
- restartPreflight?: () => Promise<void>;
740
- /**
741
- * Start held: migrate, load the roster, and bring up only the channels
742
- * that serve while held (the control API), so the daemon answers health
743
- * and nothing else. No conversation channel connects, no schedule fires,
744
- * no parked approval is re-asked and no abandoned turn is failed, and a
745
- * dispatch is refused, until `promote()`. For an upgrade that has to prove
746
- * the new version healthy before it touches any work.
747
- */
748
- held?: boolean;
749
- /**
750
- * Called once after a held gateway is promoted and its work has begun.
751
- * `stratus serve --hold-new-version` records the running version here,
752
- * so the next start of the same version is not held. A rejection is
753
- * warned about and does not undo the promotion: the daemon is serving.
754
- */
755
- onPromoted?: () => Promise<void>;
756
- /** The drain window a `restart()` uses when the request names none. Default 30s. */
757
- restartDrainTimeoutMs?: number;
758
- /**
759
- * How long a roster reload waits on one channel's `reload()` before it
760
- * goes on without it. Default 30s. The reload is not cancelled: the
761
- * channel finishes on its own, and its outcome is logged when it does.
762
- */
763
- channelReloadTimeoutMs?: number;
764
- /** The drain window a `stop()` uses. Default 30s; see `DEFAULT_STOP_DRAIN_TIMEOUT_MS`. */
765
- stopDrainTimeoutMs?: number;
766
- log?: (line: string) => void;
767
- warn?: (line: string) => void;
768
- }
769
- export interface DispatchInput {
770
- /**
771
- * Stable session id chosen by the caller — channels derive it from the
772
- * conversation (e.g. a Slack thread), so any later message with the same
773
- * id resumes the conversation, across daemon restarts included.
774
- */
775
- sessionId: string;
776
- /** Roster agent id. Defaults to the gateway's default agent. */
777
- agentId?: string;
778
- userMessage: string;
779
- /** Images sent with the message — see `Message.images` in `@stratusagent/core`. */
780
- images?: ImageAttachment[];
781
- /**
782
- * Whether the message was said to the agent — see `RunInput.addressed`
783
- * in `@stratusagent/core`. `false` runs a turn the agent may answer with
784
- * nothing; omitted, the turn is one somebody asked for.
785
- */
786
- addressed?: boolean;
787
- /**
788
- * Said to the agent, but possibly needing no answer — see
789
- * `RunInput.replyOptional` in `@stratusagent/core` (card 601). Messages
790
- * merged into one turn (card 423) keep it only if every one of them
791
- * carries it: one that owes an answer makes the turn owe it.
792
- */
793
- replyOptional?: boolean;
794
- /**
795
- * What was said before this message that the agent never heard — see
796
- * `RunInput.earlier` in `@stratusagent/core`. Used only when this
797
- * dispatch creates the session; a session that already exists ignores
798
- * it, so a caller that cannot tell which case it is in may always send it.
799
- */
800
- earlier?: ObserveEntry[];
801
- metadata?: JsonObject;
802
- signal?: AbortSignal;
803
- /**
804
- * Caller-chosen id for this turn, so events emitted while it runs can be
805
- * attributed to it. A session processes several turns in sequence and
806
- * `StratusEvent` carries no turn identifier, so a surface that queued a
807
- * message has no other way to tell its own deltas from the next caller's.
808
- *
809
- * Optional because most callers do not need one: a channel renders
810
- * whatever arrives for the conversation it is watching.
811
- */
812
- turnId?: string;
813
- /**
814
- * The caller's name for this message, unique within the session — for a
815
- * channel, the platform's own message id. A repeat never starts a second
816
- * turn: while the first is queued or running the repeat resolves with it;
817
- * once it has finished the repeat resolves with the session as it stands,
818
- * running nothing and emitting nothing — which may have moved on since,
819
- * so the repeated message's own reply is `turnReplyFor(session, key)`,
820
- * not `latestTurnReply`; and a turn whose process died
821
- * mid-run is continued, once (`AgentRunner.continueTurn`), by whichever
822
- * comes first — the repeat or the next start's sweep. Not on a harness
823
- * (`runsOnHarness`), whose own tool loop may already have acted on the
824
- * prompt: that turn is failed, as an unkeyed one is.
825
- *
826
- * Stored on the user message in the write that accepts it
827
- * (`Message.idempotencyKey`), so it lasts as long as the transcript does.
828
- * A repeat's other fields are not compared, and its `signal` does not
829
- * reach a turn it attaches to; a failed turn stays failed, and retrying
830
- * it takes a new key.
831
- *
832
- * Optional, and a host that omits it gives up crash-safe dedupe: a
833
- * channel that delivers at least once can only dedupe in memory, which a
834
- * restart erases, and a turn that was running when the process died is
835
- * failed at the next start instead of finished.
836
- */
837
- idempotencyKey?: string;
838
- /**
839
- * Called, before the dispatch resolves, when it repeats a turn rather
840
- * than starting or continuing one, and says which kind of repeat:
841
- *
842
- * - `live` — it attached to a turn another dispatch with the same key
843
- * started in this process and is still waiting on. That dispatch is
844
- * alive to render the outcome, so a caller with a reply of its own in
845
- * progress takes it down and posts nothing.
846
- * - `finished` — the turn is over and nothing ran. Its outcome may never
847
- * have been posted — the process can die between the turn's last write
848
- * and the channel's — and nothing durable says whether it was, so the
849
- * caller posts it from the session: `turnReplyFor`, `turnFilesFor`, and
850
- * `turnFailureFor` from `@stratusagent/core`, since the session may
851
- * have moved on and no event will carry any of it. Said twice beats
852
- * never said.
853
- *
854
- * Not called for a repeat that continues a turn a crash left unfinished:
855
- * that turn runs, its events are this caller's, and nobody else is
856
- * rendering it.
857
- */
858
- onRepeat?: (repeat: 'live' | 'finished') => void;
859
- /**
860
- * Opts this dispatch into merged turns. A message that arrives while the
861
- * session's turn is running is not given a turn of its own: every
862
- * opted-in, addressed dispatch queued by the time that turn ends is
863
- * answered by ONE next turn, its messages joined in arrival order in one
864
- * user message (`Message.absorbedKeys` keeps each one's key, so a repeat
865
- * is that turn). No delay is added; the batch is whatever queued.
866
- *
867
- * The first queued dispatch leads: its `turnId` stamps the merged turn's
868
- * events, and it resolves as any dispatch does. Each later one is
869
- * absorbed — called here, before it resolves (or rejects) with the same
870
- * outcome — and, like a `live` repeat, posts nothing of its own: the
871
- * leader's caller renders the answer. A caller that cannot withdraw what
872
- * it put up leaves this unset and keeps one turn per dispatch.
873
- *
874
- * The merged turn runs at the least trusted sender in it. A turn nobody
875
- * asked for (`addressed: false`) is never merged.
876
- *
877
- * Steering (card 948): while the running turn is itself an opted-in,
878
- * addressed dispatch for the same agent, the batch waiting behind it is
879
- * handed to that turn at its next tool step instead (`RunInput.steer`),
880
- * so the agent reads it before its next call rather than after its whole
881
- * turn. Every dispatch in the batch, its leader included, is then
882
- * absorbed into the running turn — called here and settled with that
883
- * turn's outcome — and the running turn's caller renders the answer. A
884
- * turn that answers without another tool step leaves the batch to run
885
- * next, merged, as above.
886
- */
887
- onAbsorbed?: () => void;
888
- }
889
- export interface ObserveInput {
890
- sessionId: string;
891
- /** Must match the session's agent when given; a session never crosses identities. */
892
- agentId?: string;
893
- /** What was said, speaker included — see `ObserveInput.message` in core. */
894
- message: string;
895
- /** This message's metadata, read for the speaker's trust; never merged. */
896
- metadata?: JsonObject;
897
- }
898
- export interface Gateway {
899
- start(): Promise<void>;
900
- stop(): Promise<void>;
901
- /** The one entrypoint: resolve the agent, load-or-create the session, run a turn. */
902
- dispatch(input: DispatchInput): Promise<Session>;
903
- /**
904
- * A message into an existing session with no turn run on it —
905
- * `AgentRunner.observe`, on the session's chain so an overheard message
906
- * and a turn never interleave writes, and behind the same two refusals
907
- * as `dispatch`: the scheduler's reserved id namespace and the daemon's
908
- * reserved metadata keys, since text entering a session's context is
909
- * text entering its next prompt whichever door it came through.
910
- *
911
- * Existing only: an agent overhears a conversation it is already in, and
912
- * a session that would have to be created is one it was never invited
913
- * to — so a session that does not exist is nothing to hear into, and
914
- * resolves `undefined` rather than refusing. Read on the chain, which is
915
- * what makes that answer right: an invitation still in flight — a first
916
- * mention whose dispatch is queued ahead of this — has created the
917
- * session by the time this runs, where a read outside the chain would
918
- * have found nothing and dropped the message for good. A rolled-over
919
- * transcript refuses like a dispatch would.
920
- */
921
- observe(input: ObserveInput): Promise<Session | undefined>;
922
- /** Live events from every runner, one stream for all consumers. */
923
- readonly bus: EventBus;
924
- /** The store shared by every runner (durable across restarts). */
925
- readonly store: ShardedSessionStore;
926
- /** The current roster, default agent included. */
927
- agents(): AgentDefinition[];
928
- /**
929
- * Re-read the agents directory and the configured default soul, returning
930
- * the roster that results. Souls are already re-read on every dispatch, so
931
- * this is for files that appeared or disappeared since start — an agent
932
- * created by another surface, or one whose soul was deleted.
933
- */
934
- reloadRoster(): Promise<AgentDefinition[]>;
935
- /**
936
- * `reloadRoster()`, then what each started channel that can reload
937
- * (`ChannelAdapter.reload`) did with it, in start order: where a secret
938
- * stored while the daemon runs is picked up without a restart. A channel
939
- * missing from the list can't reload, and reads its secrets at the next
940
- * start. Rejects as `reloadRoster()` does, when the roster won't load.
941
- */
942
- reloadRosterAndChannels(): Promise<{
943
- agents: AgentDefinition[];
944
- channels: ChannelReloadOutcome[];
945
- }>;
946
- /**
947
- * Re-read `~/.stratus/skills/` and serve what is there now, without a
948
- * restart — the shape `reloadRoster()` established: idempotent,
949
- * serialized against itself and the roster reload, and a failure that
950
- * leaves the previous set serving. Plugin-contributed skills are rebuilt
951
- * from the plugins already loaded (never re-imported), in the same order
952
- * they loaded, so operator precedence and contested aliases come out as
953
- * a restart would produce them.
954
- *
955
- * A skill that would not load — a malformed `SKILL.md`, a directory whose
956
- * name is not an id — rejects with the file named and changes nothing:
957
- * half a catalog is worse than a stale one.
958
- *
959
- * Loaded is not enabled: a reloaded skill still reaches only the agents
960
- * whose souls list it.
961
- */
962
- reloadSkills(): Promise<GatewaySkill[]>;
963
- /**
964
- * Announce a restart: refuse new turns now, let running ones finish
965
- * inside the drain window, abort what is still running at the end of
966
- * it, stop, and hand the process back to the host through `onRestart`.
967
- * Returns at once with the terms; the drain runs behind it. Idempotent —
968
- * a second call while one is underway reports the same announcement.
969
- *
970
- * What comes back is what a stop-and-start brings back: durable sessions,
971
- * schedules, and channels; a call parked on a human is denied as
972
- * cancelled, exactly as a stop denies it. The refusal comes first, which
973
- * makes the drain's known gap smaller — a turn finishing after the
974
- * snapshot can still start work the snapshot never saw — but does not
975
- * close it.
976
- */
977
- restart(request?: RestartRequest): RestartStatus;
978
- /**
979
- * Whether a `restart()` now would bring the daemon back, as the host
980
- * judges it (`restartPreflight`): rejects with the host's refusal,
981
- * resolves when there is none. Separate from `restart()`, which stays
982
- * synchronous and idempotent, so a caller asks this first — `POST
983
- * /restart` does.
984
- */
985
- checkRestart(): Promise<void>;
986
- /**
987
- * The turn currently running on a session, if the caller that started it
988
- * named one. Single-flight per session is what makes this exact: at most
989
- * one turn is ever running on a session, so an event emitted for it
990
- * belongs to this turn and no other.
991
- */
992
- activeTurnId(sessionId: string): string | undefined;
993
- /**
994
- * True while a side run is running under this session id: a reviewer
995
- * call (`review:…`) or a memory pass (`memory-pass:…`). Its events reach
996
- * `bus` like a turn's, so whatever counts provider calls there counts
997
- * them, but it is not a conversation: nobody can send to it, it is never
998
- * stored, and a surface that lists conversations leaves it out. Known by
999
- * the run, not the id's shape, so a dispatched session whose id happens
1000
- * to start the same way is still a conversation.
1001
- */
1002
- isSideRun(sessionId: string): boolean;
1003
- /**
1004
- * Aborts every turn running on a session right now, one recovered after a
1005
- * restart included, the way the watchdog and a restart's window do: each
1006
- * fails with `reason`, its session saved. Returns how many turns are on
1007
- * the session, each now stopping: one aborted earlier that has not
1008
- * settled yet still counts (it is not aborted again), so 0 means nothing
1009
- * is running there at all. An idle or unknown session is 0 and nothing
1010
- * happens. A message queued on the session but not started is left to
1011
- * its caller's own signal. The caller decides who may stop; this asks
1012
- * nothing.
1013
- */
1014
- abortTurns(sessionId: string, reason: string): number;
1015
- /** Every call parked on a human right now, oldest first. */
1016
- pendingApprovals(): PendingApproval[];
1017
- /**
1018
- * Every schedule the fleet has set, oldest first — the audit list the
1019
- * spec demands: an agent that scheduled something an operator cannot see
1020
- * or stop is a bug. Each row is also the scope of its own destination
1021
- * grant, so this doubles as the list of standing permissions to speak.
1022
- */
1023
- schedules(): ScheduleRecord[];
1024
- /**
1025
- * Cancel any agent's schedule — the operator's stop button, and the
1026
- * revocation of the destination grant riding on the row. False when no
1027
- * such schedule exists.
1028
- */
1029
- cancelSchedule(scheduleId: string): boolean;
1030
- /**
1031
- * One request to the events port (card 590): verified, deduplicated,
1032
- * matched against event triggers and recorded, all before the answer.
1033
- * The listener in control-api calls this with the raw body.
1034
- */
1035
- receiveEvent(request: EventIntakeRequest): Promise<EventIntakeResponse>;
1036
- /**
1037
- * Per configured event source: the last delivery, the last refusal and
1038
- * why, and the last 24 hours by status, with the requests refused before
1039
- * they could leave a row (`stratus events`, `/health`). No sources when
1040
- * none is configured.
1041
- */
1042
- eventSummary(): EventsSummary;
1043
- /** Delivery rows, newest first; a row never holds the body. Empty when no source is configured. */
1044
- eventDeliveries(options?: {
1045
- source?: string;
1046
- limit?: number;
1047
- }): EventDeliveryRow[];
1048
- /**
1049
- * An agent's memory passes from its ledger, newest first (spec 962).
1050
- * Read from the file each time, so a pass that just ended shows.
1051
- */
1052
- memoryPasses(agentId: string): Promise<MemoryPassSummary[]>;
1053
- /**
1054
- * One pass and the facts it wrote, from the store this daemon writes
1055
- * to (in shadow, the facts it would have written). Undefined when the
1056
- * ledger has no such pass.
1057
- */
1058
- memoryPass(agentId: string, passId: string): Promise<MemoryPassDetail | undefined>;
1059
- /**
1060
- * Every registered tool, with the risk it will actually be judged at and
1061
- * the package it came from.
1062
- *
1063
- * Provenance is the reason this is not `tools.describe()`: "which of
1064
- * these can this daemon run, and whose code is it" is the question the
1065
- * catalog endpoint and 07's tool screens are asking, and a descriptor
1066
- * list answers only half of it. Kernel tools have no package, which is
1067
- * itself the answer for them.
1068
- */
1069
- tools(): GatewayTool[];
1070
- /**
1071
- * Every skill this daemon serves — operator-installed and
1072
- * plugin-contributed — with the ids an allowlist can name. Descriptors
1073
- * only: bodies stay behind `skill.read`, on the agent's own turn.
1074
- */
1075
- skills(): GatewaySkill[];
1076
- /**
1077
- * The plugins this daemon loaded, and the ones it could not — a plugin an
1078
- * operator enabled that failed to load is exactly what they need to see,
1079
- * and an empty list would say the opposite.
1080
- */
1081
- plugins(): GatewayPluginStatus[];
1082
- /**
1083
- * Every provider a soul's `provider:` can select on this daemon: the
1084
- * built-ins, then what loaded plugins registered, in load order. The
1085
- * registry, not the literal — a surface listing providers reads this so
1086
- * a contributed one is offered where the built-ins are.
1087
- */
1088
- providers(): GatewayProviderStatus[];
1089
- /**
1090
- * The soul behind each agent in the current roster, built-in included
1091
- * (which has none).
1092
- *
1093
- * The roster the daemon is *serving*, which the agents directory stops
1094
- * describing the moment a soul is added, deleted, or broken since the last
1095
- * reload — and the gateway keeps dispatching from the soul it cached. A
1096
- * caller that has to answer "what would a turn as this agent run on" needs
1097
- * the pins that answer it, so this hands back the parsed soul rather than a
1098
- * path a second reader would have to re-read (and now read differently).
1099
- *
1100
- * Async because it re-reads each soul file, exactly as `refreshAgent` does
1101
- * before every dispatch: a pin edited on disk is live on the next turn, so
1102
- * a caller answering from the load-time snapshot would name the provider
1103
- * the daemon has already stopped billing. An unreadable file falls back to
1104
- * the cached soul, which is what that next dispatch falls back to as well.
1105
- *
1106
- * An agent is **absent** from this list when its file now declares a
1107
- * different id: `refreshAgent` refuses every dispatch for the old one until
1108
- * the roster reloads, so nothing is served under it and no runtime should
1109
- * be claimed for it. The roster it reports is therefore what can currently
1110
- * run, which is a narrower thing than `agents()`.
1111
- */
1112
- servedSouls(): Promise<Array<{
1113
- id: string;
1114
- soul?: ParsedSoul;
1115
- }>>;
1116
- /**
1117
- * Where a durable session came from — its agent, and the metadata the
1118
- * dispatching surface attached to it.
1119
- *
1120
- * A projection rather than an accessor: the session's messages are not
1121
- * a channel's business, and handing back the whole record to save a few
1122
- * lines would make them one.
1123
- */
1124
- sessionRouting(sessionId: string): Promise<SessionRouting | undefined>;
1125
- /**
1126
- * Whether a session already accepted the message an idempotency key names,
1127
- * as addressed to it: a turn was started for it, finished, running or
1128
- * still queued, here or in the transcript a recent rollover archived
1129
- * (`settleRepeat` reads the same). A turn nobody asked for
1130
- * (`addressed: false`) does not count — that agent heard the message, it
1131
- * did not take it. Lets a channel route a redelivered message to the agent
1132
- * that accepted it rather than to whoever its routing rule picks now.
1133
- */
1134
- holdsMessage(sessionId: string, idempotencyKey: string): Promise<boolean>;
1135
- /**
1136
- * Start a conversation over under the same id, leaving its transcript so
1137
- * far behind as an archived session.
1138
- *
1139
- * The remedy for a session whose trust label cannot honestly be raised. A
1140
- * session from before provenance was tracked reads `unknown`, the label
1141
- * never rises, and some sessions never end — a Slack DM is one resumable
1142
- * session for the life of the install — so without this an operator's
1143
- * own conversation would write `unknown` forever, however carefully the
1144
- * memory it injects was re-asserted. A session boundary is the only move
1145
- * that neither strands the conversation nor quietly raises trust over a
1146
- * transcript nobody re-read: the old session stays exactly what it was,
1147
- * under `<id>:rolledover:<time>-<suffix>`, and the fresh one starts at the top of
1148
- * the lattice with only the routing metadata the channel needs to keep
1149
- * finding it. The fresh session is still `unknown` on its first turn if
1150
- * the memory it injects is — correctly, and that is the case that
1151
- * catches anyone who thinks the rollover alone is the remedy.
1152
- *
1153
- * Refuses a session with a turn in flight: the runner holds the live
1154
- * object and would save the old transcript back over the fresh row.
1155
- */
1156
- rolloverSession(sessionId: string): Promise<{
1157
- sessionId: string;
1158
- archivedAs: string;
1159
- }>;
1160
- /**
1161
- * Settles a parked call. Returns false when the request is not pending —
1162
- * already decided, expired, or belonging to a turn that was cancelled —
1163
- * which is the normal outcome of a button clicked a minute too late, not
1164
- * an error. Callers surface it to whoever clicked; they must never treat
1165
- * it as "try again".
1166
- */
1167
- resolveApproval(input: ResolveApprovalInput): boolean;
1168
- /** Whether the daemon is taking new work. */
1169
- intake(): IntakeState;
1170
- /**
1171
- * Stop taking new work without stopping. Turns already running or queued
1172
- * finish; a new dispatch is refused with `IntakePausedError` carrying
1173
- * `message` (or `DEFAULT_PAUSED_MESSAGE`); due recurring schedules are
1174
- * skipped and one-shots wait. Channels stay connected, so they can say
1175
- * so. Durable: the pause outlives a restart until `resumeIntake`.
1176
- * Pausing again replaces the message and keeps the original `since`.
1177
- */
1178
- pauseIntake(input?: {
1179
- message?: string;
1180
- }): Promise<IntakeState>;
1181
- /** Take new work again. Idempotent. */
1182
- resumeIntake(): Promise<IntakeState>;
1183
- /** Whether the gateway started held and has not been promoted yet. */
1184
- held(): boolean;
1185
- /**
1186
- * End a held start: connect the remaining channels, start the scheduler,
1187
- * and run the recovery sweeps a normal start runs. Resolves once the
1188
- * channels are up. A no-op on a gateway that is not held. A required
1189
- * channel that fails to start rejects, and the gateway stays held.
1190
- */
1191
- promote(): Promise<void>;
1192
- /**
1193
- * Answers a `credential.requested` with the value an approver entered:
1194
- * stores it add-only (`addNamedCredential`), under the agent or the fleet
1195
- * as the request asked, and adds the name to the requesting agent's soul.
1196
- * The value goes to the credential store and nowhere else — not onto the
1197
- * bus, the log, or the session.
1198
- *
1199
- * Who may answer is the channel's question, as it is for approvals.
1200
- */
1201
- provideCredential(input: ProvideCredentialInput): Promise<ProvideCredentialResult>;
1202
- }
1203
- export interface ProvideCredentialInput {
1204
- requestId: string;
1205
- /**
1206
- * As entered, line breaks included: a key file such as a PEM private key
1207
- * is stored with the ones inside it. Line breaks at either end are
1208
- * dropped, the way `stratus credential set` drops a trailing one.
1209
- */
1210
- value: string;
1211
- /** Who provided it. Channel-native id (a Slack user). */
1212
- actor?: string;
1213
- }
1214
- /**
1215
- * `ok: false` carries a sentence for the person who entered the value: the
1216
- * request is gone, the name is already stored, or the value is empty or a
1217
- * PEM key whose line breaks were lost.
1218
- * `granted: false` on success means the soul already listed the name.
1219
- *
1220
- * `retired: true` means the request is no longer pending and no answer
1221
- * can change that, so a channel should take its form down: the name was
1222
- * stored since (add-only means no retry can land), the soul went to another
1223
- * agent, or the request was already answered. Without it the refusal is
1224
- * one a corrected value can clear, like an empty one, and the form stays.
1225
- */
1226
- export type ProvideCredentialResult = {
1227
- ok: true;
1228
- name: string;
1229
- scope: CredentialScope;
1230
- agentId: string;
1231
- granted: boolean;
1232
- } | {
1233
- ok: false;
1234
- message: string;
1235
- retired?: boolean;
1236
- };
1237
- /**
1238
- * Recorded on a turn the previous process was still running when it died.
1239
- *
1240
- * Exported because "distinguishable" is the whole point: a surface, a
1241
- * test, or an operator reading `lastError` has to be able to tell a turn
1242
- * nobody finished apart from one a provider or a tool actually failed, and
1243
- * matching on prose that lives in one place beats each caller inventing
1244
- * its own guess at the wording.
1245
- */
1246
- export declare const ABANDONED_TURN_ERROR = "stratusd stopped while this turn was still running; it was not resumed. Send the message again.";
1247
- /**
1248
- * The longest `DispatchInput.idempotencyKey` accepted. A platform message id
1249
- * is far shorter; the bound is so a key cannot be used to grow every row it
1250
- * is stored on.
1251
- */
1252
- /**
1253
- * Recorded on a delegated sub-session that was parked on a human when the
1254
- * previous process died.
1255
- *
1256
- * Its parent — the turn whose `agent.delegate` call was awaiting the reply
1257
- * — was running, so the restart settles it as abandoned (above). Recovering
1258
- * the child anyway would re-ask a person to approve a call whose result no
1259
- * turn will ever read, and then run it: a command executed, tokens spent,
1260
- * and a reply delivered to nobody. Distinguishable from the parent's error
1261
- * because the remedy differs — there is no message to send again here;
1262
- * the parent's is the one to repeat.
1263
- */
1264
- /**
1265
- * Session metadata keys the daemon writes for itself, which no external
1266
- * dispatch may supply.
1267
- *
1268
- * Metadata is one bag: the keys a channel attaches to say where a turn is
1269
- * happening sit beside the ones the kernel, the scheduler, the fallback
1270
- * wrapper, and `agent.delegate` write to remember what a session *is* —
1271
- * and the control API forwards a caller's `metadata` into a new session as
1272
- * given. A caller who can write `delegatedBy` can have the restart sweep
1273
- * fail its own parked turn as an orphan; one who can write `fallbackActive`
1274
- * bills its conversation to the fallback model from the first turn. The
1275
- * scheduler already refused to trust its own keys for exactly this reason
1276
- * (see `activeFirings`); the durable ones cannot be re-derived in memory,
1277
- * so they are refused at the door instead.
1278
- */
1279
- /**
1280
- * A rolled-over session: the fresh row records where its predecessor's
1281
- * transcript went, and the archived row records which live id continues
1282
- * the conversation. See `Gateway.rolloverSession`.
1283
- */
1284
- export declare const ROLLED_OVER_FROM_METADATA_KEY = "rolledOverFrom";
1285
- export declare const ROLLED_OVER_TO_METADATA_KEY = "rolledOverTo";
1286
- /** The segment a rollover mints into the archived transcript's id. */
1287
- export declare const ROLLED_OVER_SESSION_ID_MARKER = ":rolledover:";
1288
- /** Where the session's tool calls ran: `local-command`, or a contributed executor's registered name. */
1289
- export declare const EXECUTOR_METADATA_KEY = "executor";
1290
- /**
1291
- * Marks a sub-agent's session (roadmap 24). Reserved: it is what makes the
1292
- * daemon deny the session's gated calls without asking anyone, so a caller
1293
- * that could set it could also clear it.
1294
- */
1295
- export declare const SUB_AGENT_METADATA_KEY = "subAgent";
1296
- export declare const SUB_AGENT_SESSION_ID_MARKER = ":subagent:";
1297
- /** How many sub-agents one parent turn runs at once unless the host says otherwise. */
1298
- export declare const DEFAULT_SUB_AGENTS_PER_TURN = 4;
1299
- export declare const isSubAgentSession: (session: Pick<Session, "id" | "metadata">) => boolean;
1300
- export declare const RESERVED_SESSION_METADATA_KEYS: readonly string[];
1301
- /**
1302
- * The first reserved key `metadata` carries, or undefined when it carries
1303
- * none. Exported so the control API can refuse the request where the
1304
- * caller can see it, with the same rule the gateway applies at dispatch.
1305
- */
1306
- export declare const reservedSessionMetadataKey: (metadata: JsonObject | undefined) => string | undefined;
1307
- export declare const ORPHANED_DELEGATION_ERROR = "stratusd stopped while the turn that delegated this one was still running; the delegation was not resumed. Send the original message again.";
1308
19
  /**
1309
20
  * The always-on Stratus process. One gateway holds the roster, the durable
1310
21
  * session store, the shared event bus, and a pool of runners keyed by
@@ -1312,4 +23,7 @@ export declare const ORPHANED_DELEGATION_ERROR = "stratusd stopped while the tur
1312
23
  * model, and credentials, delegation included.
1313
24
  */
1314
25
  export declare const createGateway: (options?: GatewayOptions) => Gateway;
26
+ export { type ChannelReloadOutcome, DEFAULT_CHANNEL_RELOAD_TIMEOUT_MS, DEFAULT_PAUSED_MESSAGE, DEFAULT_RESTART_DRAIN_TIMEOUT_MS, DEFAULT_STOP_DRAIN_TIMEOUT_MS, GatewayStoppingError, HELD_MESSAGE, IntakePausedError, type IntakeState, MAX_PAUSED_MESSAGE_LENGTH, PauseMessageError, RESTARTING_TURN_ERROR, type RestartOutcome, type RestartRequest, type RestartStatus, RestartUnsupportedError, STOPPING_TURN_ERROR, } from './lifecycle.ts';
27
+ export { type ApprovalRequester, type ApprovalTransport, type DispatchInput, type Gateway, type GatewayApprovalRequest, type GatewayChannelAdapter, type GatewayOptions, type GatewayPluginStatus, type GatewayProviderStatus, type GatewayReviewRequest, type GatewaySkill, type GatewayTool, type ObserveInput, type PendingApproval, type ProvideCredentialInput, type ProvideCredentialResult, type ResolveApprovalInput, type SessionRouting, } from './types.ts';
28
+ export { ABANDONED_TURN_ERROR, DEFAULT_SUB_AGENTS_PER_TURN, EXECUTOR_METADATA_KEY, isSubAgentSession, ORPHANED_DELEGATION_ERROR, RESERVED_SESSION_METADATA_KEYS, reservedSessionMetadataKey, ROLLED_OVER_FROM_METADATA_KEY, ROLLED_OVER_SESSION_ID_MARKER, ROLLED_OVER_TO_METADATA_KEY, SUB_AGENT_METADATA_KEY, SUB_AGENT_SESSION_ID_MARKER, } from './session-keys.ts';
1315
29
  //# sourceMappingURL=index.d.ts.map