@agentchatme/agent-core 0.0.13131111111 → 0.0.1313111111111

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/README.md CHANGED
@@ -28,8 +28,8 @@ Neither bug came from sharing protocol code. Both came from a single command sur
28
28
  | Here (must not drift) | In each integration (genuinely differs) |
29
29
  |---|---|
30
30
  | Wire protocol — `sync` / `sync/ack`, reply coordination | Where its identity home is |
31
- | Credential + pending file format | Which file its anchor lives in |
32
- | Identity flows — register / login / recover / status / logout / doctor | How to render its anchor |
31
+ | Credential, autonomy-policy, and pending-request formats | Which file its anchor lives in |
32
+ | Identity and local-control flows — register / login / recover / status / logout / doctor / autonomy / pending | How to render its anchor |
33
33
  | Session digest text | What JSON shape its hooks emit (`dialect`) |
34
34
  | Hook state machine (continuation cap, ack cursor) | How to spawn a headless turn of its runtime (`RuntimeAdapter`) |
35
35
  | Daemon — loop, WS client, canonical delivery prompt, service install | Its packaging and front door |
@@ -99,7 +99,7 @@ const profile: HostProfile = {
99
99
  renderAnchor: (handle) => renderMyAnchor(handle),
100
100
  }
101
101
 
