@prismer/runtime 2.0.4 → 2.0.6

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.cts CHANGED
@@ -1,9 +1,411 @@
1
1
  import { z } from 'zod';
2
- import { EventEmitter } from 'node:events';
3
2
  import Database from 'better-sqlite3';
3
+ import { EventEmitter } from 'node:events';
4
4
  import { IncomingMessage, ServerResponse } from 'node:http';
5
5
  import { Command } from 'commander';
6
6
 
7
+ type IMAgentStatus = 'online' | 'busy' | 'idle' | 'offline';
8
+ interface HostedAgentDeclaration {
9
+ imUserId: string;
10
+ name: string;
11
+ adapterName: string;
12
+ capabilities: string[];
13
+ profiles: Array<{
14
+ id: string;
15
+ version: number;
16
+ }>;
17
+ }
18
+ interface AgentHostDeclarePayload {
19
+ daemonId: string;
20
+ daemonVersion: string;
21
+ platform: 'darwin' | 'linux' | 'win32';
22
+ agents: HostedAgentDeclaration[];
23
+ }
24
+ interface RejectedHostedAgent {
25
+ imUserId: string;
26
+ reason: 'bound-to-other-daemon' | 'not-owned' | 'unknown';
27
+ ownerDaemonId?: string;
28
+ }
29
+ interface HostAckedPayload {
30
+ workspaceId: string;
31
+ syncCursor: {
32
+ workspaces: number;
33
+ agent_profiles: number;
34
+ [key: string]: number;
35
+ };
36
+ profilesToSync: string[];
37
+ /** Profile IDs the daemon declared but that no longer exist on the cloud
38
+ * (soft-deleted). The daemon should remove these from its local store. */
39
+ profilesToDelete: string[];
40
+ acceptedAgents?: string[];
41
+ rejectedAgents?: RejectedHostedAgent[];
42
+ }
43
+ interface AgentStatusChangedPayload {
44
+ agentImUserId: string;
45
+ status: IMAgentStatus;
46
+ activeProfileId?: string;
47
+ runningTaskIds?: string[];
48
+ }
49
+ interface TaskDispatchContextEntry {
50
+ sender: string;
51
+ senderRole: 'human' | 'agent' | 'admin' | 'system';
52
+ content: string;
53
+ createdAt: string;
54
+ /** Wave-8 W1: assets the human attached to THIS chat message. */
55
+ attachedAssetIds?: string[];
56
+ }
57
+ /** Wave-8 W1: hydrated asset reference attached to a dispatch. */
58
+ interface AssetRef {
59
+ assetId: string;
60
+ contentHash: string;
61
+ mime: string | null;
62
+ sizeBytes: number | null;
63
+ kind: string;
64
+ workspaceId: string;
65
+ role: 'attachment' | 'context';
66
+ /**
67
+ * 14b rev.3 §3.0.4 — cloud-hosted URL for this asset, when present.
68
+ * Adapters prefer this for image/file blocks (cheaper than base64) but
69
+ * fall back to `base64` when the URL is not reachable by the upstream
70
+ * LLM provider (D35 smart probing).
71
+ */
72
+ cdnUrl?: string;
73
+ /**
74
+ * Optional human-readable filename. Surfaced to adapters that want to
75
+ * label `input_file` blocks (OpenClaw `/v1/responses`).
76
+ */
77
+ filename?: string;
78
+ }
79
+ /**
80
+ * 14b rev.3 §3.0.4 / §9 P3 — what `resolveAssetRefs` hands to an adapter for
81
+ * multimodal-aware dispatch. Extends AssetRef with:
82
+ * - `localPath` — daemon's local cache copy (still surfaced so file-based
83
+ * tools in legacy adapters keep working);
84
+ * - `base64` — populated when daemon decided cdnUrl was unreachable and
85
+ * inlined the bytes (D35 fallback);
86
+ * - `reachable` — cdnUrl reachability probe result for adapter introspection.
87
+ */
88
+ interface ResolvedAssetRef extends AssetRef {
89
+ localPath?: string;
90
+ base64?: string;
91
+ reachable?: 'cdn' | 'base64' | 'unknown';
92
+ }
93
+ interface TaskDispatchRequestPayload {
94
+ taskId: string;
95
+ /** Agent target for runtimeRoute='agent'. Shell dispatches do not use this. */
96
+ agentImUserId?: string;
97
+ /** Runtime/device target for runtimeRoute='shell'. */
98
+ targetDaemonId?: string;
99
+ profileId: string;
100
+ capability: string;
101
+ prompt: string;
102
+ /** Execution surface. `shell` is daemon-local command execution. */
103
+ runtimeRoute?: 'agent' | 'sandbox' | 'shell';
104
+ metadata?: Record<string, unknown>;
105
+ timeoutMs?: number;
106
+ context?: TaskDispatchContextEntry[];
107
+ conversationId?: string;
108
+ /**
109
+ * Channel mode for the originating conversation. Optional and
110
+ * forward-compatible: when missing, daemon renders 'unknown' in the
111
+ * [Channel context] prompt block. See appendChannelContext in
112
+ * daemon/dispatch.ts.
113
+ * - `direct`: 1:1 DM (no @-mention needed in reply).
114
+ * - `group`: multi-party room (end reply with `@<recipient>` to
115
+ * continue the chain).
116
+ */
117
+ conversationType?: 'direct' | 'group';
118
+ /**
119
+ * Active participants of the dispatch's conversation. Daemon injects this
120
+ * into [Channel context] so the agent knows the authoritative recipient list
121
+ * without hallucinating or having to call `prismer.conversation.listAgents`. Capped at 50
122
+ * entries server-side.
123
+ */
124
+ participants?: Array<{
125
+ imUserId: string;
126
+ username: string;
127
+ displayName: string;
128
+ role: string;
129
+ agentType?: string | null;
130
+ }>;
131
+ /** Wave-8 W1: assets cloud wants daemon to fold into agent context. */
132
+ assetRefs?: AssetRef[];
133
+ }
134
+ interface TaskDispatchProgressPayload {
135
+ taskId: string;
136
+ progress: number;
137
+ message?: string;
138
+ detail?: Record<string, unknown>;
139
+ }
140
+ /** Wave-8 W1: how the daemon handled a single AssetRef. */
141
+ type AssetDispatchStrategy = 'inline-text' | 'inline-text-truncated' | 'uri-only' | 'fetched-https' | 'error';
142
+ interface AssetDispatchObservation {
143
+ /** AssetRef.assetId for cloud-attached refs; undefined for L5 https fetches. */
144
+ assetId?: string;
145
+ contentHash: string;
146
+ mime: string | null;
147
+ sizeBytes: number | null;
148
+ strategy: AssetDispatchStrategy;
149
+ inlinedBytes?: number;
150
+ error?: string;
151
+ /** L5: original URL the user wrote in the prompt. */
152
+ originalUrl?: string;
153
+ /** L5: post-redirect URL the body was actually downloaded from. */
154
+ finalUrl?: string;
155
+ /** L5: end-to-end fetch duration in ms (DNS + connect + body read). */
156
+ durationMs?: number;
157
+ }
158
+ interface TaskDispatchReplyPayload {
159
+ taskId: string;
160
+ ok: boolean;
161
+ output?: string;
162
+ error?: {
163
+ code: string;
164
+ message: string;
165
+ };
166
+ assetIds?: string[];
167
+ metrics?: {
168
+ tokensUsed?: number;
169
+ durationMs?: number;
170
+ };
171
+ /** Wave-8 W1: per-asset handling report. */
172
+ assetObservability?: AssetDispatchObservation[];
173
+ }
174
+ interface TaskCancelPayload {
175
+ taskId: string;
176
+ reason?: string;
177
+ }
178
+ interface IMWSMessage<T = unknown> {
179
+ type: string;
180
+ payload: T;
181
+ requestId?: string;
182
+ timestamp: number;
183
+ }
184
+ interface WorkspaceChangedPayload {
185
+ workspaceId: string;
186
+ /** ISO-8601 timestamp from im_workspaces.updatedAt. */
187
+ updatedAt: string;
188
+ }
189
+ interface AgentProfileChangedPayload {
190
+ profileId: string;
191
+ version: number;
192
+ }
193
+ interface AgentChangedPayload {
194
+ agentImUserId: string;
195
+ fields: {
196
+ displayName?: string;
197
+ capabilities?: string[];
198
+ };
199
+ }
200
+ interface WorkspaceFileChangedPayload {
201
+ workspaceId: string;
202
+ path: string;
203
+ operation: 'create' | 'update' | 'delete';
204
+ assetId?: string;
205
+ contentHash?: string;
206
+ version: number;
207
+ }
208
+ interface AssetChangedPayload {
209
+ workspaceId: string;
210
+ assetId: string;
211
+ operation: 'create' | 'update' | 'delete';
212
+ contentHash?: string;
213
+ assetIndexSeq?: number;
214
+ revision?: number;
215
+ }
216
+
217
+ type MemoryPageType = 'hub' | 'leaf' | 'decision' | 'glossary' | 'archive';
218
+ type MemoryVisibility = {
219
+ kind: 'workspace';
220
+ } | {
221
+ kind: 'agent';
222
+ imUserId: string;
223
+ } | {
224
+ kind: 'private';
225
+ imUserId: string;
226
+ };
227
+ type ActorKind = 'human' | 'agent';
228
+ type MemorySyncStatus = 'local-only' | 'pending' | 'acked' | 'remote-conflict';
229
+ interface MemoryPage {
230
+ id: string;
231
+ workspaceId: string;
232
+ path: string;
233
+ title: string | null;
234
+ description: string | null;
235
+ contentHash: string;
236
+ version: number;
237
+ pageType: MemoryPageType;
238
+ visibility: MemoryVisibility;
239
+ encrypted: boolean;
240
+ stale: boolean;
241
+ archivedAt: number | null;
242
+ sourceAssetId: string | null;
243
+ sourceRefs: string[];
244
+ syncStatus: MemorySyncStatus;
245
+ createdAt: number;
246
+ updatedAt: number;
247
+ }
248
+ interface MemoryPageContent {
249
+ pageId: string;
250
+ version: number;
251
+ content: string;
252
+ }
253
+ interface MemoryLink {
254
+ sourceUri: string;
255
+ targetUri: string;
256
+ relation: string;
257
+ weight: number;
258
+ extractedFromPageId: string | null;
259
+ }
260
+ interface MemorySearchResult {
261
+ pageId: string;
262
+ path: string;
263
+ title: string | null;
264
+ snippet: string;
265
+ score: number;
266
+ tokenCount: number;
267
+ }
268
+ interface MemorySearchOptions {
269
+ topK?: number;
270
+ relevanceThreshold?: number;
271
+ maxBytes?: number;
272
+ pageType?: MemoryPageType[];
273
+ }
274
+ interface MemoryWriteInput {
275
+ workspaceId: string;
276
+ path: string;
277
+ content: string;
278
+ pageType?: MemoryPageType;
279
+ title?: string;
280
+ description?: string;
281
+ visibility?: MemoryVisibility;
282
+ sourceAssetId?: string;
283
+ sourceRefs?: string[];
284
+ stale?: boolean;
285
+ actorImUserId: string;
286
+ actorKind: ActorKind;
287
+ }
288
+ interface MemoryStats {
289
+ workspaceId: string | null;
290
+ pageCount: number;
291
+ pendingOutbox: number;
292
+ deadLetterCount: number;
293
+ lastSyncAt: number | null;
294
+ dbPath: string;
295
+ }
296
+
297
+ interface MemoryStoreOptions {
298
+ /** Absolute path to the SQLite database file. Parent dir created with 0o700 if absent. */
299
+ dbPath: string;
300
+ /** Workspace this store belongs to. Reject ops referencing other workspaces. */
301
+ workspaceId: string;
302
+ /** Device identifier. Stamped onto version + outbox rows at write time. */
303
+ deviceId: string;
304
+ }
305
+ declare class MemoryStore {
306
+ private readonly opts;
307
+ private db;
308
+ constructor(opts: MemoryStoreOptions);
309
+ open(): void;
310
+ close(): void;
311
+ loadByPath(pagePath: string): MemoryPage | null;
312
+ loadById(pageId: string): MemoryPage | null;
313
+ loadContent(pageId: string, version?: number): MemoryPageContent | null;
314
+ list(options?: {
315
+ pageType?: MemoryPageType;
316
+ limit?: number;
317
+ }): MemoryPage[];
318
+ write(input: MemoryWriteInput): MemoryPage;
319
+ invalidate(pageIds: string[], _reason: string): void;
320
+ upsertLink(link: MemoryLink): void;
321
+ stats(): MemoryStats;
322
+ /**
323
+ * Record sync cursor for incremental sync. Used by cloud-sync.ts to
324
+ * persist the high-water mark for future cursor-based catch-up.
325
+ */
326
+ recordCursor(workspaceId: string, cursor: string): void;
327
+ /**
328
+ * Internal accessor for outbox.ts — outbox writes its own table within the
329
+ * same DB. Returning the live Database handle keeps outbox transactions
330
+ * shareable with store transactions if ever needed.
331
+ */
332
+ rawDb(): Database.Database;
333
+ /** Workspace this store is bound to (read-only). */
334
+ workspaceId(): string;
335
+ /** Device id stamped onto version + outbox rows. */
336
+ deviceId(): string;
337
+ private requireDb;
338
+ private rowToPage;
339
+ }
340
+
341
+ type MemoryScope = 'workspace-shared' | 'agent-private';
342
+ interface ScopedMemoryStoreOptions {
343
+ /** Filesystem root, e.g. `~/.prismer/memory` (will create per-workspace dir under). */
344
+ rootDir: string;
345
+ /** Workspace identifier — used both as the cloud workspaceId and the dir slug. */
346
+ workspaceId: string;
347
+ /** Optional human-friendly slug for the dir; defaults to workspaceId. */
348
+ workspaceSlug?: string;
349
+ /** Device identifier, stamped onto outbox + version rows. */
350
+ deviceId: string;
351
+ }
352
+ interface ScopedWriteInput extends Omit<MemoryWriteInput, 'workspaceId'> {
353
+ scope: MemoryScope;
354
+ /** Required when scope='agent-private'; rejected when scope='workspace-shared'. */
355
+ agentImUserId?: string;
356
+ }
357
+ interface ScopedSearchInput {
358
+ query: string;
359
+ scope: MemoryScope;
360
+ /** Required when scope='agent-private' — search only this agent's bucket. */
361
+ agentImUserId?: string;
362
+ options?: MemorySearchOptions;
363
+ }
364
+ /**
365
+ * Per-workspace, per-bucket SQLite store with the same lifecycle semantics
366
+ * as the existing single-store MemoryStore class.
367
+ */
368
+ declare class ScopedMemoryStore {
369
+ private readonly opts;
370
+ private sharedStore;
371
+ private agentStores;
372
+ private readonly workspaceDir;
373
+ constructor(opts: ScopedMemoryStoreOptions);
374
+ /** Resolve the shared store (lazy open). */
375
+ shared(): MemoryStore;
376
+ /** Resolve the per-agent store (lazy open). */
377
+ forAgent(agentImUserId: string): MemoryStore;
378
+ /** Resolve the right store for (scope, agentImUserId). */
379
+ resolve(scope: MemoryScope, agentImUserId?: string): MemoryStore;
380
+ /** Write a memory page into the correct bucket. */
381
+ write(input: ScopedWriteInput): {
382
+ pageId: string;
383
+ scope: MemoryScope;
384
+ agentImUserId?: string;
385
+ };
386
+ /**
387
+ * Search only the requested bucket. This is the daemon-first FTS5 path
388
+ * referenced by FF_MEMORY_SEARCH_DAEMON_FIRST.
389
+ */
390
+ search(input: ScopedSearchInput): MemorySearchResult[];
391
+ /**
392
+ * Search across BOTH the shared bucket and the requesting agent's private
393
+ * bucket, merging results by descending score. Used by the recall-injector
394
+ * so the agent's prompt sees both shared knowledge and its own private notes.
395
+ */
396
+ searchForAgent(query: string, agentImUserId: string, options?: MemorySearchOptions): MemorySearchResult[];
397
+ /** List which agent ids currently have a private DB on disk (for diagnostics). */
398
+ listAgentBuckets(): string[];
399
+ /** Total disk footprint (best-effort; sums each bucket's main DB file). */
400
+ diskBytes(): {
401
+ shared: number;
402
+ perAgent: Record<string, number>;
403
+ total: number;
404
+ };
405
+ /** Close all underlying stores. */
406
+ close(): void;
407
+ }
408
+
7
409
  type AdapterKind = 'long-running' | 'interactive';
