@prismer/runtime 1.9.0-hotfix.1
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 +288 -0
- package/dist/adapter-registry-EMZFKFYK.mjs +8 -0
- package/dist/adapter-registry-EMZFKFYK.mjs.map +1 -0
- package/dist/artifacts-uploader-N2HNBSTW.mjs +159 -0
- package/dist/artifacts-uploader-N2HNBSTW.mjs.map +1 -0
- package/dist/auto-register-M4YSPJS6.mjs +204 -0
- package/dist/auto-register-M4YSPJS6.mjs.map +1 -0
- package/dist/bin/prismer.js +14988 -0
- package/dist/chunk-BJTO5JO5.mjs +11 -0
- package/dist/chunk-BJTO5JO5.mjs.map +1 -0
- package/dist/chunk-JIEDFDVI.mjs +148 -0
- package/dist/chunk-JIEDFDVI.mjs.map +1 -0
- package/dist/chunk-NDNX2G6O.mjs +68 -0
- package/dist/chunk-NDNX2G6O.mjs.map +1 -0
- package/dist/chunk-POWV475F.mjs +45 -0
- package/dist/chunk-POWV475F.mjs.map +1 -0
- package/dist/chunk-VTFKZAUY.mjs +68 -0
- package/dist/chunk-VTFKZAUY.mjs.map +1 -0
- package/dist/dispatch-mux-MW4HHICS.mjs +8 -0
- package/dist/dispatch-mux-MW4HHICS.mjs.map +1 -0
- package/dist/dispatch-rpc-J7H6KC2V.mjs +78 -0
- package/dist/dispatch-rpc-J7H6KC2V.mjs.map +1 -0
- package/dist/fs-rpc-OSHJZYK6.mjs +58 -0
- package/dist/fs-rpc-OSHJZYK6.mjs.map +1 -0
- package/dist/heartbeat-loop-H2LAI3V5.mjs +95 -0
- package/dist/heartbeat-loop-H2LAI3V5.mjs.map +1 -0
- package/dist/icon +21 -0
- package/dist/index.d.mts +2063 -0
- package/dist/index.d.ts +2063 -0
- package/dist/index.js +10013 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8807 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mode-b-DXJ7FJAL.mjs +152 -0
- package/dist/mode-b-DXJ7FJAL.mjs.map +1 -0
- package/dist/registry-ZYU2HDFL.mjs +12 -0
- package/dist/registry-ZYU2HDFL.mjs.map +1 -0
- package/dist/smallicon +6 -0
- package/package.json +72 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,2063 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import { FsContext } from '@prismer/sandbox-runtime';
|
|
3
|
+
import { ParaEvent } from '@prismer/wire';
|
|
4
|
+
import { EventEmitter } from 'node:events';
|
|
5
|
+
|
|
6
|
+
type DaemonState = 'starting' | 'running' | 'shutting_down' | 'stopped';
|
|
7
|
+
interface DaemonOptions {
|
|
8
|
+
pidFile?: string;
|
|
9
|
+
dataDir?: string;
|
|
10
|
+
logFile?: string;
|
|
11
|
+
installSignalHandlers?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface ShutdownHandler {
|
|
14
|
+
name: string;
|
|
15
|
+
handler: () => Promise<void> | void;
|
|
16
|
+
}
|
|
17
|
+
declare class DaemonAlreadyRunningError extends Error {
|
|
18
|
+
readonly existingPid: number;
|
|
19
|
+
readonly pidFile: string;
|
|
20
|
+
constructor(pid: number, pidFile: string);
|
|
21
|
+
}
|
|
22
|
+
declare class DaemonProcess {
|
|
23
|
+
private _state;
|
|
24
|
+
private readonly _pidFile;
|
|
25
|
+
private readonly _dataDir;
|
|
26
|
+
private readonly _logFile;
|
|
27
|
+
private readonly _installSignalHandlers;
|
|
28
|
+
private readonly _shutdownHandlers;
|
|
29
|
+
private readonly _signalHistory;
|
|
30
|
+
private _shutdownInProgress;
|
|
31
|
+
private _sigTermHandler;
|
|
32
|
+
private _sigIntHandler;
|
|
33
|
+
private _sigHupHandler;
|
|
34
|
+
private _signalReceived;
|
|
35
|
+
constructor(opts?: DaemonOptions);
|
|
36
|
+
get state(): DaemonState;
|
|
37
|
+
get pid(): number;
|
|
38
|
+
get pidFile(): string;
|
|
39
|
+
get dataDir(): string;
|
|
40
|
+
get signalHistory(): ReadonlyArray<{
|
|
41
|
+
signal: string;
|
|
42
|
+
ts: number;
|
|
43
|
+
}>;
|
|
44
|
+
start(): Promise<void>;
|
|
45
|
+
onShutdown(h: ShutdownHandler): void;
|
|
46
|
+
shutdown(signal?: NodeJS.Signals | 'manual'): Promise<void>;
|
|
47
|
+
reload(): Promise<void>;
|
|
48
|
+
static isRunning(pidFile: string): boolean;
|
|
49
|
+
static cleanupStalePidFile(pidFile: string): boolean;
|
|
50
|
+
private _atomicPidWrite;
|
|
51
|
+
private _recordSignal;
|
|
52
|
+
private _readPidFromFile;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface EventBusEnvelope<T = unknown> {
|
|
56
|
+
topic: string;
|
|
57
|
+
ts: number;
|
|
58
|
+
payload: T;
|
|
59
|
+
source?: string;
|
|
60
|
+
requestId?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Unified subscription handler that accepts EventBusEnvelope.
|
|
64
|
+
* This matches the EventBus API expectation.
|
|
65
|
+
*/
|
|
66
|
+
type SubscriptionHandler<T = unknown> = (ev: EventBusEnvelope<T>) => void | Promise<void>;
|
|
67
|
+
interface Subscription {
|
|
68
|
+
id: string;
|
|
69
|
+
pattern: string;
|
|
70
|
+
unsubscribe(): void;
|
|
71
|
+
}
|
|
72
|
+
interface EventBusOptions {
|
|
73
|
+
onSubscriberError?: (err: unknown, sub: Subscription, ev: EventBusEnvelope) => void;
|
|
74
|
+
queueWarnThreshold?: number;
|
|
75
|
+
}
|
|
76
|
+
declare class EventBus {
|
|
77
|
+
private readonly _subscribers;
|
|
78
|
+
private _monotonicTs;
|
|
79
|
+
private _totalPublished;
|
|
80
|
+
private readonly _onError;
|
|
81
|
+
private readonly _warnThreshold;
|
|
82
|
+
constructor(opts?: EventBusOptions);
|
|
83
|
+
publish<T>(topic: string, payload: T, meta?: {
|
|
84
|
+
source?: string;
|
|
85
|
+
requestId?: string;
|
|
86
|
+
}): void;
|
|
87
|
+
subscribe<T>(topic: string, handler: SubscriptionHandler<T>): Subscription;
|
|
88
|
+
unsubscribeAll(): void;
|
|
89
|
+
get subscriberCount(): number;
|
|
90
|
+
get totalPublished(): number;
|
|
91
|
+
private _drain;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type AgentState = 'registered' | 'spawning' | 'running' | 'degraded' | 'crashed' | 'backoff' | 'stopping' | 'stopped' | 'failed';
|
|
95
|
+
interface AgentDescriptor {
|
|
96
|
+
id: string;
|
|
97
|
+
name: string;
|
|
98
|
+
command: string;
|
|
99
|
+
args?: string[];
|
|
100
|
+
cwd?: string;
|
|
101
|
+
env?: NodeJS.ProcessEnv;
|
|
102
|
+
healthCheck?: () => Promise<boolean> | boolean;
|
|
103
|
+
backoff?: {
|
|
104
|
+
initialMs?: number;
|
|
105
|
+
multiplier?: number;
|
|
106
|
+
maxMs?: number;
|
|
107
|
+
maxRestarts?: number;
|
|
108
|
+
resetAfterMs?: number;
|
|
109
|
+
};
|
|
110
|
+
attachPid?: number;
|
|
111
|
+
}
|
|
112
|
+
interface AgentStatus {
|
|
113
|
+
id: string;
|
|
114
|
+
state: AgentState;
|
|
115
|
+
pid?: number;
|
|
116
|
+
startedAt?: number;
|
|
117
|
+
restarts: number;
|
|
118
|
+
lastExitCode?: number;
|
|
119
|
+
lastSignal?: NodeJS.Signals | null;
|
|
120
|
+
lastHealthError?: string;
|
|
121
|
+
nextRestartAt?: number;
|
|
122
|
+
}
|
|
123
|
+
interface SupervisorOptions {
|
|
124
|
+
eventBus: EventBus;
|
|
125
|
+
healthProbeIntervalMs?: number;
|
|
126
|
+
degradedThreshold?: number;
|
|
127
|
+
stopTimeoutMs?: number;
|
|
128
|
+
}
|
|
129
|
+
declare class AgentSupervisor {
|
|
130
|
+
private readonly _bus;
|
|
131
|
+
private readonly _probeIntervalMs;
|
|
132
|
+
private readonly _degradedThreshold;
|
|
133
|
+
private readonly _stopTimeoutMs;
|
|
134
|
+
private readonly _agents;
|
|
135
|
+
constructor(opts: SupervisorOptions);
|
|
136
|
+
register(descriptor: AgentDescriptor): void;
|
|
137
|
+
unregister(id: string): Promise<void>;
|
|
138
|
+
spawn(id: string): Promise<void>;
|
|
139
|
+
stop(id: string, reason?: string): Promise<void>;
|
|
140
|
+
restart(id: string): Promise<void>;
|
|
141
|
+
attach(id: string, pid: number): Promise<void>;
|
|
142
|
+
get(id: string): AgentStatus | undefined;
|
|
143
|
+
list(): AgentStatus[];
|
|
144
|
+
shutdown(): Promise<void>;
|
|
145
|
+
private _doSpawn;
|
|
146
|
+
private _handleCrash;
|
|
147
|
+
private _scheduleRestart;
|
|
148
|
+
private _killOwned;
|
|
149
|
+
private _cancelBackoff;
|
|
150
|
+
private _startProbe;
|
|
151
|
+
private _stopProbe;
|
|
152
|
+
private _runProbe;
|
|
153
|
+
private _pollAttached;
|
|
154
|
+
private _require;
|
|
155
|
+
private _publish;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Prismer Runtime — Adapter Registry (Sprint A3, D4 dispatch mux).
|
|
160
|
+
*
|
|
161
|
+
* One process-wide registry maps adapter `name` → `AdapterImpl`. The
|
|
162
|
+
* registry is the data store; selection policy lives in DispatchMux
|
|
163
|
+
* (dispatch-mux.ts) so the same registry can drive multiple selectors
|
|
164
|
+
* (capability-based, name-based, weighted load, etc.) without coupling.
|
|
165
|
+
*
|
|
166
|
+
* An adapter advertises:
|
|
167
|
+
* - `name` : stable identifier (matches catalog name).
|
|
168
|
+
* - `tiersSupported` : PARA Tier numbers it can host (e.g. [1..7]).
|
|
169
|
+
* - `capabilityTags` : tags from the task's `requiresCapability`
|
|
170
|
+
* vocabulary that this adapter can satisfy.
|
|
171
|
+
*
|
|
172
|
+
* On register conflict, replace — adapter modules are expected to be
|
|
173
|
+
* the only source of truth for their own descriptor.
|
|
174
|
+
*
|
|
175
|
+
* Adapters typically run as out-of-process workers (Claude Code CLI,
|
|
176
|
+
* OpenClaw daemon plug-in, Hermes Python). The `dispatch` boundary
|
|
177
|
+
* here is the *runtime side* of that bridge — it returns a Promise
|
|
178
|
+
* whose resolution corresponds to the adapter's reported completion.
|
|
179
|
+
*/
|
|
180
|
+
interface AdapterDescriptor {
|
|
181
|
+
/** Stable identifier — must match the catalog entry. */
|
|
182
|
+
name: string;
|
|
183
|
+
/** PARA Tier numbers (L1–L10). */
|
|
184
|
+
tiersSupported: number[];
|
|
185
|
+
/** Capability tag vocabulary (e.g. ["code.write", "code.review"]). */
|
|
186
|
+
capabilityTags: string[];
|
|
187
|
+
/** Free-form for telemetry — version, build, etc. */
|
|
188
|
+
metadata?: Record<string, unknown>;
|
|
189
|
+
}
|
|
190
|
+
interface AdapterDispatchInput {
|
|
191
|
+
/** Cloud task ID (im_tasks.id). */
|
|
192
|
+
taskId: string;
|
|
193
|
+
/** Step index when the task is multi-step (route step). */
|
|
194
|
+
stepIdx?: number;
|
|
195
|
+
/** Capability the cloud asked for — drives adapter selection. */
|
|
196
|
+
capability: string;
|
|
197
|
+
/** User-facing prompt / instruction. */
|
|
198
|
+
prompt: string;
|
|
199
|
+
/** Free-form metadata passed through to the adapter. */
|
|
200
|
+
metadata?: Record<string, unknown>;
|
|
201
|
+
/** Optional deadline (ms epoch). Adapters should respect it best-effort. */
|
|
202
|
+
deadlineAt?: number;
|
|
203
|
+
}
|
|
204
|
+
interface AdapterDispatchResult {
|
|
205
|
+
ok: boolean;
|
|
206
|
+
/** Output text (stdout, summary, etc.). */
|
|
207
|
+
output?: string;
|
|
208
|
+
/** Files produced by the adapter (paths are relative to the agent
|
|
209
|
+
* workspace; cloud uploads them via the artifact stream). */
|
|
210
|
+
artifacts?: Array<{
|
|
211
|
+
path: string;
|
|
212
|
+
bytes: number;
|
|
213
|
+
mime?: string;
|
|
214
|
+
}>;
|
|
215
|
+
/** Failure reason — must be set when ok=false. */
|
|
216
|
+
error?: string;
|
|
217
|
+
/** Free-form telemetry — token counts, latency, model, etc. */
|
|
218
|
+
metadata?: Record<string, unknown>;
|
|
219
|
+
}
|
|
220
|
+
interface AdapterImpl extends AdapterDescriptor {
|
|
221
|
+
/** Dispatch a task step to this adapter. Should not throw — errors
|
|
222
|
+
* belong in the result. */
|
|
223
|
+
dispatch(input: AdapterDispatchInput): Promise<AdapterDispatchResult>;
|
|
224
|
+
/** Optional: per-adapter health probe. Defaults to "healthy". */
|
|
225
|
+
health?(): Promise<{
|
|
226
|
+
healthy: boolean;
|
|
227
|
+
reason?: string;
|
|
228
|
+
}>;
|
|
229
|
+
/**
|
|
230
|
+
* Reset adapter state for a specific agent (or all agents if undefined).
|
|
231
|
+
*
|
|
232
|
+
* v1.9.x remote-command `agent_restart` semantic: abort/clear whatever
|
|
233
|
+
* per-agent context the adapter holds. For stateless adapters (CLI shim),
|
|
234
|
+
* this is a no-op; for Mode B adapters, it's typically a POST /reset to
|
|
235
|
+
* the loopback so the adapter host clears its own session state.
|
|
236
|
+
*
|
|
237
|
+
* NOT a process restart — the adapter is not expected to own a PID.
|
|
238
|
+
*
|
|
239
|
+
* @param agentName Optional agent name to scope the reset. Undefined = reset all.
|
|
240
|
+
* @returns Arbitrary result; ok:true for success, ok:false+reason otherwise.
|
|
241
|
+
*/
|
|
242
|
+
reset?(agentName?: string): Promise<{
|
|
243
|
+
ok: boolean;
|
|
244
|
+
state?: string;
|
|
245
|
+
reason?: string;
|
|
246
|
+
[k: string]: unknown;
|
|
247
|
+
}>;
|
|
248
|
+
}
|
|
249
|
+
declare class AdapterRegistry {
|
|
250
|
+
private readonly adapters;
|
|
251
|
+
register(adapter: AdapterImpl): void;
|
|
252
|
+
unregister(name: string): boolean;
|
|
253
|
+
has(name: string): boolean;
|
|
254
|
+
get(name: string): AdapterImpl | undefined;
|
|
255
|
+
list(): AdapterDescriptor[];
|
|
256
|
+
size(): number;
|
|
257
|
+
/**
|
|
258
|
+
* Find adapters that can satisfy the given capability tag.
|
|
259
|
+
*
|
|
260
|
+
* An adapter matches if `capabilityTags` includes the tag verbatim or
|
|
261
|
+
* if the adapter declared a wildcard prefix match (e.g. `code.*` matches
|
|
262
|
+
* `code.write`). Returns deterministic ordering — adapters are sorted by
|
|
263
|
+
* name so callers see the same result for the same registry state.
|
|
264
|
+
*/
|
|
265
|
+
findByCapability(capability: string): AdapterImpl[];
|
|
266
|
+
/**
|
|
267
|
+
* Find adapters that can host a given PARA tier.
|
|
268
|
+
*/
|
|
269
|
+
findByTier(tier: number): AdapterImpl[];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Prismer Runtime — Dispatch Mux (Sprint A3, D4).
|
|
274
|
+
*
|
|
275
|
+
* Picks an adapter for an incoming task and forwards the dispatch call.
|
|
276
|
+
* Selection policy (in priority order):
|
|
277
|
+
*
|
|
278
|
+
* 1. `preferAdapter` (caller-supplied) wins if registered AND the
|
|
279
|
+
* adapter can satisfy the requested capability.
|
|
280
|
+
* 2. If exactly one adapter matches the capability, use it.
|
|
281
|
+
* 3. If multiple match, pick deterministically — adapters sorted by
|
|
282
|
+
* `name` (so the same task always lands on the same adapter when
|
|
283
|
+
* multiple are eligible). Future: load/latency-aware ranking.
|
|
284
|
+
* 4. None matches → return a structured "no_adapter" failure rather
|
|
285
|
+
* than throw — the caller (cloud) decides whether to reroute.
|
|
286
|
+
*
|
|
287
|
+
* The mux never throws on adapter errors; it always returns a result
|
|
288
|
+
* with `ok=false` so the cloud-side TaskRouter can report `step_failed`
|
|
289
|
+
* instead of seeing a daemon-side stack trace.
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
interface DispatchMuxRequest extends AdapterDispatchInput {
|
|
293
|
+
/** If set and registered, win selection. */
|
|
294
|
+
preferAdapter?: string;
|
|
295
|
+
}
|
|
296
|
+
interface DispatchMuxResult extends AdapterDispatchResult {
|
|
297
|
+
/** Which adapter handled this request. Useful for telemetry +
|
|
298
|
+
* debugging when the answer "feels wrong". */
|
|
299
|
+
adapter?: string;
|
|
300
|
+
}
|
|
301
|
+
declare class DispatchMux {
|
|
302
|
+
private readonly registry;
|
|
303
|
+
constructor(registry: AdapterRegistry);
|
|
304
|
+
/**
|
|
305
|
+
* Resolve the adapter that would handle a given request without
|
|
306
|
+
* actually dispatching. Returned undefined means "no adapter matches".
|
|
307
|
+
* Useful for cloud-side capability probing.
|
|
308
|
+
*/
|
|
309
|
+
resolve(req: {
|
|
310
|
+
capability: string;
|
|
311
|
+
preferAdapter?: string;
|
|
312
|
+
}): {
|
|
313
|
+
adapter: string;
|
|
314
|
+
} | undefined;
|
|
315
|
+
dispatch(req: DispatchMuxRequest): Promise<DispatchMuxResult>;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
interface PairOfferRecord {
|
|
319
|
+
offer: string;
|
|
320
|
+
uri: string;
|
|
321
|
+
expiresAt: number;
|
|
322
|
+
createdAt: number;
|
|
323
|
+
paired: boolean;
|
|
324
|
+
bindingId?: string;
|
|
325
|
+
deviceName?: string;
|
|
326
|
+
transport?: 'lan' | 'relay';
|
|
327
|
+
clientPubKey?: string;
|
|
328
|
+
}
|
|
329
|
+
interface PairingStatus {
|
|
330
|
+
paired: boolean;
|
|
331
|
+
expired: boolean;
|
|
332
|
+
bindingId?: string;
|
|
333
|
+
deviceName?: string;
|
|
334
|
+
transport?: 'lan' | 'relay';
|
|
335
|
+
}
|
|
336
|
+
declare class PairingManager {
|
|
337
|
+
private readonly offers;
|
|
338
|
+
createOffer(ttlSec?: number): PairOfferRecord;
|
|
339
|
+
getStatus(offer: string): PairingStatus | null;
|
|
340
|
+
confirm(offer: string, input?: {
|
|
341
|
+
bindingId?: string;
|
|
342
|
+
deviceName?: string;
|
|
343
|
+
transport?: 'lan' | 'relay';
|
|
344
|
+
clientPubKey?: string;
|
|
345
|
+
}): PairingStatus;
|
|
346
|
+
cleanExpired(): void;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* event-handler.ts — PARA event subscription and processing for Daemon
|
|
351
|
+
*
|
|
352
|
+
* Implements PARA event subscription, Tier-based event routing, and
|
|
353
|
+
* integration with daemon startup workflow.
|
|
354
|
+
*
|
|
355
|
+
* Reference: docs/version190/04-sandbox-permissions.md §5.3
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
interface EventHandlerOptions {
|
|
359
|
+
bus: EventBus;
|
|
360
|
+
supervisor: AgentSupervisor;
|
|
361
|
+
/** Optional tier filter for event routing */
|
|
362
|
+
tierFilter?: number;
|
|
363
|
+
}
|
|
364
|
+
declare class EventHandler {
|
|
365
|
+
private readonly _bus;
|
|
366
|
+
private readonly _supervisor;
|
|
367
|
+
private readonly _tierFilter?;
|
|
368
|
+
private readonly _subscriptions;
|
|
369
|
+
private readonly _blockedEvents;
|
|
370
|
+
private _registered;
|
|
371
|
+
constructor(opts: EventHandlerOptions);
|
|
372
|
+
/**
|
|
373
|
+
* Start subscribing to PARA events.
|
|
374
|
+
* Must be called after daemon is fully initialized.
|
|
375
|
+
*/
|
|
376
|
+
start(): void;
|
|
377
|
+
/**
|
|
378
|
+
* Stop all event subscriptions.
|
|
379
|
+
*/
|
|
380
|
+
stop(): void;
|
|
381
|
+
/**
|
|
382
|
+
* Subscribe to a specific PARA event type with optional tier filter.
|
|
383
|
+
*/
|
|
384
|
+
private _subscribe;
|
|
385
|
+
/**
|
|
386
|
+
* Unsubscribe from a specific event type.
|
|
387
|
+
*/
|
|
388
|
+
unsubscribe(eventType: string, handler?: SubscriptionHandler<ParaEvent>): void;
|
|
389
|
+
/**
|
|
390
|
+
* Handle agent registration event
|
|
391
|
+
*/
|
|
392
|
+
private _handleAgentRegister;
|
|
393
|
+
/**
|
|
394
|
+
* Handle session started event
|
|
395
|
+
*/
|
|
396
|
+
private _handleSessionStarted;
|
|
397
|
+
/**
|
|
398
|
+
* Handle session ended event
|
|
399
|
+
*/
|
|
400
|
+
private _handleSessionEnded;
|
|
401
|
+
/**
|
|
402
|
+
* Handle agent state change
|
|
403
|
+
*/
|
|
404
|
+
private _handleAgentState;
|
|
405
|
+
/**
|
|
406
|
+
* Handle skill activation
|
|
407
|
+
*/
|
|
408
|
+
private _handleSkillActivated;
|
|
409
|
+
/**
|
|
410
|
+
* Handle skill deactivation
|
|
411
|
+
*/
|
|
412
|
+
private _handleSkillDeactivated;
|
|
413
|
+
/**
|
|
414
|
+
* Handle approval request
|
|
415
|
+
*/
|
|
416
|
+
private _handleApprovalRequest;
|
|
417
|
+
/**
|
|
418
|
+
* Handle approval result
|
|
419
|
+
*/
|
|
420
|
+
private _handleApprovalResult;
|
|
421
|
+
/**
|
|
422
|
+
* Handle task creation
|
|
423
|
+
*/
|
|
424
|
+
private _handleTaskCreated;
|
|
425
|
+
/**
|
|
426
|
+
* Handle task completion
|
|
427
|
+
*/
|
|
428
|
+
private _handleTaskCompleted;
|
|
429
|
+
/**
|
|
430
|
+
* Handle LLM pre-request
|
|
431
|
+
*/
|
|
432
|
+
private _handleLlmPre;
|
|
433
|
+
/**
|
|
434
|
+
* Handle LLM post-request
|
|
435
|
+
*/
|
|
436
|
+
private _handleLlmPost;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** Authenticated identity returned by the authenticate callback. */
|
|
440
|
+
interface AuthenticatedIdentity {
|
|
441
|
+
agentId: string;
|
|
442
|
+
bearerSub?: string;
|
|
443
|
+
}
|
|
444
|
+
interface DaemonHttpOptions {
|
|
445
|
+
host?: string;
|
|
446
|
+
port?: number;
|
|
447
|
+
eventBus: EventBus;
|
|
448
|
+
supervisor: AgentSupervisor;
|
|
449
|
+
fsContextProvider?: (req: {
|
|
450
|
+
agentId: string;
|
|
451
|
+
workspace?: string;
|
|
452
|
+
}) => FsContext | undefined;
|
|
453
|
+
pairingManager?: PairingManager;
|
|
454
|
+
eventHandler?: EventHandler;
|
|
455
|
+
/** When provided, every non-health request must carry a valid Bearer token (null = 401).
|
|
456
|
+
* When absent, localhost trust mode: body agentId is used as-is. */
|
|
457
|
+
authenticate?: (bearerToken: string | undefined) => AuthenticatedIdentity | null;
|
|
458
|
+
}
|
|
459
|
+
/** Generic extension-point route handler registered via registerRoute() (Q3). */
|
|
460
|
+
type RouteHandler = (req: http.IncomingMessage, res: http.ServerResponse, ctx: {
|
|
461
|
+
authed: AuthenticatedIdentity | null;
|
|
462
|
+
body: Buffer;
|
|
463
|
+
}) => Promise<void> | void;
|
|
464
|
+
declare class DaemonHttpServer {
|
|
465
|
+
private readonly _host;
|
|
466
|
+
private readonly _port;
|
|
467
|
+
private readonly _bus;
|
|
468
|
+
private readonly _supervisor;
|
|
469
|
+
private readonly _fsCtxProvider?;
|
|
470
|
+
private readonly _authenticate?;
|
|
471
|
+
private readonly _pairingManager;
|
|
472
|
+
private readonly _eventHandler?;
|
|
473
|
+
private _server;
|
|
474
|
+
private _url;
|
|
475
|
+
private _running;
|
|
476
|
+
private readonly _sseClients;
|
|
477
|
+
private readonly _inFlight;
|
|
478
|
+
private readonly _startedAt;
|
|
479
|
+
private readonly _customRoutes;
|
|
480
|
+
constructor(opts: DaemonHttpOptions);
|
|
481
|
+
get url(): string | undefined;
|
|
482
|
+
get isRunning(): boolean;
|
|
483
|
+
/** Register a custom route handler. Built-in routes take precedence. */
|
|
484
|
+
registerRoute(method: 'GET' | 'POST' | 'PATCH' | 'DELETE', path: string, handler: RouteHandler): void;
|
|
485
|
+
/** Remove a previously registered custom route. */
|
|
486
|
+
unregisterRoute(method: string, path: string): void;
|
|
487
|
+
private _findCustomRoute;
|
|
488
|
+
start(): Promise<{
|
|
489
|
+
host: string;
|
|
490
|
+
port: number;
|
|
491
|
+
}>;
|
|
492
|
+
stop(timeoutMs?: number): Promise<void>;
|
|
493
|
+
private _handleRequest;
|
|
494
|
+
private _handleHealth;
|
|
495
|
+
private _handlePairOffer;
|
|
496
|
+
private _handlePairStatus;
|
|
497
|
+
private _handlePairConfirm;
|
|
498
|
+
private _handleTransportStatus;
|
|
499
|
+
/** Read-only snapshot of transport state, used by /gui/status. Returns
|
|
500
|
+
* null when the multi-path transport isn't enabled. */
|
|
501
|
+
private _snapshotTransportStatus;
|
|
502
|
+
private _handleLanProbe;
|
|
503
|
+
private _handleTransportReprobe;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
interface LLMMessage {
|
|
507
|
+
role: 'system' | 'user' | 'assistant';
|
|
508
|
+
content: string;
|
|
509
|
+
}
|
|
510
|
+
interface LLMRequest {
|
|
511
|
+
messages: LLMMessage[];
|
|
512
|
+
maxTokens?: number;
|
|
513
|
+
temperature?: number;
|
|
514
|
+
maxCostUsd?: number;
|
|
515
|
+
maxLatencyMs?: number;
|
|
516
|
+
capabilities?: string[];
|
|
517
|
+
}
|
|
518
|
+
interface LLMResponse {
|
|
519
|
+
content: string;
|
|
520
|
+
usage: {
|
|
521
|
+
promptTokens: number;
|
|
522
|
+
completionTokens: number;
|
|
523
|
+
totalTokens: number;
|
|
524
|
+
};
|
|
525
|
+
provider: string;
|
|
526
|
+
model: string;
|
|
527
|
+
latencyMs: number;
|
|
528
|
+
costUsd?: number;
|
|
529
|
+
}
|
|
530
|
+
interface LLMProvider {
|
|
531
|
+
name: string;
|
|
532
|
+
model: string;
|
|
533
|
+
priority: number;
|
|
534
|
+
capabilities?: string[];
|
|
535
|
+
pricing?: {
|
|
536
|
+
inputPer1kUsd: number;
|
|
537
|
+
outputPer1kUsd: number;
|
|
538
|
+
};
|
|
539
|
+
invoke: (req: LLMRequest, signal: AbortSignal) => Promise<Omit<LLMResponse, 'provider' | 'model' | 'latencyMs'>>;
|
|
540
|
+
healthCheck?: () => Promise<boolean>;
|
|
541
|
+
}
|
|
542
|
+
interface RoutingPolicy {
|
|
543
|
+
strategy: 'priority' | 'cheapest' | 'fastest' | 'round-robin';
|
|
544
|
+
retryOnFailure?: boolean;
|
|
545
|
+
maxRetries?: number;
|
|
546
|
+
healthCheckIntervalMs?: number;
|
|
547
|
+
timeoutMs?: number;
|
|
548
|
+
}
|
|
549
|
+
interface ProviderStats {
|
|
550
|
+
invocations: number;
|
|
551
|
+
successes: number;
|
|
552
|
+
failures: number;
|
|
553
|
+
p50Ms: number;
|
|
554
|
+
p95Ms: number;
|
|
555
|
+
avgCostUsd: number;
|
|
556
|
+
}
|
|
557
|
+
declare class AllProvidersFailedError extends Error {
|
|
558
|
+
attempts: Array<{
|
|
559
|
+
provider: string;
|
|
560
|
+
error: string;
|
|
561
|
+
}>;
|
|
562
|
+
constructor(attempts: AllProvidersFailedError['attempts']);
|
|
563
|
+
}
|
|
564
|
+
declare class LLMDispatcher {
|
|
565
|
+
private readonly providers;
|
|
566
|
+
private policy;
|
|
567
|
+
private readonly state;
|
|
568
|
+
private rrCounter;
|
|
569
|
+
private healthTimer;
|
|
570
|
+
constructor(providers: LLMProvider[], policy?: RoutingPolicy);
|
|
571
|
+
complete(req: LLMRequest): Promise<LLMResponse>;
|
|
572
|
+
get stats(): Record<string, ProviderStats>;
|
|
573
|
+
setPolicy(policy: RoutingPolicy): void;
|
|
574
|
+
markProviderDown(name: string): void;
|
|
575
|
+
markProviderUp(name: string): void;
|
|
576
|
+
stopHealthChecks(): void;
|
|
577
|
+
private startHealthChecks;
|
|
578
|
+
private runHealthChecks;
|
|
579
|
+
_runHealthChecksNow(): Promise<void>;
|
|
580
|
+
private isDown;
|
|
581
|
+
private estimateCostUsd;
|
|
582
|
+
private selectProviders;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Prismer Runtime — Evolution Gateway HTTP API
|
|
587
|
+
*
|
|
588
|
+
* Exposes evolution gateway operations via HTTP for daemon process.
|
|
589
|
+
* Integrates with LLM Dispatcher for distillation triggers.
|
|
590
|
+
*
|
|
591
|
+
* Endpoints:
|
|
592
|
+
* - POST /evolution/signal — Extract signals from tool output / log
|
|
593
|
+
* - POST /evolution/record — Record gene execution outcome
|
|
594
|
+
* - POST /evolution/analyze — Analyze signals and recommend gene
|
|
595
|
+
* - POST /evolution/genes — Create new gene
|
|
596
|
+
* - GET /evolution/genes — Query available genes
|
|
597
|
+
* - GET /evolution/personality — Get agent personality
|
|
598
|
+
* - POST /evolution/distill — Trigger distillation
|
|
599
|
+
*
|
|
600
|
+
* @see docs/version190/IMPLEMENTATION-PLAN-RUNTIME-GAP.md
|
|
601
|
+
*/
|
|
602
|
+
|
|
603
|
+
interface EvolutionGatewayOptions {
|
|
604
|
+
eventBus: EventBus;
|
|
605
|
+
supervisor: AgentSupervisor;
|
|
606
|
+
llmDispatcher?: LLMDispatcher;
|
|
607
|
+
/** Cloud API base URL (default: https://prismer.cloud) */
|
|
608
|
+
cloudApiBase?: string;
|
|
609
|
+
/** Authenticated identity (from Bearer token or API key) */
|
|
610
|
+
authenticate?: (bearerToken: string | undefined) => AuthenticatedIdentity | null;
|
|
611
|
+
/**
|
|
612
|
+
* v1.9.0 B.7.a — API key used as the Bearer token when forwarding daemon
|
|
613
|
+
* requests to the cloud Evolution API. When undefined, outbound requests
|
|
614
|
+
* omit the Authorization header (callers get 401 from cloud until the
|
|
615
|
+
* daemon is started with `apiKey`).
|
|
616
|
+
*/
|
|
617
|
+
cloudApiKey?: string;
|
|
618
|
+
}
|
|
619
|
+
interface SignalExtractionRequest {
|
|
620
|
+
toolOutput?: {
|
|
621
|
+
toolName?: string;
|
|
622
|
+
output?: string;
|
|
623
|
+
exitCode?: number;
|
|
624
|
+
error?: string;
|
|
625
|
+
durationMs?: number;
|
|
626
|
+
};
|
|
627
|
+
logFile?: {
|
|
628
|
+
logPath?: string;
|
|
629
|
+
logContent?: string;
|
|
630
|
+
timestamp?: string;
|
|
631
|
+
};
|
|
632
|
+
provider?: string;
|
|
633
|
+
stage?: string;
|
|
634
|
+
severity?: string;
|
|
635
|
+
tags?: string[];
|
|
636
|
+
}
|
|
637
|
+
interface AnalyzeRequest {
|
|
638
|
+
signals: string[];
|
|
639
|
+
taskCapability?: string;
|
|
640
|
+
provider?: string;
|
|
641
|
+
stage?: string;
|
|
642
|
+
severity?: string;
|
|
643
|
+
}
|
|
644
|
+
interface RecordRequest {
|
|
645
|
+
geneId: string;
|
|
646
|
+
signals: string[];
|
|
647
|
+
outcome: 'success' | 'failed';
|
|
648
|
+
score?: number;
|
|
649
|
+
summary?: string;
|
|
650
|
+
costCredits?: number;
|
|
651
|
+
transitionReason?: 'gene_applied' | 'fallback_relaxed' | 'fallback_neighbor' | 'baseline';
|
|
652
|
+
}
|
|
653
|
+
interface CreateGeneRequest {
|
|
654
|
+
category: 'repair' | 'optimize' | 'innovate' | 'diagnostic';
|
|
655
|
+
signalsMatch: string[];
|
|
656
|
+
strategy: string[];
|
|
657
|
+
preconditions?: string[];
|
|
658
|
+
constraints?: {
|
|
659
|
+
maxCredits?: number;
|
|
660
|
+
maxRetries?: number;
|
|
661
|
+
maxExecutionTime?: number;
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
interface DistillationRequest {
|
|
665
|
+
/** Skip LLM verification and trigger immediately */
|
|
666
|
+
dryRun?: boolean;
|
|
667
|
+
}
|
|
668
|
+
declare class EvolutionGatewayHttpHandler {
|
|
669
|
+
private readonly eventBus;
|
|
670
|
+
private readonly supervisor;
|
|
671
|
+
private readonly llmDispatcher;
|
|
672
|
+
private readonly cloudApiBase;
|
|
673
|
+
private readonly authenticate;
|
|
674
|
+
private readonly cloudApiKey;
|
|
675
|
+
private readonly routes;
|
|
676
|
+
constructor(opts: EvolutionGatewayOptions);
|
|
677
|
+
private cloudAuthHeaders;
|
|
678
|
+
private registerRoutes;
|
|
679
|
+
/** Get all registered routes */
|
|
680
|
+
getRoutes(): Map<string, RouteHandler>;
|
|
681
|
+
/** Get a specific route handler */
|
|
682
|
+
getRoute(method: string, path: string): RouteHandler | undefined;
|
|
683
|
+
/**
|
|
684
|
+
* POST /evolution/signal
|
|
685
|
+
* Extract signals from tool output or log file.
|
|
686
|
+
*/
|
|
687
|
+
private handleExtractSignal;
|
|
688
|
+
/**
|
|
689
|
+
* POST /evolution/analyze
|
|
690
|
+
* Analyze signals and recommend the best gene.
|
|
691
|
+
*/
|
|
692
|
+
private handleAnalyze;
|
|
693
|
+
/**
|
|
694
|
+
* POST /evolution/record
|
|
695
|
+
* Record gene execution outcome.
|
|
696
|
+
*/
|
|
697
|
+
private handleRecord;
|
|
698
|
+
/**
|
|
699
|
+
* POST /evolution/genes
|
|
700
|
+
* Create a new gene.
|
|
701
|
+
*/
|
|
702
|
+
private handleCreateGene;
|
|
703
|
+
/**
|
|
704
|
+
* GET /evolution/genes
|
|
705
|
+
* Query available genes.
|
|
706
|
+
*/
|
|
707
|
+
private handleQueryGenes;
|
|
708
|
+
/**
|
|
709
|
+
* GET /evolution/personality
|
|
710
|
+
* Get agent personality.
|
|
711
|
+
*/
|
|
712
|
+
private handleGetPersonality;
|
|
713
|
+
/**
|
|
714
|
+
* POST /evolution/distill
|
|
715
|
+
* Trigger gene distillation.
|
|
716
|
+
*/
|
|
717
|
+
private handleDistill;
|
|
718
|
+
/**
|
|
719
|
+
* GET /evolution/unmatched
|
|
720
|
+
* Get unmatched signals (evolution frontier).
|
|
721
|
+
*/
|
|
722
|
+
private handleGetUnmatched;
|
|
723
|
+
private parseCloudResponse;
|
|
724
|
+
private callCloudAnalyze;
|
|
725
|
+
private callCloudRecord;
|
|
726
|
+
private callCloudCreateGene;
|
|
727
|
+
private callCloudQueryGenes;
|
|
728
|
+
private callCloudGetPersonality;
|
|
729
|
+
private callCloudDistill;
|
|
730
|
+
private callCloudGetUnmatched;
|
|
731
|
+
private triggerLocalDistillation;
|
|
732
|
+
private extractSignalsLocal;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Prismer Runtime — Task Router
|
|
737
|
+
*
|
|
738
|
+
* v1.9.0 Task Router: HTTP API for routing tasks to agents,
|
|
739
|
+
* capability matching, and SSE streaming of task state changes.
|
|
740
|
+
*
|
|
741
|
+
* Integrates with:
|
|
742
|
+
* - Cloud API (/api/im/tasks) for persistent task store
|
|
743
|
+
* - AgentSupervisor for local agent status
|
|
744
|
+
* - EventBus for SSE streaming
|
|
745
|
+
*
|
|
746
|
+
* Design reference: docs/version190/IMPLEMENTATION-PLAN-RUNTIME-GAP.md §P1
|
|
747
|
+
*/
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Task routing state machine per §16.4.
|
|
751
|
+
*
|
|
752
|
+
* State transitions:
|
|
753
|
+
* created → dispatching → step_running → step_completed → (next step or completed)
|
|
754
|
+
* step_running → step_failed → retrying (×3) → retry_exhausted → needs_human
|
|
755
|
+
* step_running → step_timeout → rerouting → step_running (new agent)
|
|
756
|
+
*/
|
|
757
|
+
declare enum TaskRouteState {
|
|
758
|
+
Created = "created",
|
|
759
|
+
Dispatching = "dispatching",
|
|
760
|
+
StepRunning = "step_running",
|
|
761
|
+
StepCompleted = "step_completed",
|
|
762
|
+
StepFailed = "step_failed",
|
|
763
|
+
StepTimeout = "step_timeout",
|
|
764
|
+
Retrying = "retrying",
|
|
765
|
+
RetryExhausted = "retry_exhausted",
|
|
766
|
+
NeedsHuman = "needs_human",
|
|
767
|
+
Rerouting = "rerouting",
|
|
768
|
+
Completed = "completed",
|
|
769
|
+
Cancelled = "cancelled"
|
|
770
|
+
}
|
|
771
|
+
interface TaskRouterOptions {
|
|
772
|
+
eventBus: EventBus;
|
|
773
|
+
supervisor: AgentSupervisor;
|
|
774
|
+
cloudBaseUrl?: string;
|
|
775
|
+
apiToken?: string;
|
|
776
|
+
}
|
|
777
|
+
interface TaskInfo {
|
|
778
|
+
id: string;
|
|
779
|
+
title: string;
|
|
780
|
+
status: TaskRouteState | string;
|
|
781
|
+
requiresCapability?: string | null;
|
|
782
|
+
runtimeRoute?: unknown[] | null;
|
|
783
|
+
assigneeId?: string | null;
|
|
784
|
+
progress?: number | null;
|
|
785
|
+
statusMessage?: string | null;
|
|
786
|
+
createdAt: string;
|
|
787
|
+
updatedAt: string;
|
|
788
|
+
}
|
|
789
|
+
interface RouteTaskRequest {
|
|
790
|
+
taskId?: string;
|
|
791
|
+
priority?: 'high' | 'normal' | 'low';
|
|
792
|
+
preferredAgentId?: string;
|
|
793
|
+
}
|
|
794
|
+
interface RouteTaskResponse {
|
|
795
|
+
taskId: string;
|
|
796
|
+
agentId: string;
|
|
797
|
+
capability: string;
|
|
798
|
+
stepIdx: number;
|
|
799
|
+
totalSteps: number;
|
|
800
|
+
}
|
|
801
|
+
interface AssignTaskRequest {
|
|
802
|
+
agentId: string;
|
|
803
|
+
taskId?: string;
|
|
804
|
+
}
|
|
805
|
+
interface StepCompletedRequest {
|
|
806
|
+
taskId?: string;
|
|
807
|
+
stepId: string;
|
|
808
|
+
result?: Record<string, unknown>;
|
|
809
|
+
metadata?: Record<string, unknown>;
|
|
810
|
+
}
|
|
811
|
+
interface CancelTaskRequest {
|
|
812
|
+
taskId?: string;
|
|
813
|
+
reason: string;
|
|
814
|
+
}
|
|
815
|
+
interface StepFailedRequest {
|
|
816
|
+
taskId?: string;
|
|
817
|
+
stepId: string;
|
|
818
|
+
error?: string;
|
|
819
|
+
metadata?: Record<string, unknown>;
|
|
820
|
+
}
|
|
821
|
+
interface StepTimeoutRequest {
|
|
822
|
+
taskId?: string;
|
|
823
|
+
stepId: string;
|
|
824
|
+
capability?: string;
|
|
825
|
+
metadata?: Record<string, unknown>;
|
|
826
|
+
}
|
|
827
|
+
declare class TaskRouter {
|
|
828
|
+
private readonly _bus;
|
|
829
|
+
private readonly _supervisor;
|
|
830
|
+
private readonly _cloudBaseUrl;
|
|
831
|
+
private readonly _apiToken;
|
|
832
|
+
private _sseClients;
|
|
833
|
+
/** Per-step retry counters: key = `${taskId}:${stepId}` */
|
|
834
|
+
private readonly _retryCounters;
|
|
835
|
+
constructor(opts: TaskRouterOptions);
|
|
836
|
+
/**
|
|
837
|
+
* Register HTTP routes with the daemon server.
|
|
838
|
+
* Call this from daemon-http.ts to expose task router endpoints.
|
|
839
|
+
*/
|
|
840
|
+
registerRoutes(server: {
|
|
841
|
+
registerRoute: (method: 'GET' | 'POST' | 'PATCH' | 'DELETE', path: string, handler: RouteHandler) => void;
|
|
842
|
+
}): void;
|
|
843
|
+
private _handleRouteTask;
|
|
844
|
+
private _handleAssignTask;
|
|
845
|
+
private _handleStepCompleted;
|
|
846
|
+
private _handleCancelTask;
|
|
847
|
+
private _handleStepFailed;
|
|
848
|
+
private _handleStepTimeout;
|
|
849
|
+
private _handleGetTask;
|
|
850
|
+
private _handleListTasks;
|
|
851
|
+
/** Publish a task.state event with the given TaskRouteState and optional extra fields. */
|
|
852
|
+
private _publishState;
|
|
853
|
+
private _callCloudApi;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
type KeychainBackend = 'macos-keychain' | 'libsecret' | 'pass' | 'encrypted-file';
|
|
857
|
+
interface KeychainAdapter {
|
|
858
|
+
name: KeychainBackend;
|
|
859
|
+
available(): Promise<boolean>;
|
|
860
|
+
get(service: string, account: string): Promise<string | null>;
|
|
861
|
+
set(service: string, account: string, value: string): Promise<void>;
|
|
862
|
+
delete(service: string, account: string): Promise<void>;
|
|
863
|
+
list(service: string): Promise<string[]>;
|
|
864
|
+
}
|
|
865
|
+
interface KeychainOptions {
|
|
866
|
+
preferredBackend?: KeychainBackend;
|
|
867
|
+
masterPassphrase?: string;
|
|
868
|
+
encryptedFilePath?: string;
|
|
869
|
+
}
|
|
870
|
+
declare class NoKeychainBackendError extends Error {
|
|
871
|
+
constructor();
|
|
872
|
+
}
|
|
873
|
+
declare class KeychainOperationError extends Error {
|
|
874
|
+
constructor(op: string, cause: unknown);
|
|
875
|
+
}
|
|
876
|
+
declare class Keychain {
|
|
877
|
+
private readonly opts;
|
|
878
|
+
private resolvedBackend;
|
|
879
|
+
private detecting;
|
|
880
|
+
constructor(opts?: KeychainOptions);
|
|
881
|
+
backend(): Promise<KeychainAdapter>;
|
|
882
|
+
private buildCandidateList;
|
|
883
|
+
private makeAdapter;
|
|
884
|
+
get(service: string, account: string): Promise<string | null>;
|
|
885
|
+
set(service: string, account: string, value: string): Promise<void>;
|
|
886
|
+
delete(service: string, account: string): Promise<void>;
|
|
887
|
+
list(service: string): Promise<string[]>;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
interface PrismerConfig {
|
|
891
|
+
apiKey?: string;
|
|
892
|
+
apiBase?: string;
|
|
893
|
+
daemon?: {
|
|
894
|
+
host?: string;
|
|
895
|
+
port?: number;
|
|
896
|
+
};
|
|
897
|
+
agents?: Record<string, {
|
|
898
|
+
enabled?: boolean;
|
|
899
|
+
apiKey?: string;
|
|
900
|
+
[key: string]: unknown;
|
|
901
|
+
}>;
|
|
902
|
+
[key: string]: unknown;
|
|
903
|
+
}
|
|
904
|
+
interface LoadConfigOptions {
|
|
905
|
+
path?: string;
|
|
906
|
+
keychain?: Keychain;
|
|
907
|
+
resolvePlaceholders?: boolean;
|
|
908
|
+
}
|
|
909
|
+
declare class ConfigError extends Error {
|
|
910
|
+
constructor(message: string);
|
|
911
|
+
}
|
|
912
|
+
declare function parseKeyringPlaceholder(value: string): {
|
|
913
|
+
service: string;
|
|
914
|
+
account: string;
|
|
915
|
+
} | null;
|
|
916
|
+
declare function loadConfig(opts?: LoadConfigOptions): Promise<PrismerConfig>;
|
|
917
|
+
declare function writeConfig(config: PrismerConfig, opts?: {
|
|
918
|
+
path?: string;
|
|
919
|
+
}): Promise<void>;
|
|
920
|
+
|
|
921
|
+
interface MigrateStep {
|
|
922
|
+
level: 'info' | 'ok' | 'warn' | 'error';
|
|
923
|
+
message: string;
|
|
924
|
+
detail?: string;
|
|
925
|
+
}
|
|
926
|
+
interface MigrateSecretsOptions {
|
|
927
|
+
configPath?: string;
|
|
928
|
+
keychain: Keychain;
|
|
929
|
+
dryRun?: boolean;
|
|
930
|
+
onStep?: (step: MigrateStep) => void;
|
|
931
|
+
}
|
|
932
|
+
interface MigrateSecretsResult {
|
|
933
|
+
migrated: Array<{
|
|
934
|
+
path: string;
|
|
935
|
+
service: string;
|
|
936
|
+
account: string;
|
|
937
|
+
}>;
|
|
938
|
+
skipped: Array<{
|
|
939
|
+
path: string;
|
|
940
|
+
reason: string;
|
|
941
|
+
}>;
|
|
942
|
+
errors: Array<{
|
|
943
|
+
path: string;
|
|
944
|
+
error: string;
|
|
945
|
+
}>;
|
|
946
|
+
}
|
|
947
|
+
declare function migrateSecrets(opts: MigrateSecretsOptions): Promise<MigrateSecretsResult>;
|
|
948
|
+
|
|
949
|
+
interface DaemonRunnerOptions {
|
|
950
|
+
host?: string;
|
|
951
|
+
port?: number;
|
|
952
|
+
pidFile?: string;
|
|
953
|
+
dataDir?: string;
|
|
954
|
+
installSignalHandlers?: boolean;
|
|
955
|
+
authBearer?: string;
|
|
956
|
+
apiKey?: string;
|
|
957
|
+
daemonId?: string;
|
|
958
|
+
userId?: string;
|
|
959
|
+
enableTransport?: boolean;
|
|
960
|
+
forceProbe?: boolean;
|
|
961
|
+
workspace?: string;
|
|
962
|
+
/**
|
|
963
|
+
* v1.9.0 B.7.a — cloud base URL (https://prismer.cloud for prod,
|
|
964
|
+
* https://cloud.prismer.dev for test). Forwarded to EvolutionGateway so
|
|
965
|
+
* daemon→cloud calls hit the same env the API key was issued against.
|
|
966
|
+
*/
|
|
967
|
+
cloudApiBase?: string;
|
|
968
|
+
}
|
|
969
|
+
interface DaemonRunnerHandle {
|
|
970
|
+
stop(): Promise<void>;
|
|
971
|
+
readonly url: string;
|
|
972
|
+
readonly pid: number;
|
|
973
|
+
readonly dataDir: string;
|
|
974
|
+
}
|
|
975
|
+
declare function startDaemonRunner(opts?: DaemonRunnerOptions): Promise<DaemonRunnerHandle>;
|
|
976
|
+
|
|
977
|
+
declare function assertBrandVoice(text: string, label?: string): void;
|
|
978
|
+
|
|
979
|
+
type OutputMode = 'pretty' | 'json' | 'quiet';
|
|
980
|
+
interface UIOptions {
|
|
981
|
+
mode?: OutputMode;
|
|
982
|
+
color?: boolean;
|
|
983
|
+
stream?: NodeJS.WritableStream;
|
|
984
|
+
errStream?: NodeJS.WritableStream;
|
|
985
|
+
}
|
|
986
|
+
interface TableRow {
|
|
987
|
+
[column: string]: string;
|
|
988
|
+
}
|
|
989
|
+
interface TableOptions {
|
|
990
|
+
columns: string[];
|
|
991
|
+
maxWidth?: number;
|
|
992
|
+
}
|
|
993
|
+
interface LegacyTableOptions {
|
|
994
|
+
columns: string[];
|
|
995
|
+
rows: TableRow[];
|
|
996
|
+
maxWidth?: number;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
declare class UI {
|
|
1000
|
+
readonly mode: OutputMode;
|
|
1001
|
+
readonly colorEnabled: boolean;
|
|
1002
|
+
private readonly stream;
|
|
1003
|
+
private readonly errStream;
|
|
1004
|
+
constructor(opts?: UIOptions);
|
|
1005
|
+
private ansi;
|
|
1006
|
+
private green;
|
|
1007
|
+
private red;
|
|
1008
|
+
private yellow;
|
|
1009
|
+
private cyan;
|
|
1010
|
+
private dim;
|
|
1011
|
+
private bold;
|
|
1012
|
+
private gray;
|
|
1013
|
+
private brandMark;
|
|
1014
|
+
private colorBrandLine;
|
|
1015
|
+
write(text: string): void;
|
|
1016
|
+
writeErr(text: string): void;
|
|
1017
|
+
header(text: string): void;
|
|
1018
|
+
smallHeader(subtitle?: string): void;
|
|
1019
|
+
banner(subtitle?: string, opts?: {
|
|
1020
|
+
full?: boolean;
|
|
1021
|
+
}): void;
|
|
1022
|
+
blank(): void;
|
|
1023
|
+
line(text: string): void;
|
|
1024
|
+
info(text: string): void;
|
|
1025
|
+
secondary(text: string, indent?: number): void;
|
|
1026
|
+
tip(text: string): void;
|
|
1027
|
+
next(text: string): void;
|
|
1028
|
+
ok(text: string, detail?: string): void;
|
|
1029
|
+
success(text: string, detail?: string): void;
|
|
1030
|
+
fail(text: string, detail?: string): void;
|
|
1031
|
+
online(text: string): void;
|
|
1032
|
+
offline(text: string): void;
|
|
1033
|
+
notInstalled(text: string): void;
|
|
1034
|
+
pending(text: string): void;
|
|
1035
|
+
warn(text: string, detail?: string): void;
|
|
1036
|
+
error(what: string, cause?: string, fix?: string): void;
|
|
1037
|
+
table(rows: TableRow[], opts: TableOptions): void;
|
|
1038
|
+
table(opts: LegacyTableOptions): void;
|
|
1039
|
+
spinner(text: string): {
|
|
1040
|
+
update(t: string): void;
|
|
1041
|
+
stop(final?: string): void;
|
|
1042
|
+
};
|
|
1043
|
+
progress(text: string, total: number): {
|
|
1044
|
+
update(current: number, detail?: string): void;
|
|
1045
|
+
stop(final?: string): void;
|
|
1046
|
+
};
|
|
1047
|
+
json(payload: unknown, opts?: {
|
|
1048
|
+
pretty?: boolean;
|
|
1049
|
+
}): void;
|
|
1050
|
+
result<T>(pretty: () => void, jsonPayload: T): void;
|
|
1051
|
+
}
|
|
1052
|
+
declare function getUI(): UI;
|
|
1053
|
+
declare function setUI(ui: UI): void;
|
|
1054
|
+
declare function applyCommonFlags(argv: string[]): {
|
|
1055
|
+
mode: OutputMode;
|
|
1056
|
+
color: boolean;
|
|
1057
|
+
restArgv: string[];
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
interface CliContext {
|
|
1061
|
+
ui: UI;
|
|
1062
|
+
keychain: Keychain;
|
|
1063
|
+
cwd: string;
|
|
1064
|
+
argv: string[];
|
|
1065
|
+
}
|
|
1066
|
+
declare function createCliContext(opts?: {
|
|
1067
|
+
argv?: string[];
|
|
1068
|
+
ui?: UI;
|
|
1069
|
+
}): Promise<CliContext>;
|
|
1070
|
+
|
|
1071
|
+
interface AgentCatalogEntry {
|
|
1072
|
+
name: string;
|
|
1073
|
+
displayName: string;
|
|
1074
|
+
packPackage: string;
|
|
1075
|
+
packVersionRange: string;
|
|
1076
|
+
hookConfigPath: string;
|
|
1077
|
+
mcpConfigPath?: string;
|
|
1078
|
+
upstreamBinary: string;
|
|
1079
|
+
upstreamVersionRange?: string;
|
|
1080
|
+
localSourcePath?: string;
|
|
1081
|
+
installCommand?: string;
|
|
1082
|
+
tiersSupported: number[];
|
|
1083
|
+
capabilityTags: string[];
|
|
1084
|
+
detect: () => Promise<{
|
|
1085
|
+
found: boolean;
|
|
1086
|
+
binaryPath?: string;
|
|
1087
|
+
version?: string;
|
|
1088
|
+
}>;
|
|
1089
|
+
}
|
|
1090
|
+
declare const AGENT_CATALOG: AgentCatalogEntry[];
|
|
1091
|
+
declare function getAgent(name: string): AgentCatalogEntry | undefined;
|
|
1092
|
+
|
|
1093
|
+
type HookFormat = 'v1.8' | 'v1.9';
|
|
1094
|
+
interface HookEntry {
|
|
1095
|
+
command?: string;
|
|
1096
|
+
webhook?: string;
|
|
1097
|
+
enabled?: boolean;
|
|
1098
|
+
[key: string]: unknown;
|
|
1099
|
+
}
|
|
1100
|
+
interface HookConfig {
|
|
1101
|
+
hooks: Record<string, HookEntry | HookEntry[]>;
|
|
1102
|
+
[key: string]: unknown;
|
|
1103
|
+
}
|
|
1104
|
+
interface MergeResult {
|
|
1105
|
+
merged: HookConfig;
|
|
1106
|
+
added: string[];
|
|
1107
|
+
preserved: string[];
|
|
1108
|
+
replaced: string[];
|
|
1109
|
+
backupPath?: string;
|
|
1110
|
+
}
|
|
1111
|
+
interface MergeOptions {
|
|
1112
|
+
daemonUrl: string;
|
|
1113
|
+
dryRun?: boolean;
|
|
1114
|
+
/** Absolute path to the installed claude-code-plugin root (contains hooks/para-emit.mjs). */
|
|
1115
|
+
pluginRoot?: string;
|
|
1116
|
+
}
|
|
1117
|
+
declare function mergeHooks(existing: HookConfig | null, opts: MergeOptions): MergeResult;
|
|
1118
|
+
declare function readHookConfig(filePath: string): Promise<HookConfig | null>;
|
|
1119
|
+
declare function writeHookConfig(filePath: string, cfg: HookConfig): Promise<void>;
|
|
1120
|
+
declare function installHooks(configPath: string, existing: HookConfig | null, opts: MergeOptions): Promise<MergeResult>;
|
|
1121
|
+
declare function rollbackHooks(configPath: string): Promise<{
|
|
1122
|
+
restored: boolean;
|
|
1123
|
+
fromBackup: string | null;
|
|
1124
|
+
}>;
|
|
1125
|
+
|
|
1126
|
+
interface PairOffer {
|
|
1127
|
+
offer: string;
|
|
1128
|
+
uri: string;
|
|
1129
|
+
expiresAt: number;
|
|
1130
|
+
relayUrl?: string;
|
|
1131
|
+
lanHost?: string;
|
|
1132
|
+
lanPort?: number;
|
|
1133
|
+
}
|
|
1134
|
+
interface PairedDevice {
|
|
1135
|
+
id: string;
|
|
1136
|
+
name: string;
|
|
1137
|
+
method: 'qr' | 'api-key';
|
|
1138
|
+
transport: 'lan' | 'relay';
|
|
1139
|
+
lastSeenAt: number;
|
|
1140
|
+
pairedAt: number;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
interface MigrateOptions {
|
|
1144
|
+
dryRun?: boolean;
|
|
1145
|
+
configPath?: string;
|
|
1146
|
+
homeDir?: string;
|
|
1147
|
+
yes?: boolean;
|
|
1148
|
+
/**
|
|
1149
|
+
* Dependency-injection hook for tests — override the TTY confirmation prompt.
|
|
1150
|
+
* When provided, replaces the readline-based promptConfirm call entirely.
|
|
1151
|
+
*/
|
|
1152
|
+
confirmer?: () => Promise<boolean>;
|
|
1153
|
+
}
|
|
1154
|
+
interface MigrateResult {
|
|
1155
|
+
apiKeyMigrated: boolean;
|
|
1156
|
+
hooksBackedUp: string[];
|
|
1157
|
+
hooksRedirected: string[];
|
|
1158
|
+
memoryFilesImported: number;
|
|
1159
|
+
networkEndpointsRewritten: Array<{
|
|
1160
|
+
file: string;
|
|
1161
|
+
rewrites: number;
|
|
1162
|
+
backupPath: string;
|
|
1163
|
+
}>;
|
|
1164
|
+
errors: Array<{
|
|
1165
|
+
step: string;
|
|
1166
|
+
error: string;
|
|
1167
|
+
}>;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
interface EncryptionConfig {
|
|
1171
|
+
enabled: boolean;
|
|
1172
|
+
key?: Buffer;
|
|
1173
|
+
}
|
|
1174
|
+
interface MemoryFile {
|
|
1175
|
+
id: string;
|
|
1176
|
+
ownerId: string;
|
|
1177
|
+
ownerType: 'user' | 'agent';
|
|
1178
|
+
scope: string;
|
|
1179
|
+
path: string;
|
|
1180
|
+
content: string;
|
|
1181
|
+
version: number;
|
|
1182
|
+
memoryType?: string;
|
|
1183
|
+
description?: string;
|
|
1184
|
+
contentHash: string;
|
|
1185
|
+
createdAt: string;
|
|
1186
|
+
updatedAt: string;
|
|
1187
|
+
stale: boolean;
|
|
1188
|
+
}
|
|
1189
|
+
interface MemoryFileVersion {
|
|
1190
|
+
id: string;
|
|
1191
|
+
fileId: string;
|
|
1192
|
+
version: number;
|
|
1193
|
+
content: string;
|
|
1194
|
+
createdAt: string;
|
|
1195
|
+
}
|
|
1196
|
+
interface DreamCompaction {
|
|
1197
|
+
id: string;
|
|
1198
|
+
ownerId: string;
|
|
1199
|
+
scope: string;
|
|
1200
|
+
summary: string;
|
|
1201
|
+
createdAt: string;
|
|
1202
|
+
}
|
|
1203
|
+
interface WriteMemoryFileInput {
|
|
1204
|
+
ownerId: string;
|
|
1205
|
+
ownerType: 'user' | 'agent';
|
|
1206
|
+
scope?: string;
|
|
1207
|
+
path: string;
|
|
1208
|
+
content: string;
|
|
1209
|
+
memoryType?: string;
|
|
1210
|
+
description?: string;
|
|
1211
|
+
}
|
|
1212
|
+
interface MemoryFileFilters {
|
|
1213
|
+
ownerId?: string;
|
|
1214
|
+
scope?: string;
|
|
1215
|
+
path?: string;
|
|
1216
|
+
memoryType?: string;
|
|
1217
|
+
stale?: boolean;
|
|
1218
|
+
limit?: number;
|
|
1219
|
+
offset?: number;
|
|
1220
|
+
}
|
|
1221
|
+
interface MemorySearchResult extends MemoryFile {
|
|
1222
|
+
relevance: number;
|
|
1223
|
+
snippet?: string;
|
|
1224
|
+
}
|
|
1225
|
+
interface MemoryStats {
|
|
1226
|
+
fileCount: number;
|
|
1227
|
+
totalSize: number;
|
|
1228
|
+
totalBytes: number;
|
|
1229
|
+
staleFiles: number;
|
|
1230
|
+
ftsIndexed: number;
|
|
1231
|
+
recallP95: number;
|
|
1232
|
+
}
|
|
1233
|
+
declare class MemoryDB {
|
|
1234
|
+
private readonly filePath;
|
|
1235
|
+
private readonly db;
|
|
1236
|
+
private readonly crypto;
|
|
1237
|
+
constructor(_encryptionConfig?: EncryptionConfig, opts?: {
|
|
1238
|
+
filePath?: string;
|
|
1239
|
+
});
|
|
1240
|
+
writeMemoryFile(input: WriteMemoryFileInput): MemoryFile;
|
|
1241
|
+
getMemoryFileById(id: string): MemoryFile | null;
|
|
1242
|
+
deleteMemoryFile(id: string): boolean;
|
|
1243
|
+
listMemoryFiles(filters?: MemoryFileFilters): MemoryFile[];
|
|
1244
|
+
searchMemoryFiles(keyword: string, filters?: MemoryFileFilters): MemorySearchResult[];
|
|
1245
|
+
getStats(ownerId?: string): MemoryStats;
|
|
1246
|
+
close(): void;
|
|
1247
|
+
}
|
|
1248
|
+
declare function getMemoryDB(): MemoryDB;
|
|
1249
|
+
declare function closeMemoryDB(): void;
|
|
1250
|
+
declare function generateSalt(): Buffer;
|
|
1251
|
+
declare function deriveKey(password: string, salt: Buffer): Buffer;
|
|
1252
|
+
declare function encrypt(plaintext: string, key: Buffer): string;
|
|
1253
|
+
declare function decrypt(encoded: string, key: Buffer): string;
|
|
1254
|
+
|
|
1255
|
+
interface WriteMemoryRequest {
|
|
1256
|
+
ownerId: string;
|
|
1257
|
+
ownerType: 'user' | 'agent';
|
|
1258
|
+
path: string;
|
|
1259
|
+
content: string;
|
|
1260
|
+
scope?: string;
|
|
1261
|
+
memoryType?: string;
|
|
1262
|
+
description?: string;
|
|
1263
|
+
encrypt?: boolean;
|
|
1264
|
+
}
|
|
1265
|
+
interface RecallRequest {
|
|
1266
|
+
keyword: string;
|
|
1267
|
+
ownerId?: string;
|
|
1268
|
+
scope?: string;
|
|
1269
|
+
limit?: number;
|
|
1270
|
+
useCloudFallback?: boolean;
|
|
1271
|
+
}
|
|
1272
|
+
interface ListMemoryRequest {
|
|
1273
|
+
ownerId?: string;
|
|
1274
|
+
scope?: string;
|
|
1275
|
+
path?: string;
|
|
1276
|
+
memoryType?: string;
|
|
1277
|
+
stale?: boolean;
|
|
1278
|
+
limit?: number;
|
|
1279
|
+
offset?: number;
|
|
1280
|
+
}
|
|
1281
|
+
interface MemoryResponse {
|
|
1282
|
+
success: boolean;
|
|
1283
|
+
data?: unknown;
|
|
1284
|
+
error?: {
|
|
1285
|
+
code: string;
|
|
1286
|
+
message: string;
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
interface DreamResult {
|
|
1291
|
+
ok: boolean;
|
|
1292
|
+
compactedFiles: number;
|
|
1293
|
+
summary?: DreamCompaction;
|
|
1294
|
+
}
|
|
1295
|
+
interface DreamSchedulerOptions {
|
|
1296
|
+
ownerId?: string;
|
|
1297
|
+
scope?: string;
|
|
1298
|
+
intervalMs?: number;
|
|
1299
|
+
}
|
|
1300
|
+
declare class DreamScheduler {
|
|
1301
|
+
private readonly opts;
|
|
1302
|
+
private timer;
|
|
1303
|
+
constructor(opts?: DreamSchedulerOptions);
|
|
1304
|
+
start(): void;
|
|
1305
|
+
stop(): void;
|
|
1306
|
+
}
|
|
1307
|
+
declare function createDreamScheduler(opts?: DreamSchedulerOptions): DreamScheduler;
|
|
1308
|
+
declare function runDream(opts?: DreamSchedulerOptions): Promise<DreamResult>;
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* PARA L8 — Session Export Trace Writer
|
|
1312
|
+
*
|
|
1313
|
+
* Per PARA spec §4.2 L8 (docs/version190/03-para-spec.md):
|
|
1314
|
+
* session export 必须是 pre-compaction 的原始 trace。compaction 会把历史消息替换为 summary——
|
|
1315
|
+
* 这种 adapter 必须额外持久化原始 trace 到 `.prismer/trace/<sessionId>.jsonl.zst`
|
|
1316
|
+
* (append-only + zstd 压缩),compaction 只影响 working memory,不动 trace。
|
|
1317
|
+
*
|
|
1318
|
+
* Implementation:
|
|
1319
|
+
* - Each `append()` JSON-stringifies the envelope, adds '\n', zstd-compresses it,
|
|
1320
|
+
* then sync-appends the compressed bytes to the trace file.
|
|
1321
|
+
* - zstd supports concatenated frames — multiple independent compressed blobs
|
|
1322
|
+
* catenated together decompress back to the concatenation of their inputs.
|
|
1323
|
+
* This is what makes append-only valid without holding a long-lived stream.
|
|
1324
|
+
* - File mode 0o600 (trace may contain sensitive payloads).
|
|
1325
|
+
* - Node 22+ gives us native `zlib.zstdCompressSync`. Older Node: append is a no-op
|
|
1326
|
+
* and a single stderr warning is emitted at construction time. We do NOT throw
|
|
1327
|
+
* or crash the daemon — daemon must still start on Node 20.
|
|
1328
|
+
*/
|
|
1329
|
+
|
|
1330
|
+
interface TraceWriterOptions {
|
|
1331
|
+
sessionId: string;
|
|
1332
|
+
/**
|
|
1333
|
+
* Override home directory; trace file will live at
|
|
1334
|
+
* `<homeDir>/.prismer/trace/<sessionId>.jsonl.zst`. Defaults to `os.homedir()`.
|
|
1335
|
+
*/
|
|
1336
|
+
homeDir?: string;
|
|
1337
|
+
/**
|
|
1338
|
+
* Explicit trace directory. Overrides homeDir if provided. Useful when the
|
|
1339
|
+
* daemon runs with a non-default `dataDir`.
|
|
1340
|
+
*/
|
|
1341
|
+
traceDir?: string;
|
|
1342
|
+
}
|
|
1343
|
+
declare class TraceWriter {
|
|
1344
|
+
private readonly _sessionId;
|
|
1345
|
+
private readonly _filePath;
|
|
1346
|
+
private readonly _compress;
|
|
1347
|
+
private _bytesWritten;
|
|
1348
|
+
private _warnedAppendError;
|
|
1349
|
+
private _closed;
|
|
1350
|
+
constructor(opts: TraceWriterOptions);
|
|
1351
|
+
/** Absolute path of the trace file: `<home>/.prismer/trace/<sessionId>.jsonl.zst`. */
|
|
1352
|
+
get filePath(): string;
|
|
1353
|
+
/** Session ID this writer is bound to. */
|
|
1354
|
+
get sessionId(): string;
|
|
1355
|
+
/** Total compressed bytes appended since construction. */
|
|
1356
|
+
get bytesWritten(): number;
|
|
1357
|
+
/** True if zstd is unavailable (Node < 22); append() is a no-op. */
|
|
1358
|
+
get disabled(): boolean;
|
|
1359
|
+
/**
|
|
1360
|
+
* Synchronously append one event envelope to the trace as a new zstd frame.
|
|
1361
|
+
* Safe to call after close() — becomes a no-op. Errors are logged once and swallowed.
|
|
1362
|
+
*/
|
|
1363
|
+
append(event: EventBusEnvelope): void;
|
|
1364
|
+
/**
|
|
1365
|
+
* Close the writer. Currently a no-op because we use sync append with one
|
|
1366
|
+
* self-contained zstd frame per event. The API exists so callers can switch to
|
|
1367
|
+
* a streaming implementation later without changing integration code.
|
|
1368
|
+
*/
|
|
1369
|
+
close(): void;
|
|
1370
|
+
}
|
|
1371
|
+
interface TraceWriterManagerOptions {
|
|
1372
|
+
/** Override home directory (tests). Defaults to os.homedir(). */
|
|
1373
|
+
homeDir?: string;
|
|
1374
|
+
/** Explicit trace directory — takes precedence over homeDir when set. */
|
|
1375
|
+
traceDir?: string;
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Routes events from the EventBus (`bus.subscribe('*', ...)`) to the per-session
|
|
1379
|
+
* TraceWriter.
|
|
1380
|
+
*
|
|
1381
|
+
* agent.session.started → open writer for payload.sessionId
|
|
1382
|
+
* agent.session.ended → close + remove writer
|
|
1383
|
+
* any other event → pick the writer by payload.sessionId if present;
|
|
1384
|
+
* events without sessionId are skipped (not guessed)
|
|
1385
|
+
*/
|
|
1386
|
+
declare class TraceWriterManager {
|
|
1387
|
+
private readonly _writers;
|
|
1388
|
+
private readonly _homeDir;
|
|
1389
|
+
private readonly _traceDir;
|
|
1390
|
+
private readonly _warnedOpenFailures;
|
|
1391
|
+
constructor(opts?: TraceWriterManagerOptions);
|
|
1392
|
+
/** Event-bus subscription handler. Pass via `bus.subscribe('*', mgr.handle)`. */
|
|
1393
|
+
readonly handle: (ev: EventBusEnvelope) => void;
|
|
1394
|
+
/** Close all writers — call on daemon shutdown. */
|
|
1395
|
+
shutdown(): void;
|
|
1396
|
+
/** Test / introspection helper. */
|
|
1397
|
+
activeSessions(): string[];
|
|
1398
|
+
private _openWriter;
|
|
1399
|
+
private _closeWriter;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Prismer Runtime — E2EE Crypto Module (v1.9.0)
|
|
1404
|
+
*
|
|
1405
|
+
* End-to-end encryption for LAN direct connections.
|
|
1406
|
+
* Uses X25519 for key exchange and XSalsa20-Poly1305 for encryption.
|
|
1407
|
+
*
|
|
1408
|
+
* Security model:
|
|
1409
|
+
* - Daemon and mobile each generate X25519 keypair
|
|
1410
|
+
* - Keys are exchanged via QR code or relay (out-of-band)
|
|
1411
|
+
* - Each session derives unique encryption key via HKDF
|
|
1412
|
+
* - All LAN traffic is encrypted - relay never sees plaintext
|
|
1413
|
+
*
|
|
1414
|
+
* This implementation uses Node.js built-in crypto module for
|
|
1415
|
+
* compatibility and no external dependencies.
|
|
1416
|
+
*/
|
|
1417
|
+
interface KeyPair {
|
|
1418
|
+
publicKey: Buffer;
|
|
1419
|
+
privateKey: Buffer;
|
|
1420
|
+
}
|
|
1421
|
+
interface E2EEContext {
|
|
1422
|
+
localKeyPair: KeyPair;
|
|
1423
|
+
remotePublicKey: Buffer;
|
|
1424
|
+
sharedSecret: Buffer;
|
|
1425
|
+
sendKey: Buffer;
|
|
1426
|
+
recvKey: Buffer;
|
|
1427
|
+
sendNonce: Buffer;
|
|
1428
|
+
recvNonce: Buffer;
|
|
1429
|
+
}
|
|
1430
|
+
interface EncryptedEnvelope {
|
|
1431
|
+
version: number;
|
|
1432
|
+
nonce: Buffer;
|
|
1433
|
+
ciphertext: Buffer;
|
|
1434
|
+
authTag: Buffer;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Generate X25519 keypair for E2EE
|
|
1438
|
+
*/
|
|
1439
|
+
declare function generateKeyPair(): KeyPair;
|
|
1440
|
+
/**
|
|
1441
|
+
* Derive shared secret from local private key and remote public key (X25519)
|
|
1442
|
+
*/
|
|
1443
|
+
declare function deriveSharedSecret(localPrivateKey: Buffer, remotePublicKey: Buffer): Buffer;
|
|
1444
|
+
/**
|
|
1445
|
+
* Derive send/recv keys from shared secret using HKDF-SHA256.
|
|
1446
|
+
*
|
|
1447
|
+
* Role is determined by lexicographic comparison of the two public keys
|
|
1448
|
+
* so both peers agree without an explicit role flag. Without the keys
|
|
1449
|
+
* (legacy single-context callers), defaults to the "A" role.
|
|
1450
|
+
*/
|
|
1451
|
+
declare function deriveSessionKeys(sharedSecret: Buffer, localPublicKey?: Buffer, remotePublicKey?: Buffer): {
|
|
1452
|
+
sendKey: Buffer;
|
|
1453
|
+
recvKey: Buffer;
|
|
1454
|
+
};
|
|
1455
|
+
/**
|
|
1456
|
+
* Serialize encrypted envelope to buffer
|
|
1457
|
+
*/
|
|
1458
|
+
declare function serializeEnvelope(envelope: EncryptedEnvelope): Buffer;
|
|
1459
|
+
/**
|
|
1460
|
+
* Deserialize encrypted envelope from buffer
|
|
1461
|
+
*/
|
|
1462
|
+
declare function deserializeEnvelope(data: Buffer): EncryptedEnvelope;
|
|
1463
|
+
/**
|
|
1464
|
+
* Create E2EE context for a session
|
|
1465
|
+
*/
|
|
1466
|
+
declare function createE2EEContext(localKeyPair: KeyPair, remotePublicKey: Buffer): E2EEContext;
|
|
1467
|
+
/**
|
|
1468
|
+
* Encrypt message using E2EE context
|
|
1469
|
+
*/
|
|
1470
|
+
declare function encryptMessage(context: E2EEContext, plaintext: Buffer): Buffer;
|
|
1471
|
+
/**
|
|
1472
|
+
* Decrypt message using E2EE context
|
|
1473
|
+
*/
|
|
1474
|
+
declare function decryptMessage(context: E2EEContext, data: Buffer): Buffer;
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* Prismer Runtime — E2EE Key Storage (v1.9.0)
|
|
1478
|
+
*
|
|
1479
|
+
* Secure storage for E2EE keys on daemon side.
|
|
1480
|
+
* Uses keychain backend with encrypted JSON fallback for offline/keyless systems.
|
|
1481
|
+
*
|
|
1482
|
+
* Storage model:
|
|
1483
|
+
* - Primary: Keychain (macOS Keychain / libsecret / pass)
|
|
1484
|
+
* - Fallback: Encrypted JSON file (~/.prismer/e2ee-keys.json)
|
|
1485
|
+
* - Per-session keys with TTL (30 min default)
|
|
1486
|
+
* - Automatic cleanup of expired keys
|
|
1487
|
+
*
|
|
1488
|
+
* Security:
|
|
1489
|
+
* - Keys encrypted at rest (keychain or encrypted file)
|
|
1490
|
+
* - Master passphrase required for file fallback
|
|
1491
|
+
* - Automatic key rotation on session expiry
|
|
1492
|
+
* - Forward secrecy: ephemeral keys discarded after session
|
|
1493
|
+
*/
|
|
1494
|
+
|
|
1495
|
+
interface E2EEKeyEntry {
|
|
1496
|
+
/** Session ID (userId:daemonId or userId:deviceId) */
|
|
1497
|
+
sessionId: string;
|
|
1498
|
+
/** User ID who owns this key */
|
|
1499
|
+
userId: string;
|
|
1500
|
+
/** Daemon ID or Device ID */
|
|
1501
|
+
endpointId: string;
|
|
1502
|
+
/** Local keypair (ephemeral) */
|
|
1503
|
+
keyPair: {
|
|
1504
|
+
publicKey: string;
|
|
1505
|
+
privateKey: string;
|
|
1506
|
+
};
|
|
1507
|
+
/** Remote public key */
|
|
1508
|
+
remotePublicKey: string;
|
|
1509
|
+
/** When this key was created */
|
|
1510
|
+
createdAt: number;
|
|
1511
|
+
/** When this key expires (unix timestamp) */
|
|
1512
|
+
expiresAt: number;
|
|
1513
|
+
/** Current sequence number */
|
|
1514
|
+
seq: number;
|
|
1515
|
+
}
|
|
1516
|
+
interface E2EEStorageStats {
|
|
1517
|
+
totalKeys: number;
|
|
1518
|
+
activeKeys: number;
|
|
1519
|
+
expiredKeys: number;
|
|
1520
|
+
oldestKeyAge: number;
|
|
1521
|
+
newestKeyAge: number;
|
|
1522
|
+
}
|
|
1523
|
+
declare class E2EEKeyStorage {
|
|
1524
|
+
private keychain;
|
|
1525
|
+
private useEncryptedFile;
|
|
1526
|
+
private masterPassphrase?;
|
|
1527
|
+
private cleanupInterval?;
|
|
1528
|
+
constructor(options?: {
|
|
1529
|
+
masterPassphrase?: string;
|
|
1530
|
+
});
|
|
1531
|
+
/**
|
|
1532
|
+
* Initialize storage (try keychain, fallback to encrypted file)
|
|
1533
|
+
*/
|
|
1534
|
+
initialize(): Promise<void>;
|
|
1535
|
+
/**
|
|
1536
|
+
* Store E2EE key for a session
|
|
1537
|
+
*/
|
|
1538
|
+
storeKey(entry: E2EEKeyEntry): Promise<void>;
|
|
1539
|
+
/**
|
|
1540
|
+
* Retrieve E2EE key for a session
|
|
1541
|
+
*/
|
|
1542
|
+
getKey(sessionId: string): Promise<E2EEKeyEntry | null>;
|
|
1543
|
+
/**
|
|
1544
|
+
* Remove E2EE key for a session
|
|
1545
|
+
*/
|
|
1546
|
+
removeKey(sessionId: string): Promise<void>;
|
|
1547
|
+
/**
|
|
1548
|
+
* Get all keys for a user (for recovery)
|
|
1549
|
+
*/
|
|
1550
|
+
getAllKeysForUser(userId: string): Promise<E2EEKeyEntry[]>;
|
|
1551
|
+
/**
|
|
1552
|
+
* Get storage statistics
|
|
1553
|
+
*/
|
|
1554
|
+
getStats(userId?: string): Promise<E2EEStorageStats>;
|
|
1555
|
+
/**
|
|
1556
|
+
* Cleanup expired keys
|
|
1557
|
+
*/
|
|
1558
|
+
cleanupExpiredKeys(): Promise<void>;
|
|
1559
|
+
/**
|
|
1560
|
+
* Shutdown storage (cleanup interval)
|
|
1561
|
+
*/
|
|
1562
|
+
destroy(): void;
|
|
1563
|
+
private ensureStorageFile;
|
|
1564
|
+
private readEncryptedFile;
|
|
1565
|
+
private writeEncryptedFile;
|
|
1566
|
+
private encryptFile;
|
|
1567
|
+
private decryptFile;
|
|
1568
|
+
private storeInEncryptedFile;
|
|
1569
|
+
private getFromEncryptedFile;
|
|
1570
|
+
private removeFromEncryptedFile;
|
|
1571
|
+
private getAllFromEncryptedFile;
|
|
1572
|
+
private storeInKeychain;
|
|
1573
|
+
private getFromKeychain;
|
|
1574
|
+
private removeFromKeychain;
|
|
1575
|
+
private getAllFromKeychain;
|
|
1576
|
+
}
|
|
1577
|
+
/**
|
|
1578
|
+
* Generate session ID for E2EE storage
|
|
1579
|
+
* Format: {userId}:{endpointId}
|
|
1580
|
+
*/
|
|
1581
|
+
declare function generateSessionId(userId: string, endpointId: string): string;
|
|
1582
|
+
/**
|
|
1583
|
+
* Parse session ID into components
|
|
1584
|
+
*/
|
|
1585
|
+
declare function parseSessionId(sessionId: string): {
|
|
1586
|
+
userId: string;
|
|
1587
|
+
endpointId: string;
|
|
1588
|
+
};
|
|
1589
|
+
/**
|
|
1590
|
+
* Create E2EE key entry from runtime context
|
|
1591
|
+
*/
|
|
1592
|
+
declare function createKeyEntry(userId: string, endpointId: string, keyPair: KeyPair, remotePublicKey: string, seq?: number): E2EEKeyEntry;
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* secret-scan — local-side secret detection for memory team sync.
|
|
1596
|
+
*
|
|
1597
|
+
* Mirror of src/im/services/memory/team-sync.service.ts SECRET_PATTERNS.
|
|
1598
|
+
* Kept in sync by convention — if you add a pattern there, add it here.
|
|
1599
|
+
*
|
|
1600
|
+
* Used by memory-team-sync.ts before POSTing bytes to the server (so secrets
|
|
1601
|
+
* never leave the device). Server has its own independent scanner (defense
|
|
1602
|
+
* in depth).
|
|
1603
|
+
*
|
|
1604
|
+
* Design: docs/version190/14e-memory-cc-compat.md §8.5
|
|
1605
|
+
*/
|
|
1606
|
+
interface SecretHit {
|
|
1607
|
+
/** Pattern name (e.g. 'aws-access-key'). */
|
|
1608
|
+
pattern: string;
|
|
1609
|
+
/** Truncated match (never more than 80 chars — no full secret echo). */
|
|
1610
|
+
match: string;
|
|
1611
|
+
/** 1-based line number. */
|
|
1612
|
+
line: number;
|
|
1613
|
+
/**
|
|
1614
|
+
* If true, this hit is informational only — not strong enough evidence
|
|
1615
|
+
* to block a push on its own (e.g. JWT-shaped strings).
|
|
1616
|
+
*/
|
|
1617
|
+
warnOnly: boolean;
|
|
1618
|
+
}
|
|
1619
|
+
/** Scan content for known secret patterns. Returns the list of hits. */
|
|
1620
|
+
declare function scanForSecrets(content: string): SecretHit[];
|
|
1621
|
+
/**
|
|
1622
|
+
* Convenience: true if content has at least one blocking (non-warnOnly) hit.
|
|
1623
|
+
*/
|
|
1624
|
+
declare function hasBlockingSecret(content: string): boolean;
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* memory-team-sync — client-side delta push + server-wins pull for team memory.
|
|
1628
|
+
*
|
|
1629
|
+
* Walks a local directory (typically `~/.claude/memory/team/<owner>/<repo>/`),
|
|
1630
|
+
* diffs content hashes against a persisted `last-sync.json`, pushes only the
|
|
1631
|
+
* changed files, and writes any server-side-newer rows back to disk.
|
|
1632
|
+
*
|
|
1633
|
+
* Design: docs/version190/14e-memory-cc-compat.md §8.5
|
|
1634
|
+
*
|
|
1635
|
+
* Secret scanning (local): any file whose content matches a blocking pattern
|
|
1636
|
+
* is rejected before it leaves the device. The server also scans (defense in
|
|
1637
|
+
* depth).
|
|
1638
|
+
*/
|
|
1639
|
+
/** Per-file content size limit (matches server). */
|
|
1640
|
+
declare const MEMORY_TEAM_SYNC_MAX_BYTES: number;
|
|
1641
|
+
/** Name of the sidecar state file. */
|
|
1642
|
+
declare const LAST_SYNC_FILE = ".prismer-team-sync.json";
|
|
1643
|
+
interface SyncTeamMemoryOptions {
|
|
1644
|
+
/** Team identifier, e.g. `'acme/widgets'`. */
|
|
1645
|
+
team: string;
|
|
1646
|
+
/** Root directory to sync (scanned recursively). */
|
|
1647
|
+
rootDir: string;
|
|
1648
|
+
/** Prismer API key (Bearer). */
|
|
1649
|
+
apiKey: string;
|
|
1650
|
+
/**
|
|
1651
|
+
* Base URL for the IM API — typically the cloud host.
|
|
1652
|
+
* e.g. `'https://prismer.cloud/api/im'` or `'http://localhost:3000/api/im'`.
|
|
1653
|
+
*/
|
|
1654
|
+
baseUrl: string;
|
|
1655
|
+
/**
|
|
1656
|
+
* Optional override of the since timestamp. Normally we read this from the
|
|
1657
|
+
* sidecar state file and ignore this param. Exposed mainly for testing.
|
|
1658
|
+
*/
|
|
1659
|
+
since?: string;
|
|
1660
|
+
/**
|
|
1661
|
+
* Custom fetch (for tests). Defaults to the global fetch.
|
|
1662
|
+
*/
|
|
1663
|
+
fetchImpl?: typeof fetch;
|
|
1664
|
+
/**
|
|
1665
|
+
* If true, do not write pulled[] rows to disk and do not update sidecar.
|
|
1666
|
+
* Useful for dry-run/preflight.
|
|
1667
|
+
*/
|
|
1668
|
+
dryRun?: boolean;
|
|
1669
|
+
}
|
|
1670
|
+
interface SyncTeamMemoryResult {
|
|
1671
|
+
pushed: number;
|
|
1672
|
+
pulled: number;
|
|
1673
|
+
rejected: Array<{
|
|
1674
|
+
path: string;
|
|
1675
|
+
reason: string;
|
|
1676
|
+
detail?: string;
|
|
1677
|
+
}>;
|
|
1678
|
+
skippedLocalSecrets: Array<{
|
|
1679
|
+
path: string;
|
|
1680
|
+
pattern: string;
|
|
1681
|
+
line: number;
|
|
1682
|
+
}>;
|
|
1683
|
+
serverTime: string;
|
|
1684
|
+
}
|
|
1685
|
+
/** Walk rootDir recursively for .md files (only). Returns POSIX-style rel paths. */
|
|
1686
|
+
declare function listMarkdownFiles(rootDir: string): string[];
|
|
1687
|
+
/**
|
|
1688
|
+
* Sync a team-memory directory with the cloud.
|
|
1689
|
+
*
|
|
1690
|
+
* 1. Walk rootDir for .md files
|
|
1691
|
+
* 2. Hash each; diff against last-sync.json → push[] candidates
|
|
1692
|
+
* 3. Local secret scan → any hits are skipped (not pushed)
|
|
1693
|
+
* 4. POST /memory/team/sync with push[] and since
|
|
1694
|
+
* 5. Write pulled[] to disk (server wins)
|
|
1695
|
+
* 6. Update last-sync.json
|
|
1696
|
+
*/
|
|
1697
|
+
declare function syncTeamMemory(opts: SyncTeamMemoryOptions): Promise<SyncTeamMemoryResult>;
|
|
1698
|
+
/**
|
|
1699
|
+
* Remove the sidecar state file — forces the next sync to re-push everything.
|
|
1700
|
+
* Useful from CLI tooling.
|
|
1701
|
+
*/
|
|
1702
|
+
declare function resetTeamSyncState(rootDir: string): void;
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* Shamir Secret Sharing over GF(256) — byte-oriented implementation.
|
|
1706
|
+
*
|
|
1707
|
+
* Split a secret Buffer into N shares such that any M (threshold) shares can
|
|
1708
|
+
* reconstruct the original, but fewer than M reveal zero information.
|
|
1709
|
+
*
|
|
1710
|
+
* Field: GF(256) with AES irreducible polynomial 0x11B.
|
|
1711
|
+
* Addition = XOR. Multiplication = carryless multiply mod 0x11B.
|
|
1712
|
+
* Share-id x ∈ {1..255} (x=0 is the secret itself).
|
|
1713
|
+
*
|
|
1714
|
+
* Each byte of the secret is split independently using a polynomial of degree
|
|
1715
|
+
* m-1: P(x) = s + a_1*x + a_2*x^2 + ... + a_{m-1}*x^{m-1} (all arithmetic in GF(256))
|
|
1716
|
+
* where s is the secret byte and a_1..a_{m-1} are uniformly random.
|
|
1717
|
+
*
|
|
1718
|
+
* Recovery uses Lagrange interpolation evaluated at x=0.
|
|
1719
|
+
*
|
|
1720
|
+
* Mnemonic format: Crockford base32 with 1-byte version header (0x01) and
|
|
1721
|
+
* CRC-16/CCITT-FALSE checksum. Groups of 4 characters separated by hyphens.
|
|
1722
|
+
*
|
|
1723
|
+
* No external npm dependencies.
|
|
1724
|
+
*
|
|
1725
|
+
* See: Shamir, "How to Share a Secret", 1979. GF(256) byte-level variant used
|
|
1726
|
+
* by industry (Trezor Shamir Backup, HashiCorp Vault, etc.).
|
|
1727
|
+
*/
|
|
1728
|
+
/**
|
|
1729
|
+
* Branded `Buffer` that has been validated as a Shamir share by this module.
|
|
1730
|
+
*
|
|
1731
|
+
* Nominal typing prevents accidentally passing an arbitrary `Buffer` (e.g.
|
|
1732
|
+
* `Buffer.from('hello')`) into {@link combineShares} or
|
|
1733
|
+
* {@link encodeShareAsMnemonic}. TypeScript structural equality is defeated
|
|
1734
|
+
* by the `__brand` phantom field, so the only path to a `ShamirShare` value
|
|
1735
|
+
* is through this module's own functions (`splitSecret`,
|
|
1736
|
+
* `decodeShareFromMnemonic`) or the explicit escape hatch
|
|
1737
|
+
* {@link unsafeAsShamirShare}.
|
|
1738
|
+
*
|
|
1739
|
+
* This is a compile-time guard only; runtime validation (duplicate ids,
|
|
1740
|
+
* inconsistent lengths, CRC) still happens inside `combineShares` /
|
|
1741
|
+
* `decodeShareFromMnemonic`.
|
|
1742
|
+
*/
|
|
1743
|
+
type ShamirShare = Buffer & {
|
|
1744
|
+
readonly __brand: unique symbol;
|
|
1745
|
+
};
|
|
1746
|
+
/**
|
|
1747
|
+
* Escape hatch for interop paths that already hold raw share Buffers
|
|
1748
|
+
* (e.g. legacy callers that read bytes off the wire themselves and cannot
|
|
1749
|
+
* route through {@link decodeShareFromMnemonic}).
|
|
1750
|
+
*
|
|
1751
|
+
* This is a **sharp edge**: the caller is asserting the buffer is a
|
|
1752
|
+
* well-formed share byte string (`[share_id, y_byte_1, y_byte_2, ...]` with
|
|
1753
|
+
* `share_id ∈ 1..255`). Passing an arbitrary buffer here will compile, and
|
|
1754
|
+
* will pass length / id / duplicate checks inside `combineShares`, but will
|
|
1755
|
+
* reconstruct a plausibly-shaped yet **semantically wrong** secret.
|
|
1756
|
+
*
|
|
1757
|
+
* Prefer `decodeShareFromMnemonic` wherever possible — the CRC-16 check
|
|
1758
|
+
* there catches tampered or wrong-batch shares.
|
|
1759
|
+
*/
|
|
1760
|
+
declare function unsafeAsShamirShare(buf: Buffer): ShamirShare;
|
|
1761
|
+
/**
|
|
1762
|
+
* Split a secret into n shares; any m can recombine.
|
|
1763
|
+
*
|
|
1764
|
+
* Each returned share Buffer is [share_id_byte, y_byte_1, y_byte_2, ...].
|
|
1765
|
+
* The share-id is 1..n (never 0).
|
|
1766
|
+
*
|
|
1767
|
+
* @param secret plaintext bytes
|
|
1768
|
+
* @param n total shares (2..255)
|
|
1769
|
+
* @param m threshold (2..n)
|
|
1770
|
+
*/
|
|
1771
|
+
declare function splitSecret(secret: Buffer, n: number, m: number): ShamirShare[];
|
|
1772
|
+
/**
|
|
1773
|
+
* Combine shares to recover the secret. Any m of n suffice (where m is the
|
|
1774
|
+
* threshold used at split time).
|
|
1775
|
+
*
|
|
1776
|
+
* @param shares array of share Buffers (each [x_byte, y_bytes...])
|
|
1777
|
+
* @returns recovered secret
|
|
1778
|
+
*/
|
|
1779
|
+
declare function combineShares(shares: ShamirShare[]): Buffer;
|
|
1780
|
+
/**
|
|
1781
|
+
* Encode a share as a mnemonic string.
|
|
1782
|
+
*
|
|
1783
|
+
* Framing (before base32 encoding):
|
|
1784
|
+
* [0x01 version] [share bytes...] [CRC-16/CCITT-FALSE of (version||share)]
|
|
1785
|
+
*
|
|
1786
|
+
* Output is Crockford base32, grouped in blocks of 4 characters separated by
|
|
1787
|
+
* hyphens for readability. Case-insensitive on decode.
|
|
1788
|
+
*/
|
|
1789
|
+
declare function encodeShareAsMnemonic(share: ShamirShare): string;
|
|
1790
|
+
/**
|
|
1791
|
+
* Decode a mnemonic string back into a share Buffer. Strips whitespace,
|
|
1792
|
+
* hyphens, and normalizes case. Validates the version byte and CRC-16.
|
|
1793
|
+
*/
|
|
1794
|
+
declare function decodeShareFromMnemonic(encoded: string): ShamirShare;
|
|
1795
|
+
|
|
1796
|
+
interface TimelineEntry {
|
|
1797
|
+
seq: number;
|
|
1798
|
+
opcode: number;
|
|
1799
|
+
slot: number;
|
|
1800
|
+
payload: Buffer;
|
|
1801
|
+
createdAt: number;
|
|
1802
|
+
}
|
|
1803
|
+
interface OutboxEntry {
|
|
1804
|
+
id: number;
|
|
1805
|
+
seq: number;
|
|
1806
|
+
frame: Buffer;
|
|
1807
|
+
createdAt: number;
|
|
1808
|
+
attempts: number;
|
|
1809
|
+
}
|
|
1810
|
+
interface DeadLetterEntry {
|
|
1811
|
+
id: number;
|
|
1812
|
+
originalId: number;
|
|
1813
|
+
seq: number;
|
|
1814
|
+
createdAt: number;
|
|
1815
|
+
failedAt: number;
|
|
1816
|
+
attempts: number;
|
|
1817
|
+
}
|
|
1818
|
+
interface DroppedFrame {
|
|
1819
|
+
id: number;
|
|
1820
|
+
seq: number;
|
|
1821
|
+
attempts: number;
|
|
1822
|
+
}
|
|
1823
|
+
interface DaemonOutboxOptions {
|
|
1824
|
+
bindingId: string;
|
|
1825
|
+
dataDir?: string;
|
|
1826
|
+
timelineCap?: number;
|
|
1827
|
+
outboxCap?: number;
|
|
1828
|
+
maxAttempts?: number;
|
|
1829
|
+
onDrop?: (entries: DroppedFrame[]) => void;
|
|
1830
|
+
}
|
|
1831
|
+
declare class DaemonOutbox {
|
|
1832
|
+
private db;
|
|
1833
|
+
private bindingId;
|
|
1834
|
+
private timelineCap;
|
|
1835
|
+
private outboxCap;
|
|
1836
|
+
private maxAttempts;
|
|
1837
|
+
private onDrop?;
|
|
1838
|
+
private currentSeq;
|
|
1839
|
+
private droppedCount;
|
|
1840
|
+
constructor(opts: DaemonOutboxOptions);
|
|
1841
|
+
private initSchema;
|
|
1842
|
+
appendSent(opcode: number, slot: number, payload: Buffer): number;
|
|
1843
|
+
getTimelineSince(lastSeq: number, limit?: number): TimelineEntry[];
|
|
1844
|
+
getCurrentSeq(): number;
|
|
1845
|
+
private enforceTimelineCap;
|
|
1846
|
+
queue(seq: number, frame: Buffer): number;
|
|
1847
|
+
drain(limit?: number): OutboxEntry[];
|
|
1848
|
+
ack(outboxId: number): void;
|
|
1849
|
+
bumpAttempts(outboxId: number): {
|
|
1850
|
+
attempts: number;
|
|
1851
|
+
deadLettered: boolean;
|
|
1852
|
+
};
|
|
1853
|
+
private moveToDeadLetter;
|
|
1854
|
+
getDeadLetterCount(): number;
|
|
1855
|
+
getDeadLetterEntries(limit?: number, includeFrame?: boolean): (DeadLetterEntry & {
|
|
1856
|
+
frame?: Buffer;
|
|
1857
|
+
})[];
|
|
1858
|
+
pendingCount(): number;
|
|
1859
|
+
getDroppedCount(): number;
|
|
1860
|
+
private enforceOutboxCap;
|
|
1861
|
+
close(): void;
|
|
1862
|
+
}
|
|
1863
|
+
/** 2-byte header + payload per §5.6.4 */
|
|
1864
|
+
declare function frameFromParts(opcode: number, slot: number, payload: Buffer): Buffer;
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Prismer Runtime — WSS Relay Client (v1.9.0)
|
|
1868
|
+
*
|
|
1869
|
+
* WebSocket client for cloud relay communication. Connects to the main
|
|
1870
|
+
* cloud host (derived from `cloudApiBase`) under the `/ws/daemon/*` path
|
|
1871
|
+
* prefix — there is no separate relay subdomain.
|
|
1872
|
+
*
|
|
1873
|
+
* Responsibilities:
|
|
1874
|
+
* - Register daemon binding with API key
|
|
1875
|
+
* - Receive remote command pushes (control channel)
|
|
1876
|
+
* - Route encrypted envelopes (data channel)
|
|
1877
|
+
* - Heartbeat keepalive (30s interval)
|
|
1878
|
+
* - Auto-reconnect with exponential backoff
|
|
1879
|
+
*/
|
|
1880
|
+
|
|
1881
|
+
declare const OPCODE: {
|
|
1882
|
+
readonly JSON_CONTROL: 0;
|
|
1883
|
+
readonly AGENT_OUTPUT: 1;
|
|
1884
|
+
readonly TERMINAL_IO: 2;
|
|
1885
|
+
readonly FILE_CHUNK: 3;
|
|
1886
|
+
readonly AUDIT_TAP: 4;
|
|
1887
|
+
readonly BACKFILL_REQUEST: 5;
|
|
1888
|
+
readonly BACKFILL_CHUNK: 6;
|
|
1889
|
+
};
|
|
1890
|
+
interface RelayClientOptions {
|
|
1891
|
+
apiKey: string;
|
|
1892
|
+
daemonId: string;
|
|
1893
|
+
userId: string;
|
|
1894
|
+
/**
|
|
1895
|
+
* Base WSS URL for the relay (e.g. `wss://cloud.prismer.dev`). The client
|
|
1896
|
+
* appends `/ws/daemon/control` and `/ws/daemon/data` to this base. Callers
|
|
1897
|
+
* typically derive it from `cloudApiBase` via `deriveWsFromHttp()`.
|
|
1898
|
+
*/
|
|
1899
|
+
relayUrl: string;
|
|
1900
|
+
heartbeatIntervalMs?: number;
|
|
1901
|
+
reconnectDelayMs?: number;
|
|
1902
|
+
maxReconnectDelayMs?: number;
|
|
1903
|
+
autoReconnect?: boolean;
|
|
1904
|
+
/** v1.9.0 — bindingId enables daemon-side outbox + timeline backfill (§5.6.5). */
|
|
1905
|
+
bindingId?: string;
|
|
1906
|
+
/** Override outbox location (for tests); defaults to ~/.prismer/daemon/{bindingId}. */
|
|
1907
|
+
outboxDataDir?: string;
|
|
1908
|
+
}
|
|
1909
|
+
interface RelayState {
|
|
1910
|
+
connected: boolean;
|
|
1911
|
+
channel: 'control' | 'data' | null;
|
|
1912
|
+
lastHeartbeat?: number;
|
|
1913
|
+
reconnectAttempts: number;
|
|
1914
|
+
lastError?: string;
|
|
1915
|
+
}
|
|
1916
|
+
interface RemoteCommand {
|
|
1917
|
+
id: string;
|
|
1918
|
+
type: string;
|
|
1919
|
+
payload: Record<string, unknown>;
|
|
1920
|
+
createdAt: number;
|
|
1921
|
+
}
|
|
1922
|
+
declare class RelayClient extends EventEmitter {
|
|
1923
|
+
private apiKey;
|
|
1924
|
+
private daemonId;
|
|
1925
|
+
private userId;
|
|
1926
|
+
private relayUrl;
|
|
1927
|
+
private heartbeatIntervalMs;
|
|
1928
|
+
private reconnectDelayMs;
|
|
1929
|
+
private maxReconnectDelayMs;
|
|
1930
|
+
private autoReconnect;
|
|
1931
|
+
private controlWs;
|
|
1932
|
+
private dataWs;
|
|
1933
|
+
private heartbeatInterval?;
|
|
1934
|
+
private heartbeatTimer?;
|
|
1935
|
+
private reconnectTimer?;
|
|
1936
|
+
private state;
|
|
1937
|
+
private isShuttingDown;
|
|
1938
|
+
private outbox?;
|
|
1939
|
+
constructor(opts: RelayClientOptions);
|
|
1940
|
+
/**
|
|
1941
|
+
* Connect to relay server (both control and data channels)
|
|
1942
|
+
*/
|
|
1943
|
+
connect(): Promise<void>;
|
|
1944
|
+
/**
|
|
1945
|
+
* Disconnect from relay server
|
|
1946
|
+
*/
|
|
1947
|
+
disconnect(): Promise<void>;
|
|
1948
|
+
/**
|
|
1949
|
+
* Send command to mobile client via relay (data channel).
|
|
1950
|
+
*
|
|
1951
|
+
* v1.9.0 — if bindingId was provided, every tracked data-plane frame
|
|
1952
|
+
* (AGENT_OUTPUT / TERMINAL_IO / FILE_CHUNK) is recorded in the local
|
|
1953
|
+
* timeline before send. If the data channel is down, the frame is queued
|
|
1954
|
+
* to the outbox and will be replayed on next successful connect.
|
|
1955
|
+
*/
|
|
1956
|
+
/**
|
|
1957
|
+
* Send JSON control message to cloud via the control channel.
|
|
1958
|
+
* v1.9.x — used by daemon to ack remote commands (e.g. command.result).
|
|
1959
|
+
* Returns false silently if control channel is not connected.
|
|
1960
|
+
*/
|
|
1961
|
+
sendControl(message: unknown): boolean;
|
|
1962
|
+
sendCommand(data: Buffer | string): boolean;
|
|
1963
|
+
/**
|
|
1964
|
+
* Get current connection state
|
|
1965
|
+
*/
|
|
1966
|
+
getState(): RelayState;
|
|
1967
|
+
/**
|
|
1968
|
+
* Get connection status summary
|
|
1969
|
+
*/
|
|
1970
|
+
getStatus(): {
|
|
1971
|
+
controlConnected: boolean;
|
|
1972
|
+
dataConnected: boolean;
|
|
1973
|
+
lastHeartbeat?: number;
|
|
1974
|
+
reconnectAttempts: number;
|
|
1975
|
+
};
|
|
1976
|
+
/**
|
|
1977
|
+
* Connect to control channel (/ws/daemon/control)
|
|
1978
|
+
*/
|
|
1979
|
+
private connectControlChannel;
|
|
1980
|
+
/**
|
|
1981
|
+
* Connect to data channel (/ws/daemon/data)
|
|
1982
|
+
*/
|
|
1983
|
+
private connectDataChannel;
|
|
1984
|
+
private relayEndpoint;
|
|
1985
|
+
/**
|
|
1986
|
+
* Register daemon with relay server
|
|
1987
|
+
*/
|
|
1988
|
+
private registerDaemon;
|
|
1989
|
+
/**
|
|
1990
|
+
* Handle incoming control channel messages
|
|
1991
|
+
*/
|
|
1992
|
+
private handleControlMessage;
|
|
1993
|
+
/** RPC method handlers registered by the daemon process. Keyed by method
|
|
1994
|
+
* name (e.g. 'fs.read'). Result is serialized into the rpc.response
|
|
1995
|
+
* envelope. Thrown errors → `error` field. */
|
|
1996
|
+
private rpcHandlers;
|
|
1997
|
+
/** Register an RPC handler. Daemon code calls this at startup for each
|
|
1998
|
+
* method it wants to expose to cloud relay (mobile → cloud → daemon). */
|
|
1999
|
+
registerRpcHandler(method: string, handler: (params: unknown) => Promise<unknown>): void;
|
|
2000
|
+
private handleRpcRequest;
|
|
2001
|
+
private sendRpcResponse;
|
|
2002
|
+
/**
|
|
2003
|
+
* Handle incoming data channel messages.
|
|
2004
|
+
*
|
|
2005
|
+
* v1.9.0 — intercept backfill-request (opcode 0x05) from the cloud
|
|
2006
|
+
* (cloud forwards it on behalf of a reconnecting mobile client) and
|
|
2007
|
+
* respond with backfill-chunk frames (opcode 0x06) for every timeline
|
|
2008
|
+
* entry with seq > payload.lastSeq.
|
|
2009
|
+
*/
|
|
2010
|
+
private handleDataMessage;
|
|
2011
|
+
/**
|
|
2012
|
+
* Drain outbox by replaying queued frames over the data channel.
|
|
2013
|
+
* Called after reconnect() succeeds. Stops on the first send failure
|
|
2014
|
+
* to preserve ordering.
|
|
2015
|
+
*/
|
|
2016
|
+
private replayOutbox;
|
|
2017
|
+
/** Pending count for status reporting. */
|
|
2018
|
+
getOutboxPending(): number;
|
|
2019
|
+
/**
|
|
2020
|
+
* Handle remote command from mobile
|
|
2021
|
+
*/
|
|
2022
|
+
private handleRemoteCommand;
|
|
2023
|
+
/**
|
|
2024
|
+
* Start heartbeat interval
|
|
2025
|
+
*/
|
|
2026
|
+
private startHeartbeat;
|
|
2027
|
+
/**
|
|
2028
|
+
* Clear heartbeat timers
|
|
2029
|
+
*/
|
|
2030
|
+
private clearHeartbeat;
|
|
2031
|
+
/**
|
|
2032
|
+
* Send heartbeat to relay
|
|
2033
|
+
*/
|
|
2034
|
+
private sendHeartbeat;
|
|
2035
|
+
/**
|
|
2036
|
+
* Acknowledge heartbeat from relay
|
|
2037
|
+
*/
|
|
2038
|
+
private sendHeartbeatAck;
|
|
2039
|
+
/**
|
|
2040
|
+
* Handle disconnection from one or both channels
|
|
2041
|
+
*/
|
|
2042
|
+
private handleDisconnect;
|
|
2043
|
+
/**
|
|
2044
|
+
* Schedule reconnection with exponential backoff
|
|
2045
|
+
*/
|
|
2046
|
+
private scheduleReconnect;
|
|
2047
|
+
/**
|
|
2048
|
+
* Clear reconnect timer
|
|
2049
|
+
*/
|
|
2050
|
+
private clearReconnectTimer;
|
|
2051
|
+
/**
|
|
2052
|
+
* Close control channel
|
|
2053
|
+
*/
|
|
2054
|
+
private closeControlChannel;
|
|
2055
|
+
/**
|
|
2056
|
+
* Close data channel
|
|
2057
|
+
*/
|
|
2058
|
+
private closeDataChannel;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
declare const RUNTIME_VERSION = "1.9.0";
|
|
2062
|
+
|
|
2063
|
+
export { AGENT_CATALOG, type AdapterDescriptor, type AdapterDispatchInput, type AdapterDispatchResult, type AdapterImpl, AdapterRegistry, type AgentCatalogEntry, type AgentDescriptor, type AgentState, type AgentStatus, AgentSupervisor, AllProvidersFailedError, type AnalyzeRequest, type AssignTaskRequest, type AuthenticatedIdentity, type CancelTaskRequest, type CliContext, ConfigError, type CreateGeneRequest, DaemonAlreadyRunningError, type DaemonHttpOptions, DaemonHttpServer, type DaemonOptions, DaemonOutbox, type DaemonOutboxOptions, DaemonProcess, type DaemonRunnerHandle, type DaemonRunnerOptions, type DaemonState, DispatchMux, type DispatchMuxRequest, type DispatchMuxResult, type DistillationRequest, type DreamCompaction, type DreamResult, DreamScheduler, type DreamSchedulerOptions, type E2EEContext, type E2EEKeyEntry, type KeyPair as E2EEKeyPair, E2EEKeyStorage, type E2EEStorageStats, type EncryptedEnvelope, type EncryptionConfig, EventBus, type EventBusEnvelope, type EventBusOptions, EvolutionGatewayHttpHandler, type EvolutionGatewayOptions, type HookConfig, type HookEntry, type HookFormat, Keychain, type KeychainAdapter, type KeychainBackend, KeychainOperationError, type KeychainOptions, LAST_SYNC_FILE, LLMDispatcher, type LLMMessage, type LLMProvider, type LLMRequest, type LLMResponse, type ListMemoryRequest, type LoadConfigOptions, MEMORY_TEAM_SYNC_MAX_BYTES, MemoryDB, type MemoryFile, type MemoryFileVersion, type MemoryResponse, type MergeOptions, type MergeResult, type MigrateOptions, type MigrateResult, type MigrateSecretsOptions, type MigrateSecretsResult, NoKeychainBackendError, OPCODE, type OutboxEntry, type OutputMode, type PairOffer, type PairedDevice, type PrismerConfig, type ProviderStats, RUNTIME_VERSION, type RecallRequest, type RecordRequest, RelayClient, type RelayClientOptions, type RelayState, type RemoteCommand, type RouteHandler, type RouteTaskRequest, type RouteTaskResponse, type RoutingPolicy, type SecretHit, type ShamirShare, type ShutdownHandler, type SignalExtractionRequest, type StepCompletedRequest, type StepFailedRequest, type StepTimeoutRequest, type Subscription, type SubscriptionHandler, type SupervisorOptions, type SyncTeamMemoryOptions, type SyncTeamMemoryResult, type TableOptions, type TableRow, type TaskInfo, TaskRouteState, TaskRouter, type TaskRouterOptions, type TimelineEntry, TraceWriter, TraceWriterManager, type TraceWriterManagerOptions, type TraceWriterOptions, UI, type UIOptions, type WriteMemoryRequest, applyCommonFlags, assertBrandVoice, closeMemoryDB, combineShares, createCliContext, createDreamScheduler, createE2EEContext, createKeyEntry, decodeShareFromMnemonic, decrypt, decryptMessage, deriveKey, deriveSessionKeys, deriveSharedSecret, deserializeEnvelope, encodeShareAsMnemonic, encrypt, encryptMessage, frameFromParts, generateKeyPair as generateE2EEKeyPair, generateSalt, generateSessionId, getAgent, getMemoryDB, getUI, hasBlockingSecret, installHooks, listMarkdownFiles, loadConfig, mergeHooks, migrateSecrets, parseKeyringPlaceholder, parseSessionId, readHookConfig, resetTeamSyncState, rollbackHooks, runDream, scanForSecrets, serializeEnvelope, setUI, splitSecret, startDaemonRunner, syncTeamMemory, unsafeAsShamirShare, writeConfig, writeHookConfig };
|