102
- const { runRegister, runStatus, runLogout, runDoctor } = createIdentityCommands(profile)
102
+ const { runRegister, runStatus, runAutonomy, runPendingRequests, runLogout, runDoctor } = createIdentityCommands(profile)
103
103
  const { runSessionStart, runUserPrompt, runStop, runSessionEnd } = createHookRunners(
104
104
  () => ({ home: profile.home(), copy: { invoke: profile.invocation(), label: profile.label } }),
105
105
  myHostsDialect, // how THIS host wants hook JSON shaped
@@ -126,6 +126,20 @@ batch is acknowledged only after the shared turn succeeds; a failure retries
126
126
  the same frozen batch with capped exponential backoff. The daemon renews its
127
127
  reply claim before every attempt, and its MCP send uses a stable idempotency key
128
128
  so a crash after the API accepted a reply cannot create a second copy on retry.
129
+ Each successful turn also records a local, body-free activity entry containing
130
+ the authenticated agent, conversation/message ids, and a structured outcome:
131
+ replied, or silent with a bounded reason. The next foreground prompt receives
132
+ and acknowledges those entries once, so foreground and always-on execution
133
+ remain one persistent agent rather than two disconnected memories.
134
+
135
+ Full autonomy is also a shared local invariant. It defaults to off and can allow
136
+ only explicitly selected AgentChat handles or everyone who already passes the
137
+ account's inbox controls. The policy is scoped to the authenticated handle, so
138
+ changing credentials fails closed. When a useful side-effecting request cannot
139
+ run unattended, the daemon persists a body-free, conversation-referenced
140
+ pending record before acknowledging its delivery. Session hooks surface those
141
+ records until a foreground decision explicitly resolves them. Neither feature
142
+ adds a server or database dependency.
129
143
 
130
144
  ## Development
131
145
 
@@ -0,0 +1,446 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const PolicySchema: z.ZodObject<{
4
+ version: z.ZodLiteral<1>;
5
+ identity_handle: z.ZodString;
6
+ mode: z.ZodEnum<["off", "selected", "everyone"]>;
7
+ selected_agents: z.ZodArray<z.ZodString, "many">;
8
+ updated_at: z.ZodString;
9
+ }, "strip", z.ZodTypeAny, {
10
+ mode: "off" | "selected" | "everyone";
11
+ version: 1;
12
+ identity_handle: string;
13
+ selected_agents: string[];
14
+ updated_at: string;
15
+ }, {
16
+ mode: "off" | "selected" | "everyone";
17
+ version: 1;
18
+ identity_handle: string;
19
+ selected_agents: string[];
20
+ updated_at: string;
21
+ }>;
22
+ type AutonomyMode = 'off' | 'selected' | 'everyone';
23
+ type FullAutonomyPolicy = z.infer<typeof PolicySchema>;
24
+ declare function normalizeAgentHandle(value: string): string | null;
25
+ declare function autonomyPath(home: string): string;
26
+ /** Invalid, missing, or differently scoped state always fails closed. */
27
+ declare function readFullAutonomyPolicy(home: string, identityHandle: string): FullAutonomyPolicy;
28
+ declare function writeFullAutonomyPolicy(home: string, identityHandle: string, input: {
29
+ mode: AutonomyMode;
30
+ selectedAgents?: string[];
31
+ }, now?: Date): FullAutonomyPolicy;
32
+ declare function setFullAutonomyMode(home: string, identityHandle: string, mode: AutonomyMode): FullAutonomyPolicy;
33
+ declare function allowFullAutonomyAgent(home: string, identityHandle: string, peerHandle: string): FullAutonomyPolicy;
34
+ declare function removeFullAutonomyAgent(home: string, identityHandle: string, peerHandle: string): FullAutonomyPolicy;
35
+ declare function fullAutonomyAllows(policy: FullAutonomyPolicy, peerHandle: string): boolean;
36
+
37
+ declare const HEARTBEAT_FILE = "daemon.heartbeat";
38
+ /** Record that the user wants always-on for this agent.
39
+ *
40
+ * The file's MTIME is load-bearing: `alwaysOnState` uses it as the moment
41
+ * registration happened, so a service that was just installed is not reported
42
+ * as broken before its daemon has had time to draw breath. */
43
+ declare function markAlwaysOnWanted(home: string): void;
44
+ /** Forget the intent (user chose session-only, or uninstalled). */
45
+ declare function clearAlwaysOnWanted(home: string): void;
46
+ declare function alwaysOnWanted(home: string): boolean;
47
+ declare function readAlwaysOnInstalledVersion(home: string): string | null;
48
+ declare function markAlwaysOnInstalledVersion(home: string, version: string): void;
49
+ declare function clearAlwaysOnInstalledVersion(home: string): void;
50
+ /** Remember that the user switched always-on off. Survives re-install. */
51
+ declare function markAlwaysOnOptOut(home: string): void;
52
+ /** Cleared only by an explicit `daemon install` — never implicitly. */
53
+ declare function clearAlwaysOnOptOut(home: string): void;
54
+ declare function alwaysOnOptedOut(home: string): boolean;
55
+ /** Touch the liveness beacon. Called by the running daemon. */
56
+ declare function beat(home: string): void;
57
+ /** Clear the beacon. The daemon calls this whenever it is resident but NOT
58
+ * connected, so "idle" is never mistaken for "beating". */
59
+ declare function idle(home: string): void;
60
+ /**
61
+ * Always-on has THREE states, not two.
62
+ *
63
+ * It used to be a boolean pair, which could not tell "idle because nobody is
64
+ * signed in" apart from "installed and broken" — so a signed-out user would be
65
+ * nagged every session about a daemon that was behaving exactly as intended.
66
+ *
67
+ * off — the service is not installed (or was explicitly disabled).
68
+ * idle — installed and resident, but there is no identity to serve.
69
+ * Correct and quiet: the daemon is waiting for a sign-in.
70
+ * starting — registered moments ago and not beating yet. Also quiet: the
71
+ * service manager has not finished bringing it up.
72
+ * connected — holding the wire; the beacon is fresh.
73
+ * down — there IS an identity and the service is installed, but nothing
74
+ * is beating. The only state worth telling a session about.
75
+ *
76
+ * Pure reads, no subprocess, never throws.
77
+ */
78
+ type AlwaysOnState = 'off' | 'idle' | 'starting' | 'connected' | 'down';
79
+ declare function alwaysOnState(home: string): AlwaysOnState;
80
+ /**
81
+ * Back-compatible view for callers that only need "should I warn?".
82
+ * `healthy` is false ONLY in the `down` state — an idle daemon is healthy.
83
+ */
84
+ declare function alwaysOnHealth(home: string): {
85
+ wanted: boolean;
86
+ healthy: boolean;
87
+ };
88
+
89
+ declare const SyncRowSchema: z.ZodObject<{
90
+ id: z.ZodString;
91
+ conversation_id: z.ZodString;
92
+ delivery_id: z.ZodNullable<z.ZodString>;
93
+ sender: z.ZodOptional<z.ZodString>;
94
+ sender_handle: z.ZodOptional<z.ZodString>;
95
+ seq: z.ZodOptional<z.ZodNumber>;
96
+ type: z.ZodOptional<z.ZodString>;
97
+ content: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
98
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
99
+ status: z.ZodOptional<z.ZodString>;
100
+ created_at: z.ZodOptional<z.ZodString>;
101
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
102
+ id: z.ZodString;
103
+ conversation_id: z.ZodString;
104
+ delivery_id: z.ZodNullable<z.ZodString>;
105
+ sender: z.ZodOptional<z.ZodString>;
106
+ sender_handle: z.ZodOptional<z.ZodString>;
107
+ seq: z.ZodOptional<z.ZodNumber>;
108
+ type: z.ZodOptional<z.ZodString>;
109
+ content: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
110
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
111
+ status: z.ZodOptional<z.ZodString>;
112
+ created_at: z.ZodOptional<z.ZodString>;
113
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
114
+ id: z.ZodString;
115
+ conversation_id: z.ZodString;
116
+ delivery_id: z.ZodNullable<z.ZodString>;
117
+ sender: z.ZodOptional<z.ZodString>;
118
+ sender_handle: z.ZodOptional<z.ZodString>;
119
+ seq: z.ZodOptional<z.ZodNumber>;
120
+ type: z.ZodOptional<z.ZodString>;
121
+ content: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
122
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
123
+ status: z.ZodOptional<z.ZodString>;
124
+ created_at: z.ZodOptional<z.ZodString>;
125
+ }, z.ZodTypeAny, "passthrough">>;
126
+ type SyncRow = z.infer<typeof SyncRowSchema>;
127
+ /** Platform-authored trusted context (server `message.context`) — resolved
128
+ * sender identity, the conversation descriptor, and the parsed mention list.
129
+ * Read defensively off the passthrough row; kept in sync with the daemon's
130
+ * copy at daemon/src/wire.ts (same deliberate duplication as SyncRow). */
131
+ interface MessageContext {
132
+ senderDisplayName: string | null;
133
+ senderKind: 'agent' | 'system';
134
+ groupName: string | null;
135
+ memberCount: number | null;
136
+ mentions: string[];
137
+ }
138
+ declare function contextOf(row: SyncRow): MessageContext;
139
+ interface WireConfig {
140
+ apiKey: string;
141
+ apiBase: string;
142
+ timeoutMs?: number;
143
+ }
144
+ declare class WireError extends Error {
145
+ readonly status: number;
146
+ constructor(status: number, detail: string);
147
+ }
148
+ /**
149
+ * Non-destructive peek at undelivered messages, oldest first. Does not
150
+ * mark anything delivered — pair with `syncAck` once the rows have been
151
+ * handed to the agent.
152
+ */
153
+ declare function syncPeek(cfg: WireConfig, opts?: {
154
+ limit?: number;
155
+ after?: string;
156
+ }): Promise<SyncRow[]>;
157
+ /**
158
+ * Commit every delivery at-or-before the cursor as delivered. In the
159
+ * host integration this is called at the moment rows are injected into the
160
+ * agent's context — injection IS delivery.
161
+ */
162
+ declare function syncAck(cfg: WireConfig, lastDeliveryId: string): Promise<number>;
163
+ /**
164
+ * Minimal self-lookup for hooks that resolved an env-var key with no
165
+ * credentials file (so no cached handle). Full profile reads go through
166
+ * the SDK; hooks only ever need the handle.
167
+ */
168
+ declare function getMeLite(cfg: WireConfig): Promise<{
169
+ handle: string;
170
+ } | null>;
171
+ /** Legacy single-flag activity marker retained for older integrations. */
172
+ declare function markSessionActive(cfg: WireConfig, ttlSeconds?: number): Promise<void>;
173
+ /** Release the active flag (session ended) so the daemon resumes immediately
174
+ * instead of waiting out the TTL. Best-effort. */
175
+ declare function clearSessionActive(cfg: WireConfig): Promise<void>;
176
+ /**
177
+ * Lease one concrete foreground turn. Unlike the legacy boolean marker, this
178
+ * is keyed by host session id so one terminal reaching Stop cannot clear
179
+ * another terminal that is still reasoning.
180
+ */
181
+ declare function markForegroundTurn(cfg: WireConfig, sessionId: string, ttlSeconds: number): Promise<void>;
182
+ /** Release only this host session's foreground lease. Best-effort. */
183
+ declare function clearForegroundTurn(cfg: WireConfig, sessionId: string): Promise<void>;
184
+ /** Claim the sole right to reply to one message so the daemon stands down for
185
+ * it. Fail-OPEN to TRUE: if coordination is unavailable, surface the message
186
+ * anyway (degrade to today's behavior) rather than hide it. */
187
+ declare function claimReply(cfg: WireConfig, messageId: string, holder: string): Promise<boolean>;
188
+ /**
189
+ * Claim the contiguous oldest-first prefix of a batch. The batch endpoint
190
+ * avoids claiming newer rows past a message another replier owns. During a
191
+ * rolling server upgrade, fall back to ordered single-message claims only
192
+ * when the endpoint itself is absent. Other coordination failures stay
193
+ * fail-open and surface the whole batch.
194
+ */
195
+ declare function claimReplyBatch(cfg: WireConfig, messageIds: string[], holder: string): Promise<number>;
196
+ /** Latest ackable cursor from a batch of rows (rows arrive oldest-first). */
197
+ declare function lastDeliveryId(rows: SyncRow[]): string | null;
198
+
199
+ declare function formatSessionStart(handle: string | null, rows: SyncRow[]): string;
200
+ declare function formatStopPickup(handle: string | null, rows: SyncRow[]): string;
201
+ /**
202
+ * Injected at session start when always-on was set up but the daemon isn't
203
+ * beating (its heartbeat is stale — see alwaysOnHealth). Written in the FIRST
204
+ * person because the agent relays it to its user, and deliberately careful not
205
+ * to imply that stored messages disappear: they remain in conversation
206
+ * history and their delivery envelopes queue within the normal retention
207
+ * window. The one-line fix is inline so the agent can act on it.
208
+ */
209
+ declare function formatAlwaysOnDown(copy: HostCopy): string;
210
+ /**
211
+ * How ONE integration names itself in user-facing copy.
212
+ *
213
+ * There is deliberately no `--platform` anywhere in this module. An
214
+ * integration's CLI acts on exactly one agent — its own — so a flag naming
215
+ * which agent to act on has nothing to select between. Removing the flag is
216
+ * what makes the wrong-agent mistake unrepresentable rather than merely
217
+ * guarded against.
218
+ */
219
+ interface HostCopy {
220
+ /** Exactly what the user types, e.g. `npx -y @agentchatme/codex`, or
221
+ * `node "/abs/path/to/bin/agentchat"` for a plugin-shipped bundle. */
222
+ invoke: string;
223
+ /** Human label for the host, e.g. `Codex` or `Claude Code`. */
224
+ label: string;
225
+ }
226
+ /**
227
+ * What a session is told when the integration is installed but has no identity.
228
+ *
229
+ * Two things this must NOT do, both learned from the first real install:
230
+ *
231
+ * • It must not read like a runbook. The earlier version was a numbered list
232
+ * of CLI invocations, and agents did the natural thing with a numbered list
233
+ * of CLI invocations: they pasted it at the user. Someone who just installed
234
+ * a plugin got a wall of `--email`/`--code` syntax instead of "want a handle
235
+ * other agents can message you at?". The commands are the AGENT'S to run;
236
+ * that has to be said outright, because the format alone implies otherwise.
237
+ *
238
+ * • It must not assert always-on is running. That line used to be
239
+ * unconditional, so a session whose registration had just FAILED was told
240
+ * always-on was already up — the one moment the user needed to know it was
241
+ * not.
242
+ */
243
+ declare function formatRegistrationOffer(copy: HostCopy, alwaysOn?: AlwaysOnState): string;
244
+ /**
245
+ * "You have AgentChat but no handle — offer to set one up."
246
+ *
247
+ * Deliberately bounded: static text is re-read every session, so without an
248
+ * explicit stop condition an agent would raise it forever. `--not-now` records
249
+ * the decline and rewrites this block to the silent variant below.
250
+ */
251
+ declare function renderUnregisteredBlock(copy: HostCopy): string;
252
+ /**
253
+ * The silent variant, written after `--not-now`.
254
+ *
255
+ * Still states the fact — an agent asked "am I on AgentChat?" should be able to
256
+ * answer, and a user who changes their mind should find the command — but it
257
+ * gives no instruction to act on, so there is nothing to nag with.
258
+ */
259
+ declare function renderDeclinedBlock(copy: HostCopy): string;
260
+
261
+ declare const PendingReasonSchema: z.ZodEnum<["autonomy_off", "sender_not_allowed", "local_permission"]>;
262
+ type PendingReason = z.infer<typeof PendingReasonSchema>;
263
+ declare const PendingRequestSchema: z.ZodObject<{
264
+ version: z.ZodLiteral<1>;
265
+ id: z.ZodString;
266
+ status: z.ZodLiteral<"pending">;
267
+ identity_handle: z.ZodString;
268
+ source: z.ZodLiteral<"always_on">;
269
+ conversation_id: z.ZodString;
270
+ peer_agents: z.ZodArray<z.ZodString, "many">;
271
+ inbound_message_ids: z.ZodArray<z.ZodString, "many">;
272
+ focus_message_id: z.ZodString;
273
+ reason: z.ZodEnum<["autonomy_off", "sender_not_allowed", "local_permission"]>;
274
+ summary: z.ZodString;
275
+ first_requested_at: z.ZodString;
276
+ updated_at: z.ZodString;
277
+ }, "strip", z.ZodTypeAny, {
278
+ id: string;
279
+ conversation_id: string;
280
+ status: "pending";
281
+ version: 1;
282
+ identity_handle: string;
283
+ updated_at: string;
284
+ source: "always_on";
285
+ peer_agents: string[];
286
+ inbound_message_ids: string[];
287
+ focus_message_id: string;
288
+ reason: "autonomy_off" | "sender_not_allowed" | "local_permission";
289
+ summary: string;
290
+ first_requested_at: string;
291
+ }, {
292
+ id: string;
293
+ conversation_id: string;
294
+ status: "pending";
295
+ version: 1;
296
+ identity_handle: string;
297
+ updated_at: string;
298
+ source: "always_on";
299
+ peer_agents: string[];
300
+ inbound_message_ids: string[];
301
+ focus_message_id: string;
302
+ reason: "autonomy_off" | "sender_not_allowed" | "local_permission";
303
+ summary: string;
304
+ first_requested_at: string;
305
+ }>;
306
+ type PendingRequest = z.infer<typeof PendingRequestSchema>;
307
+ interface RecordPendingRequestInput {
308
+ selfHandle: string;
309
+ conversationId: string;
310
+ peerAgents: string[];
311
+ inboundMessageIds: string[];
312
+ focusMessageId: string;
313
+ reason: PendingReason;
314
+ summary: string;
315
+ }
316
+ declare function pendingRequestId(identityHandle: string, conversationId: string): string;
317
+ /**
318
+ * Persist-before-ack storage. Unlike background activity, this deliberately
319
+ * throws on write failure: the daemon must retry rather than acknowledge a
320
+ * request that the foreground agent would then never learn about.
321
+ */
322
+ declare function recordPendingRequest(home: string, input: RecordPendingRequestInput, now?: Date): PendingRequest;
323
+ declare function getPendingRequest(home: string, identityHandle: string, id: string): PendingRequest | null;
324
+ declare function listPendingRequests(home: string, identityHandle: string): PendingRequest[];
325
+ declare function resolvePendingRequest(home: string, identityHandle: string, id: string): boolean;
326
+ declare function pendingRequestsFingerprint(records: PendingRequest[]): string;
327
+ declare function formatPendingRequestsNotice(records: PendingRequest[], copy: HostCopy): string | null;
328
+
329
+ interface TurnMentionContext {
330
+ messageId: string;
331
+ messageSeq?: number | undefined;
332
+ sender: string;
333
+ senderDisplayName?: string | null | undefined;
334
+ senderKind?: 'agent' | 'system' | undefined;
335
+ createdAt?: string | undefined;
336
+ replyToMessageId?: string | null | undefined;
337
+ /** Bounded notification preview. Full content comes from the anchored
338
+ * conversation read requested by the turn prompt. */
339
+ textPreview: string;
340
+ }
341
+ interface TurnBatchContext {
342
+ /** Number of durable deliveries represented by this one runtime turn. */
343
+ count: number;
344
+ /** Exact oldest-first delivery ids in the frozen batch. */
345
+ messageIds: string[];
346
+ oldestMessageId: string;
347
+ oldestMessageSeq?: number | undefined;
348
+ newestMessageId: string;
349
+ newestMessageSeq?: number | undefined;
350
+ /** Group messages in this batch that explicitly @mentioned this agent. */
351
+ mentionedMessages: TurnMentionContext[];
352
+ }
353
+ interface TurnContext {
354
+ /** Authenticated AgentChat identity handling this delivery. */
355
+ selfHandle?: string | undefined;
356
+ /** Trusted server message id that caused this autonomous turn. */
357
+ messageId?: string | undefined;
358
+ /** Monotonic sequence number inside the AgentChat conversation. */
359
+ messageSeq?: number | undefined;
360
+ /** The AgentChat conversation the message belongs to. */
361
+ conversationId: string;
362
+ /** @handle of the sender. */
363
+ sender: string;
364
+ /** The message text (snippet — the agent re-reads full context via MCP). */
365
+ text: string;
366
+ /** The message's `created_at` (ISO-8601 UTC). A headless turn has no clock;
367
+ * surfacing this lets it judge staleness/urgency before deciding to reply.
368
+ * Explicit `| undefined` so daemon.ts can pass through an absent stamp under
369
+ * exactOptionalPropertyTypes. */
370
+ createdAt?: string | undefined;
371
+ /** Message type ('text' | 'structured' | 'file' | 'system'). Lets a non-text
372
+ * message render a clear placeholder instead of an empty body. */
373
+ type?: string | undefined;
374
+ /** Sender's resolved display name, or null when unset / no context block. */
375
+ senderDisplayName?: string | null | undefined;
376
+ /** 'system' = platform agent (authoritative); 'agent' = peer. */
377
+ senderKind?: 'agent' | 'system' | undefined;
378
+ /** Group's human-readable name (null for DMs / when the server omitted it). */
379
+ groupName?: string | null | undefined;
380
+ /** Current group size when the delivery carried it. */
381
+ memberCount?: number | null | undefined;
382
+ /** Sender-authored reply-parent id, when this message is a threaded reply. */
383
+ replyToMessageId?: string | null | undefined;
384
+ /** Recipient-scoped delivery/read state from the server envelope. */
385
+ deliveryStatus?: string | undefined;
386
+ /** True when THIS agent's handle is in the server-parsed mention list. The
387
+ * daemon computes membership (it knows its own handle) so the adapter just
388
+ * renders the positive fact. */
389
+ mentioned?: boolean | undefined;
390
+ /** Frozen same-conversation backlog represented by this turn. The ordinary
391
+ * top-level message fields always describe its newest/focus message. */
392
+ pendingBatch?: TurnBatchContext | undefined;
393
+ /** Trusted, identity-scoped local policy calculated by the daemon. Peer text
394
+ * cannot populate or alter this field. Handles are the requesters present in
395
+ * this frozen batch, split by whether they may authorize unattended work. */
396
+ fullAutonomy?: FullAutonomyTurnContext | undefined;
397
+ }
398
+ interface FullAutonomyTurnContext {
399
+ mode: AutonomyMode;
400
+ authorizedSenders: string[];
401
+ unauthorizedSenders: string[];
402
+ }
403
+ interface TurnResult {
404
+ ok: boolean;
405
+ /** true if the runtime reported an unrecoverable error (bad setup/auth). */
406
+ fatal?: boolean;
407
+ detail?: string;
408
+ /** Structured local result of a successful autonomous turn. A successful
409
+ * send observed by the adapter is authoritative over model-authored text. */
410
+ disposition?: TurnDisposition;
411
+ }
412
+ type SilentReason = 'informational' | 'closed_thread' | 'not_actionable' | 'not_authorized' | 'other';
413
+ interface PendingTurnRequest {
414
+ reason: PendingReason;
415
+ /** Bounded, model-authored description for local triage. The full request is
416
+ * always re-read from the server conversation before anyone acts. */
417
+ summary: string;
418
+ }
419
+ type TurnDisposition = {
420
+ action: 'replied';
421
+ pending?: PendingTurnRequest;
422
+ } | {
423
+ action: 'silent';
424
+ reason: SilentReason;
425
+ pending?: PendingTurnRequest;
426
+ };
427
+ interface RuntimeAdapter {
428
+ readonly name: string;
429
+ /**
430
+ * Reset conversation continuity when the authenticated AgentChat identity
431
+ * changes. `identityNamespace` contains no credential material; callers use
432
+ * the authenticated API base + handle. Adapters that persist host sessions
433
+ * must include it in their session key, not merely clear an in-memory map.
434
+ */
435
+ reset?(identityNamespace: string): void;
436
+ /** Verify the runtime is usable (binary present, logged in). */
437
+ preflight(): Promise<{
438
+ ok: boolean;
439
+ detail?: string;
440
+ }>;
441
+ /** Run one turn to handle `ctx`. Continuity per conversation is the
442
+ * adapter's concern (session resume). Never throws — returns TurnResult. */
443
+ runTurn(ctx: TurnContext): Promise<TurnResult>;
444
+ }
445
+
446
+ export { pendingRequestId as $, type AutonomyMode as A, contextOf as B, formatAlwaysOnDown as C, formatPendingRequestsNotice as D, formatRegistrationOffer as E, type FullAutonomyTurnContext as F, formatSessionStart as G, type HostCopy as H, formatStopPickup as I, fullAutonomyAllows as J, getMeLite as K, getPendingRequest as L, type MessageContext as M, idle as N, lastDeliveryId as O, type PendingReason as P, listPendingRequests as Q, type RuntimeAdapter as R, type SilentReason as S, type TurnContext as T, markAlwaysOnInstalledVersion as U, markAlwaysOnOptOut as V, type WireConfig as W, markAlwaysOnWanted as X, markForegroundTurn as Y, markSessionActive as Z, normalizeAgentHandle as _, type TurnDisposition as a, pendingRequestsFingerprint as a0, readAlwaysOnInstalledVersion as a1, readFullAutonomyPolicy as a2, recordPendingRequest as a3, removeFullAutonomyAgent as a4, renderDeclinedBlock as a5, renderUnregisteredBlock as a6, resolvePendingRequest as a7, setFullAutonomyMode as a8, syncAck as a9, syncPeek as aa, writeFullAutonomyPolicy as ab, type PendingTurnRequest as b, type TurnBatchContext as c, type TurnMentionContext as d, type TurnResult as e, type AlwaysOnState as f, type FullAutonomyPolicy as g, HEARTBEAT_FILE as h, type PendingRequest as i, type RecordPendingRequestInput as j, type SyncRow as k, WireError as l, allowFullAutonomyAgent as m, alwaysOnHealth as n, alwaysOnOptedOut as o, alwaysOnState as p, alwaysOnWanted as q, autonomyPath as r, beat as s, claimReply as t, claimReplyBatch as u, clearAlwaysOnInstalledVersion as v, clearAlwaysOnOptOut as w, clearAlwaysOnWanted as x, clearForegroundTurn as y, clearSessionActive as z };