8
410
  /**
9
411
  * Adapter definition: how the daemon hosts one class of agent
@@ -70,6 +472,17 @@ interface AgentProfile {
70
472
  */
71
473
  agentUsername?: string;
72
474
  }
475
+ interface TaskHeartbeatHandle {
476
+ setPhase(phase: string): void;
477
+ touchStep(): void;
478
+ }
479
+ interface StepRecorderHandle {
480
+ recordPhaseChange(phase: string): void;
481
+ recordToolCall(toolName: string, input: unknown, toolCallId?: string): void;
482
+ recordToolResult(toolCallId: string, output: unknown): void;
483
+ recordReasoningChunk(text: string): void;
484
+ recordError(message: string, payload?: Record<string, unknown>): void;
485
+ }
73
486
  interface TaskInput {
74
487
  taskId: string;
75
488
  prompt: string;
@@ -81,6 +494,59 @@ interface TaskInput {
81
494
  detail?: Record<string, unknown>;
82
495
  }) => void;
83
496
  signal?: AbortSignal;
497
+ /**
498
+ * Wave-3 D2 observability handles. Optional — pre-Wave-3 adapters that
499
+ * don't read these still function (heartbeat keeps ticking with the
500
+ * adapter's last-set phase; no step timeline is emitted).
501
+ *
502
+ * `heartbeat`:
503
+ * - Adapter calls `heartbeat.setPhase('thinking' | 'tool_use' | ...)`
504
+ * when the lifecycle advances. The 15s timer is owned by dispatch.ts;
505
+ * adapters MUST NOT start/stop it themselves.
506
+ * - Adapter may call `heartbeat.touchStep()` around observable boundaries
507
+ * (tool call, token emission) so cloud's 45s "stuck" reaper can
508
+ * distinguish slow-but-alive from truly silent.
509
+ *
510
+ * `recorder`:
511
+ * - Per-task run step uploader. Adapter calls `recordToolCall`,
512
+ * `recordToolResult`, `recordReasoningChunk`, `recordPhaseChange`,
513
+ * `recordError` at the relevant moments. Implementation is throttled
514
+ * for reasoning_chunk (500ms batched).
515
+ * - Per §3.0.2 Gap C-⑤, short skill calls (<10s) may rely solely on
516
+ * the default 'tool_use' phase; long-running ones SHOULD self-report.
517
+ *
518
+ * See sdk/prismer-cloud/runtime/src/daemon/task-heartbeat.ts and
519
+ * sdk/prismer-cloud/runtime/src/daemon/step-recorder.ts.
520
+ */
521
+ heartbeat?: TaskHeartbeatHandle;
522
+ recorder?: StepRecorderHandle;
523
+ /**
524
+ * 14b rev.3 §3.0.4 / §9 P3 — assets the daemon has resolved (cdnUrl
525
+ * reachability probed, base64 prefetched if necessary). Multimodal-aware
526
+ * adapters (Hermes `/v1/chat/completions`, OpenClaw `/v1/responses`) lift
527
+ * image/file blocks into their wire format; text-like inlining is still
528
+ * handled by `dispatch.ts:composePrompt` (it stays in the `prompt` string).
529
+ * Empty/undefined preserves the legacy text-only contract.
530
+ */
531
+ assetRefs?: ResolvedAssetRef[];
532
+ /**
533
+ * v2.0 Wave 4-E6 (doc 14 §4.7 Track-F) — scoped daemon memory store.
534
+ *
535
+ * Optional handle the adapter may use to:
536
+ * - Search this agent's memory buckets (workspace-shared + agent-private)
537
+ * for daemon-first FTS5 lookup (0 round-trips to cloud)
538
+ * - Build a `[Relevant memory]` system block via the recall-injector for
539
+ * prompt enrichment before the LLM call
540
+ *
541
+ * When undefined, the adapter MUST NOT attempt local memory recall (legacy
542
+ * behaviour pre-Wave-4 — cloud `/memory/search` is the only path). When
543
+ * defined, FF_MEMORY_SEARCH_DAEMON_FIRST and operatingPrinciples drive
544
+ * whether the adapter actually injects.
545
+ *
546
+ * Wiring is owned by dispatch.ts (resolves per-agent slot → ScopedMemoryStore
547
+ * → passes here). Adapters never instantiate the store themselves.
548
+ */
549
+ memoryStore?: ScopedMemoryStore;
84
550
  }
85
551
  interface TaskResult {
86
552
  ok: boolean;
@@ -528,7 +994,7 @@ type LocalDb = Database.Database;
528
994
  declare function openLocalDb(path: string): LocalDb;
529
995
  declare function runMigrations(db: LocalDb): void;
530
996
  declare function currentSchemaVersion(db: LocalDb): number;
531
- declare const TARGET_SCHEMA_VERSION = 3;
997
+ declare const TARGET_SCHEMA_VERSION = 4;
532
998
 
533
999
  type SyncResourceType = 'workspace' | 'agent' | 'agent_profile';
534
1000
  type SyncOperation = 'create' | 'update' | 'delete';
@@ -1062,183 +1528,6 @@ declare class ParseClaimController {
1062
1528
  }): HeartbeatHandle;
1063
1529
  }
1064
1530
 
1065
- type IMAgentStatus = 'online' | 'busy' | 'idle' | 'offline';
1066
- interface HostedAgentDeclaration {
1067
- imUserId: string;
1068
- name: string;
1069
- adapterName: string;
1070
- capabilities: string[];
1071
- profiles: Array<{
1072
- id: string;
1073
- version: number;
1074
- }>;
1075
- }
1076
- interface AgentHostDeclarePayload {
1077
- daemonId: string;
1078
- daemonVersion: string;
1079
- platform: 'darwin' | 'linux' | 'win32';
1080
- agents: HostedAgentDeclaration[];
1081
- }
1082
- interface HostAckedPayload {
1083
- workspaceId: string;
1084
- syncCursor: {
1085
- workspaces: number;
1086
- agent_profiles: number;
1087
- [key: string]: number;
1088
- };
1089
- profilesToSync: string[];
1090
- /** Profile IDs the daemon declared but that no longer exist on the cloud
1091
- * (soft-deleted). The daemon should remove these from its local store. */
1092
- profilesToDelete: string[];
1093
- }
1094
- interface AgentStatusChangedPayload {
1095
- agentImUserId: string;
1096
- status: IMAgentStatus;
1097
- activeProfileId?: string;
1098
- runningTaskIds?: string[];
1099
- }
1100
- interface TaskDispatchContextEntry {
1101
- sender: string;
1102
- senderRole: 'human' | 'agent' | 'admin' | 'system';
1103
- content: string;
1104
- createdAt: string;
1105
- /** Wave-8 W1: assets the human attached to THIS chat message. */
1106
- attachedAssetIds?: string[];
1107
- }
1108
- /** Wave-8 W1: hydrated asset reference attached to a dispatch. */
1109
- interface AssetRef {
1110
- assetId: string;
1111
- contentHash: string;
1112
- mime: string | null;
1113
- sizeBytes: number | null;
1114
- kind: string;
1115
- workspaceId: string;
1116
- role: 'attachment' | 'context';
1117
- }
1118
- interface TaskDispatchRequestPayload {
1119
- taskId: string;
1120
- /** Agent target for runtimeRoute='agent'. Shell dispatches do not use this. */
1121
- agentImUserId?: string;
1122
- /** Runtime/device target for runtimeRoute='shell'. */
1123
- targetDaemonId?: string;
1124
- profileId: string;
1125
- capability: string;
1126
- prompt: string;
1127
- /** Execution surface. `shell` is daemon-local command execution. */
1128
- runtimeRoute?: 'agent' | 'sandbox' | 'shell';
1129
- metadata?: Record<string, unknown>;
1130
- timeoutMs?: number;
1131
- context?: TaskDispatchContextEntry[];
1132
- conversationId?: string;
1133
- /**
1134
- * Channel mode for the originating conversation. Optional and
1135
- * forward-compatible: when missing, daemon renders 'unknown' in the
1136
- * [Channel context] prompt block. See appendChannelContext in
1137
- * daemon/dispatch.ts.
1138
- * - `direct`: 1:1 DM (no @-mention needed in reply).
1139
- * - `group`: multi-party room (end reply with `@<recipient>` to
1140
- * continue the chain).
1141
- */
1142
- conversationType?: 'direct' | 'group';
1143
- /**
1144
- * Active participants of the dispatch's conversation. Daemon injects this
1145
- * into [Channel context] so the agent knows the authoritative recipient list
1146
- * without hallucinating or having to call `prismer.conversation.listAgents`. Capped at 50
1147
- * entries server-side.
1148
- */
1149
- participants?: Array<{
1150
- imUserId: string;
1151
- username: string;
1152
- displayName: string;
1153
- role: string;
1154
- agentType?: string | null;
1155
- }>;
1156
- /** Wave-8 W1: assets cloud wants daemon to fold into agent context. */
1157
- assetRefs?: AssetRef[];
1158
- }
1159
- interface TaskDispatchProgressPayload {
1160
- taskId: string;
1161
- progress: number;
1162
- message?: string;
1163
- detail?: Record<string, unknown>;
1164
- }
1165
- /** Wave-8 W1: how the daemon handled a single AssetRef. */
1166
- type AssetDispatchStrategy = 'inline-text' | 'inline-text-truncated' | 'uri-only' | 'fetched-https' | 'error';
1167
- interface AssetDispatchObservation {
1168
- /** AssetRef.assetId for cloud-attached refs; undefined for L5 https fetches. */
1169
- assetId?: string;
1170
- contentHash: string;
1171
- mime: string | null;
1172
- sizeBytes: number | null;
1173
- strategy: AssetDispatchStrategy;
1174
- inlinedBytes?: number;
1175
- error?: string;
1176
- /** L5: original URL the user wrote in the prompt. */
1177
- originalUrl?: string;
1178
- /** L5: post-redirect URL the body was actually downloaded from. */
1179
- finalUrl?: string;
1180
- /** L5: end-to-end fetch duration in ms (DNS + connect + body read). */
1181
- durationMs?: number;
1182
- }
1183
- interface TaskDispatchReplyPayload {
1184
- taskId: string;
1185
- ok: boolean;
1186
- output?: string;
1187
- error?: {
1188
- code: string;
1189
- message: string;
1190
- };
1191
- assetIds?: string[];
1192
- metrics?: {
1193
- tokensUsed?: number;
1194
- durationMs?: number;
1195
- };
1196
- /** Wave-8 W1: per-asset handling report. */
1197
- assetObservability?: AssetDispatchObservation[];
1198
- }
1199
- interface TaskCancelPayload {
1200
- taskId: string;
1201
- reason?: string;
1202
- }
1203
- interface IMWSMessage<T = unknown> {
1204
- type: string;
1205
- payload: T;
1206
- requestId?: string;
1207
- timestamp: number;
1208
- }
1209
- interface WorkspaceChangedPayload {
1210
- workspaceId: string;
1211
- /** ISO-8601 timestamp from im_workspaces.updatedAt. */
1212
- updatedAt: string;
1213
- }
1214
- interface AgentProfileChangedPayload {
1215
- profileId: string;
1216
- version: number;
1217
- }
1218
- interface AgentChangedPayload {
1219
- agentImUserId: string;
1220
- fields: {
1221
- displayName?: string;
1222
- capabilities?: string[];
1223
- };
1224
- }
1225
- interface WorkspaceFileChangedPayload {
1226
- workspaceId: string;
1227
- path: string;
1228
- operation: 'create' | 'update' | 'delete';
1229
- assetId?: string;
1230
- contentHash?: string;
1231
- version: number;
1232
- }
1233
- interface AssetChangedPayload {
1234
- workspaceId: string;
1235
- assetId: string;
1236
- operation: 'create' | 'update' | 'delete';
1237
- contentHash?: string;
1238
- assetIndexSeq?: number;
1239
- revision?: number;
1240
- }
1241
-
1242
1531
  type PrismerUriType = 'asset' | 'file';
1243
1532
  interface ParsedPrismerUri {
1244
1533
  /** Original full URI string. */
@@ -1479,6 +1768,29 @@ declare class OutboxWatcher {
1479
1768
  private currentScanDirs;
1480
1769
  private tick;
1481
1770
  private scanDir;
1771
+ /**
1772
+ * F2B — report an OUTBOX_MIME_MISMATCH task event to cloud so the task log
1773
+ * gains an auditable record of the rejection. Without this, the failure is
1774
+ * silent (file quarantined locally, agent unaware) and the next dispatch
1775
+ * loops on the same bad strategy.
1776
+ *
1777
+ * Best-effort: every catch site swallows errors. We never want this
1778
+ * upstream call to block local quarantine or trigger a retry loop, because
1779
+ * the reject decision is already final by the time we get here.
1780
+ *
1781
+ * Endpoint contract: POST /api/im/tasks/:id/event
1782
+ * { code: 'OUTBOX_MIME_MISMATCH', payload: { file, daemonError }, message }
1783
+ * Only callable when `task?.taskId` is known — pre-dispatch outbox files
1784
+ * (no active task) are quarantined silently as before.
1785
+ */
1786
+ private reportMimeMismatchToCloud;
1787
+ /**
1788
+ * Move a file under `<dir>/_rejected/` so it stops being a scan candidate
1789
+ * (the loop skips the reserved subdir) and operators can find the
1790
+ * offending artifact. Filename collision is rare per task; on collision
1791
+ * we append a millisecond suffix to keep the original observable.
1792
+ */
1793
+ private quarantine;
1482
1794
  private upload;
1483
1795
  }
1484
1796
 
@@ -1576,6 +1888,15 @@ interface DispatchDeps {
1576
1888
  * Keyed by workspaceId. If absent, #ref resolution is skipped entirely.
1577
1889
  */
1578
1890
  assetMetadataIndexes?: Map<string, AssetMetadataIndex>;
1891
+ /**
1892
+ * 14b D34=C — optional override for image vision-fallback. Enabled by
1893
+ * default when image assetRefs are present.
1894
+ */
1895
+ visionAux?: {
1896
+ enabled?: boolean;
1897
+ cacheDir?: string;
1898
+ timeoutMs?: number;
1899
+ };
1579
1900
  }
1580
1901
  declare function handleDispatch(payload: TaskDispatchRequestPayload, requestId: string | undefined, deps: DispatchDeps): Promise<TaskDispatchReplyPayload>;
1581
1902
  /**
@@ -1907,6 +2228,14 @@ declare class LocalServer {
1907
2228
  * to `/api/sandboxes/:id/snapshot/manifest`.
1908
2229
  */
1909
2230
  private handleSnapshot;
2231
+ /**
2232
+ * POST /v1/agents/:agentId/dump-state — agent-scoped daemon manifest.
2233
+ *
2234
+ * This is narrower than `/v1/snapshot`: it walks only the current agent's
2235
+ * profile/work dirs so cloud can attach the result to IMAgentSnapshot without
2236
+ * capturing unrelated agents hosted in the same daemon/container.
2237
+ */
2238
+ private handleAgentDumpState;
1910
2239
  /**
1911
2240
  * POST /local/asset/write — agent-gen adapter RPC.
1912
2241
  *
@@ -1985,12 +2314,15 @@ declare class Runner extends EventEmitter {
1985
2314
  private workspaceId;
1986
2315
  private wsConnected;
1987
2316
  private readonly hostedAgents;
2317
+ private readonly rejectedHostedAgentIds;
1988
2318
  private readonly runningTasks;
1989
2319
  private lastTaskError?;
1990
2320
  private heartbeatTimer?;
1991
2321
  private taskReaperTimer?;
1992
2322
  private skillResyncTimer?;
1993
2323
  private skillSyncInFlight;
2324
+ private pendingReplyCache;
2325
+ private pendingReplyRecoveryDone;
1994
2326
  constructor(opts?: RunnerOptions);
1995
2327
  start(): Promise<void>;
1996
2328
  /**
@@ -2076,11 +2408,40 @@ declare class Runner extends EventEmitter {
2076
2408
  private wireWsHandlers;
2077
2409
  private sendDeclare;
2078
2410
  private handleIncoming;
2411
+ /**
2412
+ * v2.0 §4.8.1 — webhook dispatch over WS reverse channel.
2413
+ *
2414
+ * Acks via `webhook.dispatch.reply { _rpcId, ok, acceptedAt }` immediately
2415
+ * after `handleAgentMessageDispatch()` returns its synchronous response
2416
+ * — the actual reply (the agent's reply text/attachments) still flows
2417
+ * through `postMessageDispatchReply()` which posts to
2418
+ * `/api/im/dispatch/reply` after the adapter finishes. That mirrors the
2419
+ * HTTP /dispatch contract: ack first, asynchronous final reply via the
2420
+ * dispatch-reply REST endpoint.
2421
+ *
2422
+ * The `_rpcId` field is the only addition versus the HTTP path; we strip
2423
+ * it before passing the payload to the dispatch handler so existing
2424
+ * validation logic doesn't trip on an unknown field.
2425
+ */
2426
+ private onWebhookDispatch;
2079
2427
  private onHostAcked;
2428
+ private applyHostAckOwnershipLock;
2080
2429
  private onTaskDispatch;
2081
2430
  private onTaskCancel;
2082
2431
  private findMessageDispatchAgent;
2083
2432
  private postMessageDispatchReply;
2433
+ /**
2434
+ * v2.0 §4.8.1 (Wave 4-E4) — submit the transport-probe report. Called
2435
+ * from `onHostAcked` so the daemon's container row exists in cloud
2436
+ * before we POST to /runtime/transport-report.
2437
+ *
2438
+ * The gatewayUrl reported here is `http://<local-ipv4>:<localPort>`
2439
+ * (typically `http://192.168.x.x:3210`). For a daemon without a local
2440
+ * HTTP server (startLocalServer=false in tests) we still post the
2441
+ * probe with `gatewayUrl=null` so cloud knows transport='ws' is the
2442
+ * only path.
2443
+ */
2444
+ private reportTransport;
2084
2445
  private onAgentChanged;
2085
2446
  private onAgentProfileChanged;
2086
2447
  private syncProfileFromCloud;
@@ -2268,17 +2629,17 @@ declare const CodexConfigSchema: z.ZodObject<{
2268
2629
  */
2269
2630
  apiKeyEnv: z.ZodDefault<z.ZodString>;
2270
2631
  }, "strip", z.ZodTypeAny, {
2632
+ sandbox: "read-only" | "workspace-write" | "danger-full-access";
2271
2633
  model: string;
2272
2634
  cwd: string;
2273
- sandbox: "read-only" | "workspace-write" | "danger-full-access";
2274
2635
  apiKeyEnv: string;
2275
2636
  systemPrompt?: string | undefined;
2276
2637
  envVars?: Record<string, string> | undefined;
2277
2638
  }, {
2278
2639
  cwd: string;
2640
+ sandbox?: "read-only" | "workspace-write" | "danger-full-access" | undefined;
2279
2641
  model?: string | undefined;
2280
2642
  systemPrompt?: string | undefined;
2281
- sandbox?: "read-only" | "workspace-write" | "danger-full-access" | undefined;
2282
2643
  envVars?: Record<string, string> | undefined;
2283
2644
  apiKeyEnv?: string | undefined;
2284
2645
  }>;
@@ -2294,4 +2655,4 @@ declare const codexAdapter: AdapterDef;
2294
2655
  declare function buildProgram(): Command;
2295
2656
  declare function runCli(argv?: string[]): Promise<void>;
2296
2657
 
2297
- export { type AcquireResult, type AdapterDef, type AdapterKind, AdapterRegistry, type AdapterService, type AgentChangedPayload, type AgentDispatchReplyAttachment, type AgentDispatchReplyPayload, type AgentDispatchReplyStatus, type AgentDispatchRequest, type AgentDispatchResponse, type AgentHostDeclarePayload, type AgentProfile, type AgentProfileChangedPayload, type AgentStatusChangedPayload, AssetCache, type AssetCacheOptions, type AssetChangedPayload, type AssetDispatchObservation, type AssetDispatchStrategy, type AssetRef, BUILTIN_ROLE_TEMPLATES, type CachedAsset, type ClaudeCodeConfig, CloudClient, type CloudClientOptions, CloudError, type CloudResponse, type CodexConfig, type Config, type ConfigPaths, ConfigSchema, type DispatchDeps, type Envelope, type FlushFn, type FlushResult, type HealthStatus, type HermesProfileConfig, type HostAckedPayload, type HostedAgentDeclaration, type IMAgentStatus, type IMWSMessage, type LocalDb, LocalServer, type LocalServerOptions, type LocalServerState, type MessageDispatchAgent, type MessageDispatchDeps, type MessageDispatchHandle, type MessageDispatchTaskInput, type NormalizedContent, type OpenClawProfileConfig, type PairOptions, type PairResult, type ParseClaim, ParseClaimController, type ParseClaimControllerOptions, type ParsedPrismerUri, type PrismerUriType, type RoleTemplate, Runner, type RunnerOptions, ServicePool, type SyncOperation, SyncQueue, type SyncQueueRow, type SyncResourceType, type SyncStatus, SyncWorker, type SyncWorkerOptions, TARGET_SCHEMA_VERSION, type TaskCancelPayload, type TaskDispatchContextEntry, type TaskDispatchProgressPayload, type TaskDispatchReplyPayload, type TaskDispatchRequestPayload, type TaskInput, type TaskResult, UriResolver, type UriResolverOptions, type UrlResolution, type ValidationResult, WS_CLOSE, type WorkspaceChangedPayload, type WorkspaceFileBinding, type WorkspaceFileChangedPayload, WorkspaceMirror, type WorkspaceMirrorOptions, WsClient, type WsClientOptions, buildProgram, claudeCodeAdapter, codexAdapter, composePrompt, configExists, currentSchemaVersion, deriveWsUrl, envelope, extractHttpUrls, getRoleTemplate, handleAgentMessageDispatch, handleDispatch, hermesAdapter, isDaemonId, listRoleTemplates, loadConfig, newDaemonId, nextBackoffMs, openLocalDb, openclawAdapter, pair, parseCodexOutput, parseUris, resolvePaths, runCli, runMigrations, saveConfig };
2658
+ export { type AcquireResult, type AdapterDef, type AdapterKind, AdapterRegistry, type AdapterService, type AgentChangedPayload, type AgentDispatchReplyAttachment, type AgentDispatchReplyPayload, type AgentDispatchReplyStatus, type AgentDispatchRequest, type AgentDispatchResponse, type AgentHostDeclarePayload, type AgentProfile, type AgentProfileChangedPayload, type AgentStatusChangedPayload, AssetCache, type AssetCacheOptions, type AssetChangedPayload, type AssetDispatchObservation, type AssetDispatchStrategy, type AssetRef, BUILTIN_ROLE_TEMPLATES, type CachedAsset, type ClaudeCodeConfig, CloudClient, type CloudClientOptions, CloudError, type CloudResponse, type CodexConfig, type Config, type ConfigPaths, ConfigSchema, type DispatchDeps, type Envelope, type FlushFn, type FlushResult, type HealthStatus, type HermesProfileConfig, type HostAckedPayload, type HostedAgentDeclaration, type IMAgentStatus, type IMWSMessage, type LocalDb, LocalServer, type LocalServerOptions, type LocalServerState, type MessageDispatchAgent, type MessageDispatchDeps, type MessageDispatchHandle, type MessageDispatchTaskInput, type NormalizedContent, type OpenClawProfileConfig, type PairOptions, type PairResult, type ParseClaim, ParseClaimController, type ParseClaimControllerOptions, type ParsedPrismerUri, type PrismerUriType, type RejectedHostedAgent, type RoleTemplate, Runner, type RunnerOptions, ServicePool, type SyncOperation, SyncQueue, type SyncQueueRow, type SyncResourceType, type SyncStatus, SyncWorker, type SyncWorkerOptions, TARGET_SCHEMA_VERSION, type TaskCancelPayload, type TaskDispatchContextEntry, type TaskDispatchProgressPayload, type TaskDispatchReplyPayload, type TaskDispatchRequestPayload, type TaskInput, type TaskResult, UriResolver, type UriResolverOptions, type UrlResolution, type ValidationResult, WS_CLOSE, type WorkspaceChangedPayload, type WorkspaceFileBinding, type WorkspaceFileChangedPayload, WorkspaceMirror, type WorkspaceMirrorOptions, WsClient, type WsClientOptions, buildProgram, claudeCodeAdapter, codexAdapter, composePrompt, configExists, currentSchemaVersion, deriveWsUrl, envelope, extractHttpUrls, getRoleTemplate, handleAgentMessageDispatch, handleDispatch, hermesAdapter, isDaemonId, listRoleTemplates, loadConfig, newDaemonId, nextBackoffMs, openLocalDb, openclawAdapter, pair, parseCodexOutput, parseUris, resolvePaths, runCli, runMigrations, saveConfig };