@prismer/runtime 1.9.0 → 1.9.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/CHANGELOG.md +178 -0
- package/README.md +363 -229
- package/dist/cli.cjs +10269 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +10257 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +10603 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1643 -0
- package/dist/index.d.ts +1496 -1916
- package/dist/index.js +9702 -9177
- package/dist/index.js.map +1 -1
- package/package.json +31 -33
- package/dist/adapter-registry-EMZFKFYK.mjs +0 -8
- package/dist/adapter-registry-EMZFKFYK.mjs.map +0 -1
- package/dist/artifacts-uploader-N2HNBSTW.mjs +0 -159
- package/dist/artifacts-uploader-N2HNBSTW.mjs.map +0 -1
- package/dist/auto-register-M4YSPJS6.mjs +0 -204
- package/dist/auto-register-M4YSPJS6.mjs.map +0 -1
- package/dist/bin/prismer.js +0 -14980
- package/dist/chunk-BJTO5JO5.mjs +0 -11
- package/dist/chunk-BJTO5JO5.mjs.map +0 -1
- package/dist/chunk-JIEDFDVI.mjs +0 -148
- package/dist/chunk-JIEDFDVI.mjs.map +0 -1
- package/dist/chunk-NDNX2G6O.mjs +0 -68
- package/dist/chunk-NDNX2G6O.mjs.map +0 -1
- package/dist/chunk-POWV475F.mjs +0 -45
- package/dist/chunk-POWV475F.mjs.map +0 -1
- package/dist/chunk-VTFKZAUY.mjs +0 -68
- package/dist/chunk-VTFKZAUY.mjs.map +0 -1
- package/dist/dispatch-mux-MW4HHICS.mjs +0 -8
- package/dist/dispatch-mux-MW4HHICS.mjs.map +0 -1
- package/dist/dispatch-rpc-J7H6KC2V.mjs +0 -78
- package/dist/dispatch-rpc-J7H6KC2V.mjs.map +0 -1
- package/dist/fs-rpc-OSHJZYK6.mjs +0 -58
- package/dist/fs-rpc-OSHJZYK6.mjs.map +0 -1
- package/dist/heartbeat-loop-H2LAI3V5.mjs +0 -95
- package/dist/heartbeat-loop-H2LAI3V5.mjs.map +0 -1
- package/dist/index.d.mts +0 -2063
- package/dist/index.mjs +0 -8807
- package/dist/index.mjs.map +0 -1
- package/dist/mode-b-DXJ7FJAL.mjs +0 -152
- package/dist/mode-b-DXJ7FJAL.mjs.map +0 -1
- package/dist/registry-ZYU2HDFL.mjs +0 -12
- package/dist/registry-ZYU2HDFL.mjs.map +0 -1
- /package/{dist → assets}/icon +0 -0
- /package/{dist → assets}/smallicon +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,2063 +1,1643 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { FsContext } from '@prismer/sandbox-runtime';
|
|
3
|
-
import { ParaEvent } from '@prismer/wire';
|
|
1
|
+
import { z } from 'zod';
|
|
4
2
|
import { EventEmitter } from 'node:events';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
import { Command } from 'commander';
|
|
5
6
|
|
|
6
|
-
type
|
|
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
|
-
}
|
|
7
|
+
type AdapterKind = 'long-running' | 'interactive';
|
|
62
8
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
9
|
+
* Adapter definition: how the daemon hosts one class of agent
|
|
10
|
+
* (Hermes, Claude Code, OpenClaw, ...).
|
|
11
|
+
*
|
|
12
|
+
* One Adapter ←(1:N)─ Agent ←(1:N)─ AgentProfile
|
|
13
|
+
*
|
|
14
|
+
* Long-running adapters implement {@link AdapterDef.ensureService} and
|
|
15
|
+
* return a reusable {@link AdapterService} (e.g. HTTP client to
|
|
16
|
+
* `hermes gateway`). Interactive adapters implement
|
|
17
|
+
* {@link AdapterDef.dispatch} and spawn a fresh subprocess per task
|
|
18
|
+
* (e.g. `claude --headless`).
|
|
65
19
|
*/
|
|
66
|
-
|
|
67
|
-
|
|
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;
|
|
20
|
+
interface AdapterDef {
|
|
21
|
+
/** Unique name, e.g. 'hermes', 'claude-code'. Matches `IMAgentCard.adapterName`. */
|
|
97
22
|
name: string;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
23
|
+
/** Long-running services expose ensureService; interactive adapters expose dispatch. */
|
|
24
|
+
kind: AdapterKind;
|
|
25
|
+
/** Capability tags surfaced to mobile (e.g. ['shell', 'code', 'mcp']). */
|
|
26
|
+
capabilities: string[];
|
|
27
|
+
/** Zod schema validating `AgentProfile.config` for this adapter. */
|
|
28
|
+
workspaceSchema: z.ZodSchema;
|
|
29
|
+
/** Long-running: ensure the underlying service is reachable. May spawn or just connect. */
|
|
30
|
+
ensureService?(profile: AgentProfile): Promise<AdapterService>;
|
|
31
|
+
/** Interactive: one-shot dispatch (spawn-per-task). */
|
|
32
|
+
dispatch?(profile: AgentProfile, task: TaskInput): Promise<TaskResult>;
|
|
33
|
+
/** Validate config before persisting `AgentProfile.config`. */
|
|
34
|
+
validate(config: unknown): ValidationResult;
|
|
35
|
+
/** Health probe — binary in PATH, HTTP endpoint reachable, etc. */
|
|
36
|
+
health(): Promise<HealthStatus>;
|
|
111
37
|
}
|
|
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
38
|
/**
|
|
159
|
-
*
|
|
39
|
+
* AgentProfile mirrors the cloud `im_agent_profiles` row.
|
|
160
40
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* (dispatch-mux.ts) so the same registry can drive multiple selectors
|
|
164
|
-
* (capability-based, name-based, weighted load, etc.) without coupling.
|
|
41
|
+
* Field names match Track A's `IMAgentProfile` Prisma model + `AgentProfileDTO`
|
|
42
|
+
* in `src/im/api/agent-profiles.ts` (m1, merged in d5186ca / a4b3d89).
|
|
165
43
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
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.
|
|
44
|
+
* Daemon-local SQLite mirror columns use snake_case
|
|
45
|
+
* (workspace_id / agent_im_user_id) per docs/refactor/04-daemon-runtime.md
|
|
46
|
+
* §SQLite schema; the TS types stay camelCase across the boundary.
|
|
179
47
|
*/
|
|
180
|
-
interface
|
|
181
|
-
|
|
48
|
+
interface AgentProfile {
|
|
49
|
+
id: string;
|
|
50
|
+
workspaceId: string;
|
|
51
|
+
agentImUserId: string;
|
|
52
|
+
adapterName: string;
|
|
182
53
|
name: string;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
|
|
54
|
+
config: Record<string, unknown>;
|
|
55
|
+
version: number;
|
|
56
|
+
createdAt: Date;
|
|
57
|
+
updatedAt: Date;
|
|
58
|
+
/**
|
|
59
|
+
* IMUser.username of the agent bound to this profile. Used by long-running
|
|
60
|
+
* adapters (e.g. Hermes) to derive a stable, human-readable per-agent
|
|
61
|
+
* profile directory and to scope MCP env so per-call agent identity is not
|
|
62
|
+
* lost when multiple agents share a daemon. Parallel task F1 populates this
|
|
63
|
+
* on the cloud DTO; runtime treats it as optional to stay backwards-compat
|
|
64
|
+
* with snapshots produced before F1 lands.
|
|
65
|
+
*/
|
|
66
|
+
agentUsername?: string;
|
|
189
67
|
}
|
|
190
|
-
interface
|
|
191
|
-
/** Cloud task ID (im_tasks.id). */
|
|
68
|
+
interface TaskInput {
|
|
192
69
|
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
70
|
prompt: string;
|
|
199
|
-
/** Free-form metadata passed through to the adapter. */
|
|
200
71
|
metadata?: Record<string, unknown>;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
72
|
+
timeoutMs?: number;
|
|
73
|
+
onProgress?: (p: {
|
|
74
|
+
progress: number;
|
|
75
|
+
message?: string;
|
|
76
|
+
detail?: Record<string, unknown>;
|
|
77
|
+
}) => void;
|
|
78
|
+
signal?: AbortSignal;
|
|
79
|
+
}
|
|
80
|
+
interface TaskResult {
|
|
205
81
|
ok: boolean;
|
|
206
|
-
/** Output text (stdout, summary, etc.). */
|
|
207
82
|
output?: string;
|
|
208
|
-
|
|
209
|
-
|
|
83
|
+
error?: {
|
|
84
|
+
code: string;
|
|
85
|
+
message: string;
|
|
86
|
+
};
|
|
210
87
|
artifacts?: Array<{
|
|
211
|
-
|
|
212
|
-
|
|
88
|
+
kind: string;
|
|
89
|
+
storageUri: string;
|
|
213
90
|
mime?: string;
|
|
91
|
+
size?: number;
|
|
214
92
|
}>;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
93
|
+
metrics?: {
|
|
94
|
+
tokensUsed?: number;
|
|
95
|
+
durationMs?: number;
|
|
96
|
+
};
|
|
97
|
+
/** Adapter-private metadata used by daemon-side bridges/observability. */
|
|
218
98
|
metadata?: Record<string, unknown>;
|
|
219
99
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Reusable handle to a long-running adapter's underlying service.
|
|
102
|
+
* For Hermes this wraps an HTTP client to `hermes gateway`; for OpenClaw
|
|
103
|
+
* (1.9.5+) it wraps an in-process TS worker.
|
|
104
|
+
*/
|
|
105
|
+
interface AdapterService {
|
|
106
|
+
/** Service-specific identifier (e.g. 'http://127.0.0.1:8642' for hermes). */
|
|
107
|
+
id: string;
|
|
108
|
+
dispatch(task: TaskInput): Promise<TaskResult>;
|
|
109
|
+
healthy(): Promise<boolean>;
|
|
110
|
+
/** Optional: stop the service. May be no-op if user owns lifecycle. */
|
|
111
|
+
shutdown?(): Promise<void>;
|
|
112
|
+
/** Optional: subscribe to crash events. */
|
|
113
|
+
on?(event: 'crash', cb: (err: Error) => void): void;
|
|
114
|
+
}
|
|
115
|
+
interface ValidationResult {
|
|
116
|
+
ok: boolean;
|
|
117
|
+
errors?: string[];
|
|
248
118
|
}
|
|
119
|
+
interface HealthStatus {
|
|
120
|
+
available: boolean;
|
|
121
|
+
reason?: string;
|
|
122
|
+
hint?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Process-wide adapter registry.
|
|
127
|
+
*
|
|
128
|
+
* Maps `adapter.name` → {@link AdapterDef}. Selection policy lives in
|
|
129
|
+
* `daemon/dispatch.ts`; the registry is just a typed Map.
|
|
130
|
+
*
|
|
131
|
+
* Shell cherry-picked (conceptually, not via cp) from
|
|
132
|
+
* `feat/release190-milestone:sdk/prismer-cloud/runtime/src/adapter-registry.ts`.
|
|
133
|
+
* The registered type was swapped `AdapterImpl` → `AdapterDef` per the
|
|
134
|
+
* 1.9.x adapter contract; `tiersSupported` (PARA tier) is dropped — 1.9.x
|
|
135
|
+
* uses capability tags only.
|
|
136
|
+
*/
|
|
249
137
|
declare class AdapterRegistry {
|
|
250
138
|
private readonly adapters;
|
|
251
|
-
register(adapter:
|
|
139
|
+
register(adapter: AdapterDef): void;
|
|
252
140
|
unregister(name: string): boolean;
|
|
253
141
|
has(name: string): boolean;
|
|
254
|
-
get(name: string):
|
|
255
|
-
list():
|
|
142
|
+
get(name: string): AdapterDef | undefined;
|
|
143
|
+
list(): AdapterDef[];
|
|
256
144
|
size(): number;
|
|
257
145
|
/**
|
|
258
|
-
* Find adapters that
|
|
146
|
+
* Find adapters that satisfy a capability tag.
|
|
259
147
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
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.
|
|
148
|
+
* Match rules (deterministic, sorted by name):
|
|
149
|
+
* - exact: adapter declared the tag verbatim
|
|
150
|
+
* - wildcard: adapter declared `code.*` → matches `code.write`
|
|
268
151
|
*/
|
|
269
|
-
|
|
152
|
+
findByCapability(capability: string): AdapterDef[];
|
|
270
153
|
}
|
|
271
154
|
|
|
155
|
+
declare const HermesProfileConfigSchema: z.ZodObject<{
|
|
156
|
+
/** Hermes profile name (default: AgentProfile.id slice). */
|
|
157
|
+
hermesProfileName: z.ZodOptional<z.ZodString>;
|
|
158
|
+
/** Hermes API server port. Distinct profiles use distinct ports to avoid clashes. */
|
|
159
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
160
|
+
/** Bearer token from `~/.hermes/.env API_SERVER_KEY`. */
|
|
161
|
+
apiKey: z.ZodString;
|
|
162
|
+
/** Auto-spawn `hermes -p <name> gateway run` if not reachable. Default false. */
|
|
163
|
+
autoStart: z.ZodDefault<z.ZodBoolean>;
|
|
164
|
+
/** Wait timeout when autoStart=true. */
|
|
165
|
+
startupTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
166
|
+
/**
|
|
167
|
+
* Configure Hermes' inference provider to use Prismer Cloud's existing
|
|
168
|
+
* OpenAI-compatible provider endpoint before starting the gateway.
|
|
169
|
+
*/
|
|
170
|
+
configurePrismerProvider: z.ZodDefault<z.ZodBoolean>;
|
|
171
|
+
/**
|
|
172
|
+
* Auto-install the @prismer/mcp-server stdio MCP server into the Hermes
|
|
173
|
+
* profile so the long-running role agent can drive Workspace tasks
|
|
174
|
+
* (create / update / approve / move) through tool calls. Default true.
|
|
175
|
+
*/
|
|
176
|
+
installPrismerMcpServer: z.ZodDefault<z.ZodBoolean>;
|
|
177
|
+
/**
|
|
178
|
+
* Override the absolute path to @prismer/mcp-server's `dist/index.js`.
|
|
179
|
+
* If unset the adapter resolves it via Node's module resolver from the
|
|
180
|
+
* runtime install location, then falls back to PRISMER_MCP_SERVER env.
|
|
181
|
+
*/
|
|
182
|
+
prismerMcpServerPath: z.ZodOptional<z.ZodString>;
|
|
183
|
+
/** Model sent to Prismer's /api/v1/chat/completions endpoint. */
|
|
184
|
+
model: z.ZodDefault<z.ZodString>;
|
|
185
|
+
/** Named custom provider written into Hermes config.yaml. */
|
|
186
|
+
prismerProviderName: z.ZodDefault<z.ZodString>;
|
|
187
|
+
/** Override cloud provider base. Defaults to PRISMER_BASE_URL + /api/v1. */
|
|
188
|
+
prismerProviderBaseUrl: z.ZodOptional<z.ZodString>;
|
|
189
|
+
/** Env key Hermes reads from profile .env for the Prismer API key. */
|
|
190
|
+
prismerApiKeyEnv: z.ZodDefault<z.ZodString>;
|
|
191
|
+
/**
|
|
192
|
+
* Mirror Prismer work_item projections into Hermes native Kanban as triage
|
|
193
|
+
* cards. Triage avoids duplicate execution; Prismer's agent_run remains the
|
|
194
|
+
* executable source of truth.
|
|
195
|
+
*/
|
|
196
|
+
mirrorNativeKanban: z.ZodDefault<z.ZodBoolean>;
|
|
197
|
+
/**
|
|
198
|
+
* Mirror Prismer standing-objective IMTask projections into Hermes' native
|
|
199
|
+
* per-session GoalManager state (`state.db.state_meta["goal:<session_id>"]`).
|
|
200
|
+
* Hermes has no public goals REST/CLI surface; this writes the documented
|
|
201
|
+
* native state key directly and records the exact bridge result on the task.
|
|
202
|
+
*/
|
|
203
|
+
mirrorNativeGoals: z.ZodDefault<z.ZodBoolean>;
|
|
204
|
+
/**
|
|
205
|
+
* Optional checkout path for Hermes Agent source. Used only as a fallback
|
|
206
|
+
* when the installed `hermes` binary is older than the native Kanban CLI
|
|
207
|
+
* surface but the local source tree contains hermes_cli/kanban_db.py.
|
|
208
|
+
*/
|
|
209
|
+
hermesSourceDir: z.ZodOptional<z.ZodString>;
|
|
210
|
+
nativeMirrorTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
211
|
+
}, "strip", z.ZodTypeAny, {
|
|
212
|
+
port: number;
|
|
213
|
+
apiKey: string;
|
|
214
|
+
autoStart: boolean;
|
|
215
|
+
startupTimeoutMs: number;
|
|
216
|
+
configurePrismerProvider: boolean;
|
|
217
|
+
installPrismerMcpServer: boolean;
|
|
218
|
+
model: string;
|
|
219
|
+
prismerProviderName: string;
|
|
220
|
+
prismerApiKeyEnv: string;
|
|
221
|
+
mirrorNativeKanban: boolean;
|
|
222
|
+
mirrorNativeGoals: boolean;
|
|
223
|
+
nativeMirrorTimeoutMs: number;
|
|
224
|
+
hermesProfileName?: string | undefined;
|
|
225
|
+
prismerMcpServerPath?: string | undefined;
|
|
226
|
+
prismerProviderBaseUrl?: string | undefined;
|
|
227
|
+
hermesSourceDir?: string | undefined;
|
|
228
|
+
}, {
|
|
229
|
+
apiKey: string;
|
|
230
|
+
hermesProfileName?: string | undefined;
|
|
231
|
+
port?: number | undefined;
|
|
232
|
+
autoStart?: boolean | undefined;
|
|
233
|
+
startupTimeoutMs?: number | undefined;
|
|
234
|
+
configurePrismerProvider?: boolean | undefined;
|
|
235
|
+
installPrismerMcpServer?: boolean | undefined;
|
|
236
|
+
prismerMcpServerPath?: string | undefined;
|
|
237
|
+
model?: string | undefined;
|
|
238
|
+
prismerProviderName?: string | undefined;
|
|
239
|
+
prismerProviderBaseUrl?: string | undefined;
|
|
240
|
+
prismerApiKeyEnv?: string | undefined;
|
|
241
|
+
mirrorNativeKanban?: boolean | undefined;
|
|
242
|
+
mirrorNativeGoals?: boolean | undefined;
|
|
243
|
+
hermesSourceDir?: string | undefined;
|
|
244
|
+
nativeMirrorTimeoutMs?: number | undefined;
|
|
245
|
+
}>;
|
|
246
|
+
type HermesProfileConfig = z.infer<typeof HermesProfileConfigSchema>;
|
|
247
|
+
declare const hermesAdapter: AdapterDef;
|
|
248
|
+
|
|
249
|
+
interface WsClientOptions {
|
|
250
|
+
/** Cloud WS URL, e.g. `wss://cloud.prismer.dev/ws` or `ws://127.0.0.1:3000/ws`. */
|
|
251
|
+
url: string;
|
|
252
|
+
/** API key passed as `?token=<apiKey>` (matches 1.7.x token query auth). */
|
|
253
|
+
apiKey: string;
|
|
254
|
+
/** Initial reconnect delay (ms). Defaults to 1000. */
|
|
255
|
+
reconnectInitialMs?: number;
|
|
256
|
+
/** Reconnect cap (ms). Defaults to 60_000. */
|
|
257
|
+
reconnectMaxMs?: number;
|
|
258
|
+
/** Max consecutive reconnect attempts before entering degraded mode. Defaults to 30. */
|
|
259
|
+
maxReconnectAttempts?: number;
|
|
260
|
+
/** How long to stay in degraded mode before retrying (ms). Defaults to 5 minutes. */
|
|
261
|
+
degradedCooldownMs?: number;
|
|
262
|
+
}
|
|
263
|
+
/** Subset of close codes we special-case. */
|
|
264
|
+
declare const WS_CLOSE: {
|
|
265
|
+
/** RFC 6455 normal closure — no reconnect. */
|
|
266
|
+
readonly NORMAL: 1000;
|
|
267
|
+
/** Custom auth failure — no auto-reconnect; surface to user. */
|
|
268
|
+
readonly AUTH: 4001;
|
|
269
|
+
};
|
|
272
270
|
/**
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
271
|
+
* Events emitted:
|
|
272
|
+
* - `open` ws successfully opened
|
|
273
|
+
* - `message` (msg) incoming JSON-parsed message
|
|
274
|
+
* - `close` (code, reason) connection closed (auto-reconnect happens after this)
|
|
275
|
+
* - `error` (err) transport error or invalid JSON received
|
|
276
|
+
* - `drop` (msg) send() called when not OPEN; caller may enqueue
|
|
277
|
+
* - `auth-failed` close code 4001 — user must rerun setup; no auto-reconnect
|
|
278
|
+
* - `degraded` max reconnect attempts hit; pausing for cooldown
|
|
279
|
+
* - `reconnect-scheduled` (ms) reconnect timer set
|
|
280
|
+
*/
|
|
281
|
+
declare class WsClient extends EventEmitter {
|
|
282
|
+
private opts;
|
|
283
|
+
private ws?;
|
|
284
|
+
private reconnectMs;
|
|
285
|
+
private reconnectAttempts;
|
|
286
|
+
private closed;
|
|
287
|
+
private reconnectTimer?;
|
|
288
|
+
constructor(opts: WsClientOptions);
|
|
289
|
+
start(): void;
|
|
290
|
+
/** Send a JSON-serializable message. Caller is responsible for envelope construction. */
|
|
291
|
+
send(msg: unknown): void;
|
|
292
|
+
/** Close intentionally — no reconnect. */
|
|
293
|
+
close(code?: 1000): void;
|
|
294
|
+
isOpen(): boolean;
|
|
295
|
+
private connect;
|
|
296
|
+
private scheduleReconnect;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
type LocalDb = Database.Database;
|
|
300
|
+
/**
|
|
301
|
+
* Open `~/.prismer/local.db` (or any path), enable WAL, run migrations.
|
|
302
|
+
* Idempotent — safe to call multiple times.
|
|
286
303
|
*
|
|
287
|
-
*
|
|
288
|
-
* with `ok=false` so the cloud-side TaskRouter can report `step_failed`
|
|
289
|
-
* instead of seeing a daemon-side stack trace.
|
|
304
|
+
* Pass `:memory:` for in-test usage.
|
|
290
305
|
*/
|
|
306
|
+
declare function openLocalDb(path: string): LocalDb;
|
|
307
|
+
declare function runMigrations(db: LocalDb): void;
|
|
308
|
+
declare function currentSchemaVersion(db: LocalDb): number;
|
|
309
|
+
declare const TARGET_SCHEMA_VERSION = 2;
|
|
291
310
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
311
|
+
type SyncResourceType = 'workspace' | 'agent' | 'agent_profile';
|
|
312
|
+
type SyncOperation = 'create' | 'update' | 'delete';
|
|
313
|
+
type SyncStatus = 'pending' | 'failed_conflict' | 'failed_other';
|
|
314
|
+
interface SyncQueueRow {
|
|
315
|
+
id: number;
|
|
316
|
+
resource_type: SyncResourceType;
|
|
317
|
+
resource_id: string;
|
|
318
|
+
operation: SyncOperation;
|
|
319
|
+
payload: string;
|
|
320
|
+
attempt_count: number;
|
|
321
|
+
last_attempt_at: number | null;
|
|
322
|
+
next_attempt_at: number;
|
|
323
|
+
status: SyncStatus;
|
|
324
|
+
}
|
|
325
|
+
declare function nextBackoffMs(attemptCount: number): number;
|
|
326
|
+
declare class SyncQueue {
|
|
327
|
+
private db;
|
|
328
|
+
constructor(db: LocalDb);
|
|
329
|
+
enqueue(input: {
|
|
330
|
+
resourceType: SyncResourceType;
|
|
331
|
+
resourceId: string;
|
|
332
|
+
operation: SyncOperation;
|
|
333
|
+
payload: unknown;
|
|
334
|
+
/** Defaults to now (ready immediately). */
|
|
335
|
+
runAt?: number;
|
|
336
|
+
}): number;
|
|
337
|
+
/** Take up to `limit` rows whose next_attempt_at <= now and status='pending'. Oldest first. */
|
|
338
|
+
dequeueBatch(limit: number, now?: number): SyncQueueRow[];
|
|
339
|
+
markCompleted(id: number): void;
|
|
340
|
+
markConflict(id: number): void;
|
|
341
|
+
/** Increment attempt + reschedule via backoff. Caller passes the current attempt_count. */
|
|
342
|
+
markBackoff(id: number, currentAttemptCount: number): void;
|
|
343
|
+
/** Permanent failure (4xx other than 409). */
|
|
344
|
+
markFailedOther(id: number): void;
|
|
345
|
+
pendingCount(): number;
|
|
346
|
+
/** All rows for a given resource (debugging / CLI status). */
|
|
347
|
+
listForResource(type: SyncResourceType, id: string): SyncQueueRow[];
|
|
295
348
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
349
|
+
|
|
350
|
+
interface FlushResult {
|
|
351
|
+
ok: boolean;
|
|
352
|
+
/** HTTP status when applicable. 409 → markConflict; 400-499 (other) → markFailedOther; else markBackoff. */
|
|
353
|
+
status?: number;
|
|
354
|
+
message?: string;
|
|
355
|
+
}
|
|
356
|
+
/** Caller injects the actual cloud HTTP push (m3). Scaffold uses a noop that returns ok:true. */
|
|
357
|
+
type FlushFn = (row: SyncQueueRow) => Promise<FlushResult>;
|
|
358
|
+
interface SyncWorkerOptions {
|
|
359
|
+
queue: SyncQueue;
|
|
360
|
+
flush: FlushFn;
|
|
361
|
+
/** Tick interval in ms. Defaults to 5000. */
|
|
362
|
+
tickMs?: number;
|
|
363
|
+
/** Max rows per tick. Defaults to 10. */
|
|
364
|
+
batchSize?: number;
|
|
300
365
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
366
|
+
/**
|
|
367
|
+
* Events:
|
|
368
|
+
* - `tick` ({ pending, processed }) each completed tick
|
|
369
|
+
* - `flushed` (row, result) one row push outcome
|
|
370
|
+
* - `error` (err, row?) unexpected error in flush()
|
|
371
|
+
*/
|
|
372
|
+
declare class SyncWorker extends EventEmitter {
|
|
373
|
+
private opts;
|
|
374
|
+
private timer?;
|
|
375
|
+
private running;
|
|
376
|
+
constructor(opts: SyncWorkerOptions);
|
|
377
|
+
start(): void;
|
|
378
|
+
stop(): void;
|
|
304
379
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
* Useful for cloud-side capability probing.
|
|
380
|
+
* Drain up to `batchSize` ready rows. Public for test injection.
|
|
381
|
+
* Re-entrant guarded — concurrent ticks return immediately.
|
|
308
382
|
*/
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
383
|
+
tick(): Promise<{
|
|
384
|
+
processed: number;
|
|
385
|
+
}>;
|
|
386
|
+
private handleResult;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
interface RoleTemplate {
|
|
390
|
+
templateName: string;
|
|
391
|
+
displayName: string;
|
|
392
|
+
description: string;
|
|
393
|
+
applicableAdapters: string[];
|
|
394
|
+
configSchema: Record<string, unknown>;
|
|
395
|
+
}
|
|
396
|
+
declare const BUILTIN_ROLE_TEMPLATES: ReadonlyArray<RoleTemplate>;
|
|
397
|
+
declare function getRoleTemplate(name: string): RoleTemplate | undefined;
|
|
398
|
+
declare function listRoleTemplates(): ReadonlyArray<{
|
|
399
|
+
name: string;
|
|
400
|
+
displayName: string;
|
|
401
|
+
description: string;
|
|
402
|
+
}>;
|
|
403
|
+
|
|
404
|
+
declare const ConfigSchema: z.ZodObject<{
|
|
405
|
+
/** API key from `prismer setup`, or env override. */
|
|
406
|
+
api_key: z.ZodString;
|
|
407
|
+
/** Cloud REST + WS base; `ws://` is derived by stripping `http`. */
|
|
408
|
+
cloud_api_base: z.ZodString;
|
|
409
|
+
/** Stable per-machine daemon identifier. Generated once on first setup. */
|
|
410
|
+
daemon_id: z.ZodString;
|
|
411
|
+
/** Optional adapter-specific overrides keyed by adapter name. */
|
|
412
|
+
adapters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
413
|
+
/** Local daemon shell execution. Default disabled. */
|
|
414
|
+
shell: z.ZodOptional<z.ZodObject<{
|
|
415
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
416
|
+
default_cwd: z.ZodOptional<z.ZodString>;
|
|
417
|
+
defaultCwd: z.ZodOptional<z.ZodString>;
|
|
418
|
+
shell: z.ZodOptional<z.ZodEnum<["bash", "zsh", "sh"]>>;
|
|
419
|
+
max_timeout_ms: z.ZodOptional<z.ZodNumber>;
|
|
420
|
+
maxTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
421
|
+
max_output_bytes: z.ZodOptional<z.ZodNumber>;
|
|
422
|
+
maxOutputBytes: z.ZodOptional<z.ZodNumber>;
|
|
423
|
+
allowed_workspaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
424
|
+
allowedWorkspaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
425
|
+
}, "strip", z.ZodTypeAny, {
|
|
426
|
+
enabled: boolean;
|
|
427
|
+
shell?: "bash" | "zsh" | "sh" | undefined;
|
|
428
|
+
default_cwd?: string | undefined;
|
|
429
|
+
defaultCwd?: string | undefined;
|
|
430
|
+
max_timeout_ms?: number | undefined;
|
|
431
|
+
maxTimeoutMs?: number | undefined;
|
|
432
|
+
max_output_bytes?: number | undefined;
|
|
433
|
+
maxOutputBytes?: number | undefined;
|
|
434
|
+
allowed_workspaces?: string[] | undefined;
|
|
435
|
+
allowedWorkspaces?: string[] | undefined;
|
|
436
|
+
}, {
|
|
437
|
+
shell?: "bash" | "zsh" | "sh" | undefined;
|
|
438
|
+
enabled?: boolean | undefined;
|
|
439
|
+
default_cwd?: string | undefined;
|
|
440
|
+
defaultCwd?: string | undefined;
|
|
441
|
+
max_timeout_ms?: number | undefined;
|
|
442
|
+
maxTimeoutMs?: number | undefined;
|
|
443
|
+
max_output_bytes?: number | undefined;
|
|
444
|
+
maxOutputBytes?: number | undefined;
|
|
445
|
+
allowed_workspaces?: string[] | undefined;
|
|
446
|
+
allowedWorkspaces?: string[] | undefined;
|
|
447
|
+
}>>;
|
|
448
|
+
/** Local cache settings. */
|
|
449
|
+
cache: z.ZodOptional<z.ZodObject<{
|
|
450
|
+
max_bytes: z.ZodDefault<z.ZodNumber>;
|
|
451
|
+
}, "strip", z.ZodTypeAny, {
|
|
452
|
+
max_bytes: number;
|
|
453
|
+
}, {
|
|
454
|
+
max_bytes?: number | undefined;
|
|
455
|
+
}>>;
|
|
456
|
+
}, "strip", z.ZodTypeAny, {
|
|
457
|
+
api_key: string;
|
|
458
|
+
cloud_api_base: string;
|
|
459
|
+
daemon_id: string;
|
|
460
|
+
shell?: {
|
|
461
|
+
enabled: boolean;
|
|
462
|
+
shell?: "bash" | "zsh" | "sh" | undefined;
|
|
463
|
+
default_cwd?: string | undefined;
|
|
464
|
+
defaultCwd?: string | undefined;
|
|
465
|
+
max_timeout_ms?: number | undefined;
|
|
466
|
+
maxTimeoutMs?: number | undefined;
|
|
467
|
+
max_output_bytes?: number | undefined;
|
|
468
|
+
maxOutputBytes?: number | undefined;
|
|
469
|
+
allowed_workspaces?: string[] | undefined;
|
|
470
|
+
allowedWorkspaces?: string[] | undefined;
|
|
471
|
+
} | undefined;
|
|
472
|
+
adapters?: Record<string, Record<string, unknown>> | undefined;
|
|
473
|
+
cache?: {
|
|
474
|
+
max_bytes: number;
|
|
314
475
|
} | undefined;
|
|
315
|
-
|
|
476
|
+
}, {
|
|
477
|
+
api_key: string;
|
|
478
|
+
cloud_api_base: string;
|
|
479
|
+
daemon_id: string;
|
|
480
|
+
shell?: {
|
|
481
|
+
shell?: "bash" | "zsh" | "sh" | undefined;
|
|
482
|
+
enabled?: boolean | undefined;
|
|
483
|
+
default_cwd?: string | undefined;
|
|
484
|
+
defaultCwd?: string | undefined;
|
|
485
|
+
max_timeout_ms?: number | undefined;
|
|
486
|
+
maxTimeoutMs?: number | undefined;
|
|
487
|
+
max_output_bytes?: number | undefined;
|
|
488
|
+
maxOutputBytes?: number | undefined;
|
|
489
|
+
allowed_workspaces?: string[] | undefined;
|
|
490
|
+
allowedWorkspaces?: string[] | undefined;
|
|
491
|
+
} | undefined;
|
|
492
|
+
adapters?: Record<string, Record<string, unknown>> | undefined;
|
|
493
|
+
cache?: {
|
|
494
|
+
max_bytes?: number | undefined;
|
|
495
|
+
} | undefined;
|
|
496
|
+
}>;
|
|
497
|
+
type Config = z.infer<typeof ConfigSchema>;
|
|
498
|
+
interface ConfigPaths {
|
|
499
|
+
/** Default `~/.prismer`. */
|
|
500
|
+
root: string;
|
|
501
|
+
configFile: string;
|
|
502
|
+
localDb: string;
|
|
503
|
+
cacheDir: string;
|
|
504
|
+
logsDir: string;
|
|
505
|
+
/**
|
|
506
|
+
* Per-task run scratch dirs. Each chat-mention dispatch creates
|
|
507
|
+
* `${runsDir}/${taskId}/_outbox/` for adapter-produced files; the
|
|
508
|
+
* OutboxWatcher uploads from there into IMAssets.
|
|
509
|
+
*/
|
|
510
|
+
runsDir: string;
|
|
316
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Resolve paths for the prismer home directory. Honors `PRISMER_HOME` env var;
|
|
514
|
+
* defaults to `~/.prismer`.
|
|
515
|
+
*/
|
|
516
|
+
declare function resolvePaths(home?: string): ConfigPaths;
|
|
517
|
+
declare function configExists(paths?: ConfigPaths): boolean;
|
|
518
|
+
/**
|
|
519
|
+
* Read and validate config. Env vars override file values for `api_key` and
|
|
520
|
+
* `cloud_api_base` (handy in CI / dev without rewriting config.toml).
|
|
521
|
+
*/
|
|
522
|
+
declare function loadConfig(paths?: ConfigPaths): Config;
|
|
523
|
+
declare function saveConfig(config: Config, paths?: ConfigPaths): void;
|
|
524
|
+
/** Derive a `wss?://` URL from a `https?://` base. Trailing `/ws` appended. */
|
|
525
|
+
declare function deriveWsUrl(httpBase: string): string;
|
|
317
526
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
527
|
+
declare function newDaemonId(): string;
|
|
528
|
+
declare function isDaemonId(s: string): boolean;
|
|
529
|
+
|
|
530
|
+
interface CloudClientOptions {
|
|
531
|
+
baseUrl: string;
|
|
532
|
+
apiKey: string;
|
|
533
|
+
/** Per-request timeout (ms). Defaults to 30s. */
|
|
534
|
+
defaultTimeoutMs?: number;
|
|
535
|
+
/** Pluggable fetch (tests inject a stub). */
|
|
536
|
+
fetchImpl?: typeof fetch;
|
|
328
537
|
}
|
|
329
|
-
interface
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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;
|
|
538
|
+
interface CloudResponse<T> {
|
|
539
|
+
ok: boolean;
|
|
540
|
+
status: number;
|
|
541
|
+
data?: T;
|
|
542
|
+
error?: {
|
|
543
|
+
code: string;
|
|
544
|
+
message: string;
|
|
545
|
+
};
|
|
347
546
|
}
|
|
547
|
+
declare class CloudClient {
|
|
548
|
+
private readonly opts;
|
|
549
|
+
private readonly fetchImpl;
|
|
550
|
+
constructor(opts: CloudClientOptions);
|
|
551
|
+
/** Low-level request. Caller decides on retry; sync-worker handles backoff. */
|
|
552
|
+
request<T = unknown>(method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE', path: string, init?: {
|
|
553
|
+
body?: unknown;
|
|
554
|
+
headers?: Record<string, string>;
|
|
555
|
+
signal?: AbortSignal;
|
|
556
|
+
timeoutMs?: number;
|
|
557
|
+
auth?: boolean;
|
|
558
|
+
}): Promise<CloudResponse<T>>;
|
|
559
|
+
/** Convenience: GET expecting `{ ok, data }` envelope. Returns `data` or throws. */
|
|
560
|
+
get<T>(path: string, opts?: {
|
|
561
|
+
signal?: AbortSignal;
|
|
562
|
+
}): Promise<T>;
|
|
563
|
+
urlFor(path: string): string;
|
|
564
|
+
/** Raw byte download (for asset cache). Returns response so caller can stream body. */
|
|
565
|
+
fetchRaw(path: string, init?: {
|
|
566
|
+
headers?: Record<string, string>;
|
|
567
|
+
signal?: AbortSignal;
|
|
568
|
+
timeoutMs?: number;
|
|
569
|
+
}): Promise<Response>;
|
|
570
|
+
}
|
|
571
|
+
declare class CloudError extends Error {
|
|
572
|
+
readonly status: number;
|
|
573
|
+
readonly code: string;
|
|
574
|
+
constructor(status: number, code: string, message: string);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
interface Envelope<T = unknown> {
|
|
578
|
+
type: string;
|
|
579
|
+
payload: T;
|
|
580
|
+
requestId?: string;
|
|
581
|
+
timestamp: number;
|
|
582
|
+
}
|
|
583
|
+
declare function envelope<T>(type: string, payload: T, requestId?: string): Envelope<T>;
|
|
348
584
|
|
|
585
|
+
interface CachedAsset {
|
|
586
|
+
contentHash: string;
|
|
587
|
+
sizeBytes: number;
|
|
588
|
+
mime: string | null;
|
|
589
|
+
localPath: string;
|
|
590
|
+
fetchedAt: number;
|
|
591
|
+
lastUsedAt: number;
|
|
592
|
+
pin: boolean;
|
|
593
|
+
}
|
|
594
|
+
interface AssetCacheOptions {
|
|
595
|
+
db: LocalDb;
|
|
596
|
+
cloud: CloudClient;
|
|
597
|
+
/** ~/.prismer/cache. */
|
|
598
|
+
cacheDir: string;
|
|
599
|
+
/** LRU cap in bytes. Default 5 GiB. */
|
|
600
|
+
maxBytes?: number;
|
|
601
|
+
}
|
|
349
602
|
/**
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* Implements PARA event subscription, Tier-based event routing, and
|
|
353
|
-
* integration with daemon startup workflow.
|
|
603
|
+
* Local asset cache.
|
|
354
604
|
*
|
|
355
|
-
*
|
|
605
|
+
* `getOrFetch(hash, opts)` returns the local path; downloads from
|
|
606
|
+
* `GET /api/im/assets/by-hash/:hash?wsId=…` on miss. Pinned rows survive LRU.
|
|
356
607
|
*/
|
|
608
|
+
declare class AssetCache {
|
|
609
|
+
private readonly db;
|
|
610
|
+
private readonly cloud;
|
|
611
|
+
private readonly cacheDir;
|
|
612
|
+
private readonly maxBytes;
|
|
613
|
+
constructor(opts: AssetCacheOptions);
|
|
614
|
+
/** Local file path for a content hash. Files split by first 2 chars for fs scaling. */
|
|
615
|
+
pathFor(hash: string): string;
|
|
616
|
+
get(hash: string): CachedAsset | undefined;
|
|
617
|
+
/** Bump `last_used_at` (call when an asset is consulted). */
|
|
618
|
+
touch(hash: string): void;
|
|
619
|
+
pin(hash: string): void;
|
|
620
|
+
unpin(hash: string): void;
|
|
621
|
+
/**
|
|
622
|
+
* Get or fetch by hash. On miss, downloads the raw bytes and caches them.
|
|
623
|
+
*
|
|
624
|
+
* Why two endpoints: `GET /api/im/assets/by-hash/:hash` returns the JSON
|
|
625
|
+
* `AssetDTO` (id, contentHash, storageUri, …) — *metadata only*. The raw
|
|
626
|
+
* bytes live at `GET /api/im/assets/:id`. If `opts.assetId` is supplied
|
|
627
|
+
* the caller already has the id from an `AssetRef`, so we go straight to
|
|
628
|
+
* the byte endpoint; otherwise we resolve `hash → id` first (one extra
|
|
629
|
+
* round-trip on the metadata route) before downloading the body.
|
|
630
|
+
*
|
|
631
|
+
* Earlier this method called `by-hash` and treated the JSON body as the
|
|
632
|
+
* asset content — the daemon stored the 500-byte metadata blob on disk
|
|
633
|
+
* and `resolveAssetRefs` inlined that JSON into the prompt. The agent
|
|
634
|
+
* saw `{"id":"…","storageUri":"…"}` instead of the user's file and
|
|
635
|
+
* concluded the upload had failed.
|
|
636
|
+
*/
|
|
637
|
+
getOrFetch(hash: string, opts?: {
|
|
638
|
+
workspaceIdHint?: string;
|
|
639
|
+
signal?: AbortSignal;
|
|
640
|
+
assetId?: string;
|
|
641
|
+
}): Promise<CachedAsset>;
|
|
642
|
+
/** Insert an already-on-disk asset into the cache (e.g., asset just produced by adapter). */
|
|
643
|
+
registerLocal(hash: string, localPath: string, mime?: string): CachedAsset;
|
|
644
|
+
/** Total bytes across all rows (incl. pinned). */
|
|
645
|
+
totalBytes(): number;
|
|
646
|
+
/** Run LRU eviction until under cap. Pinned rows are never evicted. */
|
|
647
|
+
evictIfOver(): {
|
|
648
|
+
removed: number;
|
|
649
|
+
freed: number;
|
|
650
|
+
};
|
|
651
|
+
}
|
|
357
652
|
|
|
358
|
-
interface
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
constructor(opts: EventHandlerOptions);
|
|
653
|
+
interface WorkspaceFileBinding {
|
|
654
|
+
workspaceId: string;
|
|
655
|
+
path: string;
|
|
656
|
+
assetId: string;
|
|
657
|
+
contentHash: string;
|
|
658
|
+
version: number;
|
|
659
|
+
syncedAt: number | null;
|
|
660
|
+
dirty: boolean;
|
|
661
|
+
}
|
|
662
|
+
interface WorkspaceMirrorOptions {
|
|
663
|
+
db: LocalDb;
|
|
664
|
+
cloud: CloudClient;
|
|
665
|
+
workspaceId: string;
|
|
372
666
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
667
|
+
* Per-workspace state dir. Caller should pass ~/.prismer/<wid>/ so different
|
|
668
|
+
* paired workspaces don't race on the cursor file.
|
|
375
669
|
*/
|
|
376
|
-
|
|
670
|
+
workspaceStateDir: string;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Mirror reads cloud-side path→asset bindings into SQLite for fast local
|
|
674
|
+
* lookup. The cursor file is the only piece of state that must survive
|
|
675
|
+
* daemon restart; the SQLite mirror is rebuilt from scratch by the very
|
|
676
|
+
* first pullDelta() with no cursor.
|
|
677
|
+
*/
|
|
678
|
+
declare class WorkspaceMirror {
|
|
679
|
+
private readonly db;
|
|
680
|
+
private readonly cloud;
|
|
681
|
+
private readonly workspaceId;
|
|
682
|
+
private readonly cursorPath;
|
|
683
|
+
constructor(opts: WorkspaceMirrorOptions);
|
|
684
|
+
/** Persisted cursor for this workspace, or null on first run / corrupted file. */
|
|
685
|
+
readCursor(): string | null;
|
|
686
|
+
private writeCursor;
|
|
687
|
+
/**
|
|
688
|
+
* Pull workspace_file changes since the persisted cursor and upsert into
|
|
689
|
+
* the local mirror. Soft-deleted rows are removed from the mirror; active
|
|
690
|
+
* rows are upserted with synced_at = now and dirty = 0.
|
|
691
|
+
*
|
|
692
|
+
* Returns the count of items applied + the new cursor (may be unchanged
|
|
693
|
+
* when the cloud has nothing new). Idempotent — safe to retry.
|
|
694
|
+
*/
|
|
695
|
+
pullDelta(opts?: {
|
|
696
|
+
signal?: AbortSignal;
|
|
697
|
+
}): Promise<{
|
|
698
|
+
applied: number;
|
|
699
|
+
cursor: string | null;
|
|
700
|
+
}>;
|
|
701
|
+
/** Lookup the active binding for a workspace path. Returns undefined if not mirrored. */
|
|
702
|
+
lookupByPath(path: string): WorkspaceFileBinding | undefined;
|
|
703
|
+
/** Find an asset's contentHash via any path binding (asset may map to many paths). */
|
|
704
|
+
hashForAssetId(assetId: string): string | undefined;
|
|
705
|
+
/** All active bindings for this workspace — used by status / debug surfaces. */
|
|
706
|
+
list(): WorkspaceFileBinding[];
|
|
707
|
+
/** Wipe the mirror for this workspace (e.g., re-pair). Cursor file is left intact. */
|
|
708
|
+
clear(): void;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
interface ParseClaim {
|
|
712
|
+
id: string;
|
|
713
|
+
workspaceId: string;
|
|
714
|
+
assetId: string;
|
|
715
|
+
ingestVersion: number;
|
|
716
|
+
claimantImUserId: string;
|
|
717
|
+
claimantDeviceId: string;
|
|
718
|
+
status: 'active' | 'completed' | 'abandoned';
|
|
719
|
+
claimedAt: string;
|
|
720
|
+
heartbeatAt: string;
|
|
721
|
+
completedAt: string | null;
|
|
722
|
+
}
|
|
723
|
+
type AcquireResult = {
|
|
724
|
+
kind: 'acquired';
|
|
725
|
+
claim: ParseClaim;
|
|
726
|
+
} | {
|
|
727
|
+
kind: 'refreshed';
|
|
728
|
+
claim: ParseClaim;
|
|
729
|
+
} | {
|
|
730
|
+
kind: 'claimed_active';
|
|
731
|
+
current: ParseClaim;
|
|
732
|
+
} | {
|
|
733
|
+
kind: 'already_complete';
|
|
734
|
+
current: ParseClaim;
|
|
735
|
+
};
|
|
736
|
+
type ClaimLostReason = 'claim_taken_over' | 'claim_inactive';
|
|
737
|
+
interface ParseClaimControllerOptions {
|
|
738
|
+
cloud: CloudClient;
|
|
739
|
+
/** Daemon's persisted device id. Same value across heartbeats / completions. */
|
|
740
|
+
deviceId: string;
|
|
741
|
+
/** Heartbeat interval (ms). Default 10s — well below the 30s server TTL. */
|
|
742
|
+
heartbeatIntervalMs?: number;
|
|
743
|
+
}
|
|
744
|
+
interface HeartbeatHandle {
|
|
745
|
+
stop: () => void;
|
|
377
746
|
/**
|
|
378
|
-
*
|
|
747
|
+
* Symbol.dispose so consumers can write `using hb = ctrl.startHeartbeat(...)`
|
|
748
|
+
* once they're on TypeScript 5.2 + Node 22 explicit-resource-management.
|
|
749
|
+
* Without that runtime support the field is harmless dead weight; the
|
|
750
|
+
* explicit `stop()` is the universal contract.
|
|
379
751
|
*/
|
|
380
|
-
|
|
752
|
+
[Symbol.dispose]: () => void;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Coordinates a single daemon's asset-parse claims. Owns no global state —
|
|
756
|
+
* callers may run multiple controllers if they share the same device id.
|
|
757
|
+
*/
|
|
758
|
+
declare class ParseClaimController {
|
|
759
|
+
private readonly cloud;
|
|
760
|
+
private readonly deviceId;
|
|
761
|
+
private readonly heartbeatIntervalMs;
|
|
762
|
+
constructor(opts: ParseClaimControllerOptions);
|
|
381
763
|
/**
|
|
382
|
-
*
|
|
764
|
+
* Acquire (or refresh) a claim. The return value is a 4-variant tagged
|
|
765
|
+
* union — callers must branch on `kind`. Throws on transport / 5xx / 4xx
|
|
766
|
+
* codes that aren't part of the documented contract (e.g. `claim_race`
|
|
767
|
+
* is mapped to a thrown Error so the worker can retry).
|
|
383
768
|
*/
|
|
384
|
-
|
|
769
|
+
acquire(input: {
|
|
770
|
+
assetId: string;
|
|
771
|
+
ingestVersion: number;
|
|
772
|
+
}): Promise<AcquireResult>;
|
|
385
773
|
/**
|
|
386
|
-
*
|
|
774
|
+
* Refresh the heartbeat for an active claim. Throws `ClaimLostError` on
|
|
775
|
+
* HTTP 410 with code `claim_taken_over` or `claim_inactive` — caller
|
|
776
|
+
* should abort the parse work. Throws plain `Error` on any other failure.
|
|
387
777
|
*/
|
|
388
|
-
|
|
778
|
+
sendHeartbeat(claimId: string): Promise<ParseClaim>;
|
|
389
779
|
/**
|
|
390
|
-
*
|
|
780
|
+
* Mark a claim complete. Throws `ClaimLostError` on 410 (claim was
|
|
781
|
+
* stolen before the worker finished — output should be discarded).
|
|
391
782
|
*/
|
|
392
|
-
|
|
783
|
+
complete(claimId: string): Promise<ParseClaim>;
|
|
393
784
|
/**
|
|
394
|
-
*
|
|
785
|
+
* Start a heartbeat loop in the background. Returns a handle with `stop()`
|
|
786
|
+
* and a Symbol.dispose alias for the `using` pattern.
|
|
787
|
+
*
|
|
788
|
+
* On 410 (claim taken over or marked inactive) the loop stops itself and
|
|
789
|
+
* invokes `opts.onLost` so the parse worker can abort. Other transient
|
|
790
|
+
* errors (network blip, 5xx) are logged to stderr and the loop retries on
|
|
791
|
+
* the next tick — the server-side TTL (default 30s) will only drop us if
|
|
792
|
+
* the partition outlasts the TTL.
|
|
395
793
|
*/
|
|
396
|
-
|
|
794
|
+
startHeartbeat(claimId: string, opts?: {
|
|
795
|
+
onLost?: (reason: ClaimLostReason) => void;
|
|
796
|
+
}): HeartbeatHandle;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
type PrismerUriType = 'asset' | 'file';
|
|
800
|
+
interface ParsedPrismerUri {
|
|
801
|
+
/** Original full URI string. */
|
|
802
|
+
raw: string;
|
|
803
|
+
/**
|
|
804
|
+
* Owner segment for legacy `prismer://<owner>/...` form (informational,
|
|
805
|
+
* not ACL-checked). For `prismer://workspace/<wid>/...` this is the
|
|
806
|
+
* literal string 'workspace' — prefer `workspaceId` instead.
|
|
807
|
+
*/
|
|
808
|
+
owner: string;
|
|
809
|
+
type: PrismerUriType;
|
|
810
|
+
/** For type='asset': the sha256 hash. For legacy file URIs: '<wsId>/<path>'. */
|
|
811
|
+
rest: string;
|
|
812
|
+
/** Convenience parses for type='file' and for workspace-form 'asset'. */
|
|
813
|
+
workspaceId?: string;
|
|
814
|
+
filePath?: string;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Find every recognized `prismer://...` reference in a string.
|
|
818
|
+
* Only `asset` and `file` types are recognized — other types (memory,
|
|
819
|
+
* context, conversation, evolution, task, pair) pass through untouched.
|
|
820
|
+
*/
|
|
821
|
+
declare function parseUris(text: string): ParsedPrismerUri[];
|
|
822
|
+
interface UriResolverOptions {
|
|
823
|
+
db: LocalDb;
|
|
824
|
+
cloud: CloudClient;
|
|
825
|
+
assetCache: AssetCache;
|
|
826
|
+
}
|
|
827
|
+
declare class UriResolver {
|
|
828
|
+
private readonly db;
|
|
829
|
+
private readonly cloud;
|
|
830
|
+
private readonly assetCache;
|
|
831
|
+
constructor(opts: UriResolverOptions);
|
|
832
|
+
/**
|
|
833
|
+
* Resolve a single URI to a local file path. Throws on auth/4xx;
|
|
834
|
+
* caller decides whether to leave the URI in place (best-effort) or fail.
|
|
835
|
+
*/
|
|
836
|
+
resolveOne(uri: ParsedPrismerUri, opts?: {
|
|
837
|
+
signal?: AbortSignal;
|
|
838
|
+
}): Promise<string>;
|
|
839
|
+
/**
|
|
840
|
+
* Walk a string, replace every `prismer://(asset|file)/...` with `file://<localPath>`.
|
|
841
|
+
* Unrecognized URIs pass through. Returns rewritten text + the list of pinned hashes.
|
|
842
|
+
*/
|
|
843
|
+
rewrite(text: string, opts?: {
|
|
844
|
+
pin?: boolean;
|
|
845
|
+
signal?: AbortSignal;
|
|
846
|
+
}): Promise<{
|
|
847
|
+
text: string;
|
|
848
|
+
resolvedHashes: string[];
|
|
849
|
+
}>;
|
|
850
|
+
/** Bulk-rewrite an array of strings (e.g. context entries' content). */
|
|
851
|
+
rewriteAll(texts: string[], opts?: {
|
|
852
|
+
pin?: boolean;
|
|
853
|
+
signal?: AbortSignal;
|
|
854
|
+
}): Promise<{
|
|
855
|
+
texts: string[];
|
|
856
|
+
resolvedHashes: string[];
|
|
857
|
+
}>;
|
|
858
|
+
private lookupFile;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
type IMAgentStatus = 'online' | 'busy' | 'idle' | 'offline';
|
|
862
|
+
interface HostedAgentDeclaration {
|
|
863
|
+
imUserId: string;
|
|
864
|
+
name: string;
|
|
865
|
+
adapterName: string;
|
|
866
|
+
capabilities: string[];
|
|
867
|
+
profiles: Array<{
|
|
868
|
+
id: string;
|
|
869
|
+
version: number;
|
|
870
|
+
}>;
|
|
871
|
+
}
|
|
872
|
+
interface AgentHostDeclarePayload {
|
|
873
|
+
daemonId: string;
|
|
874
|
+
daemonVersion: string;
|
|
875
|
+
platform: 'darwin' | 'linux' | 'win32';
|
|
876
|
+
agents: HostedAgentDeclaration[];
|
|
877
|
+
}
|
|
878
|
+
interface HostAckedPayload {
|
|
879
|
+
workspaceId: string;
|
|
880
|
+
syncCursor: {
|
|
881
|
+
workspaces: number;
|
|
882
|
+
agent_profiles: number;
|
|
883
|
+
[key: string]: number;
|
|
884
|
+
};
|
|
885
|
+
profilesToSync: string[];
|
|
886
|
+
}
|
|
887
|
+
interface AgentStatusChangedPayload {
|
|
888
|
+
agentImUserId: string;
|
|
889
|
+
status: IMAgentStatus;
|
|
890
|
+
activeProfileId?: string;
|
|
891
|
+
runningTaskIds?: string[];
|
|
892
|
+
}
|
|
893
|
+
interface TaskDispatchContextEntry {
|
|
894
|
+
sender: string;
|
|
895
|
+
senderRole: 'human' | 'agent' | 'admin' | 'system';
|
|
896
|
+
content: string;
|
|
897
|
+
createdAt: string;
|
|
898
|
+
/** Wave-8 W1: assets the human attached to THIS chat message. */
|
|
899
|
+
attachedAssetIds?: string[];
|
|
900
|
+
}
|
|
901
|
+
/** Wave-8 W1: hydrated asset reference attached to a dispatch. */
|
|
902
|
+
interface AssetRef {
|
|
903
|
+
assetId: string;
|
|
904
|
+
contentHash: string;
|
|
905
|
+
mime: string | null;
|
|
906
|
+
sizeBytes: number | null;
|
|
907
|
+
kind: string;
|
|
908
|
+
workspaceId: string;
|
|
909
|
+
role: 'attachment' | 'context';
|
|
910
|
+
}
|
|
911
|
+
interface TaskDispatchRequestPayload {
|
|
912
|
+
taskId: string;
|
|
913
|
+
/** Agent target for runtimeRoute='agent'. Shell dispatches do not use this. */
|
|
914
|
+
agentImUserId?: string;
|
|
915
|
+
/** Runtime/device target for runtimeRoute='shell'. */
|
|
916
|
+
targetDaemonId?: string;
|
|
917
|
+
profileId: string;
|
|
918
|
+
capability: string;
|
|
919
|
+
prompt: string;
|
|
920
|
+
/** Execution surface. `shell` is daemon-local command execution. */
|
|
921
|
+
runtimeRoute?: 'agent' | 'sandbox' | 'shell';
|
|
922
|
+
metadata?: Record<string, unknown>;
|
|
923
|
+
timeoutMs?: number;
|
|
924
|
+
context?: TaskDispatchContextEntry[];
|
|
925
|
+
conversationId?: string;
|
|
926
|
+
/**
|
|
927
|
+
* Channel mode for the originating conversation. Optional and
|
|
928
|
+
* forward-compatible: when missing, daemon renders 'unknown' in the
|
|
929
|
+
* [Channel context] prompt block. See appendChannelContext in
|
|
930
|
+
* daemon/dispatch.ts.
|
|
931
|
+
* - `direct`: 1:1 DM (no @-mention needed in reply).
|
|
932
|
+
* - `group`: multi-party room (end reply with `@<recipient>` to
|
|
933
|
+
* continue the chain).
|
|
934
|
+
*/
|
|
935
|
+
conversationType?: 'direct' | 'group';
|
|
936
|
+
/**
|
|
937
|
+
* Active participants of the dispatch's conversation. Daemon injects this
|
|
938
|
+
* into [Channel context] so the agent knows the authoritative recipient list
|
|
939
|
+
* without hallucinating or having to call `prismer.conversation.listAgents`. Capped at 50
|
|
940
|
+
* entries server-side.
|
|
941
|
+
*/
|
|
942
|
+
participants?: Array<{
|
|
943
|
+
imUserId: string;
|
|
944
|
+
username: string;
|
|
945
|
+
displayName: string;
|
|
946
|
+
role: string;
|
|
947
|
+
agentType?: string | null;
|
|
948
|
+
}>;
|
|
949
|
+
/** Wave-8 W1: assets cloud wants daemon to fold into agent context. */
|
|
950
|
+
assetRefs?: AssetRef[];
|
|
951
|
+
}
|
|
952
|
+
interface TaskDispatchProgressPayload {
|
|
953
|
+
taskId: string;
|
|
954
|
+
progress: number;
|
|
955
|
+
message?: string;
|
|
956
|
+
detail?: Record<string, unknown>;
|
|
957
|
+
}
|
|
958
|
+
/** Wave-8 W1: how the daemon handled a single AssetRef. */
|
|
959
|
+
type AssetDispatchStrategy = 'inline-text' | 'inline-text-truncated' | 'uri-only' | 'error';
|
|
960
|
+
interface AssetDispatchObservation {
|
|
961
|
+
assetId: string;
|
|
962
|
+
contentHash: string;
|
|
963
|
+
mime: string | null;
|
|
964
|
+
sizeBytes: number | null;
|
|
965
|
+
strategy: AssetDispatchStrategy;
|
|
966
|
+
inlinedBytes?: number;
|
|
967
|
+
error?: string;
|
|
968
|
+
}
|
|
969
|
+
interface TaskDispatchReplyPayload {
|
|
970
|
+
taskId: string;
|
|
971
|
+
ok: boolean;
|
|
972
|
+
output?: string;
|
|
973
|
+
error?: {
|
|
974
|
+
code: string;
|
|
975
|
+
message: string;
|
|
976
|
+
};
|
|
977
|
+
assetIds?: string[];
|
|
978
|
+
metrics?: {
|
|
979
|
+
tokensUsed?: number;
|
|
980
|
+
durationMs?: number;
|
|
981
|
+
};
|
|
982
|
+
/** Wave-8 W1: per-asset handling report. */
|
|
983
|
+
assetObservability?: AssetDispatchObservation[];
|
|
984
|
+
}
|
|
985
|
+
interface TaskCancelPayload {
|
|
986
|
+
taskId: string;
|
|
987
|
+
reason?: string;
|
|
988
|
+
}
|
|
989
|
+
interface IMWSMessage<T = unknown> {
|
|
990
|
+
type: string;
|
|
991
|
+
payload: T;
|
|
992
|
+
requestId?: string;
|
|
993
|
+
timestamp: number;
|
|
994
|
+
}
|
|
995
|
+
interface WorkspaceChangedPayload {
|
|
996
|
+
workspaceId: string;
|
|
997
|
+
/** ISO-8601 timestamp from im_workspaces.updatedAt. */
|
|
998
|
+
updatedAt: string;
|
|
999
|
+
}
|
|
1000
|
+
interface AgentProfileChangedPayload {
|
|
1001
|
+
profileId: string;
|
|
1002
|
+
version: number;
|
|
1003
|
+
}
|
|
1004
|
+
interface AgentChangedPayload {
|
|
1005
|
+
agentImUserId: string;
|
|
1006
|
+
fields: {
|
|
1007
|
+
displayName?: string;
|
|
1008
|
+
capabilities?: string[];
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
interface WorkspaceFileChangedPayload {
|
|
1012
|
+
workspaceId: string;
|
|
1013
|
+
path: string;
|
|
1014
|
+
operation: 'create' | 'update' | 'delete';
|
|
1015
|
+
assetId?: string;
|
|
1016
|
+
contentHash?: string;
|
|
1017
|
+
version: number;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
interface OutboxWatcherOptions {
|
|
1021
|
+
/**
|
|
1022
|
+
* Default directory to watch when no setActiveTask has narrowed it.
|
|
1023
|
+
* Container mode: `/workspace/_outbox`. Host mode: omit (no default
|
|
1024
|
+
* scan — only watch when an active task points at a specific dir).
|
|
1025
|
+
*/
|
|
1026
|
+
outboxDir?: string;
|
|
1027
|
+
/** Cloud client (already configured with apiKey + baseUrl). */
|
|
1028
|
+
cloud: CloudClient;
|
|
1029
|
+
/**
|
|
1030
|
+
* Container identifier — required by the cloud's kind=sandbox-output
|
|
1031
|
+
* contract as metadata.containerId. Read from env PRISMER_CONTAINER_ID
|
|
1032
|
+
* when controller injected one; otherwise the daemon_id as fallback so
|
|
1033
|
+
* the request still passes validation. Host-mode (kind=agent-output)
|
|
1034
|
+
* uploads bypass containerId validation.
|
|
1035
|
+
*/
|
|
1036
|
+
containerId: string;
|
|
1037
|
+
/**
|
|
1038
|
+
* Resolver for the owning workspace at upload time. Returns null when no
|
|
1039
|
+
* workspace context is yet known — the watcher will skip the file and
|
|
1040
|
+
* retry on the next scan tick.
|
|
1041
|
+
*/
|
|
1042
|
+
workspaceId: () => string | null;
|
|
1043
|
+
/** Poll interval in ms. Default 2000. */
|
|
1044
|
+
pollIntervalMs?: number;
|
|
1045
|
+
/** Optional log sink (default writes to stdout/stderr). */
|
|
1046
|
+
log?: {
|
|
1047
|
+
info: (msg: string) => void;
|
|
1048
|
+
warn: (msg: string) => void;
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
interface ActiveTask {
|
|
1052
|
+
taskId: string;
|
|
1053
|
+
/** Adapter name; surfaces in upload metadata for observability. */
|
|
1054
|
+
adapter?: string;
|
|
397
1055
|
/**
|
|
398
|
-
*
|
|
1056
|
+
* Per-task outbox directory. When set, scans this directory in
|
|
1057
|
+
* addition to `opts.outboxDir`. Files uploaded from here are tagged
|
|
1058
|
+
* with `kind=agent-output` (host-mode contract).
|
|
399
1059
|
*/
|
|
400
|
-
|
|
1060
|
+
outboxDir?: string;
|
|
1061
|
+
}
|
|
1062
|
+
declare class OutboxWatcher {
|
|
1063
|
+
private opts;
|
|
1064
|
+
private timer?;
|
|
1065
|
+
private uploaded;
|
|
401
1066
|
/**
|
|
402
|
-
*
|
|
1067
|
+
* Host-mode tasks keyed by taskId. Each entry has its own `outboxDir`
|
|
1068
|
+
* and the watcher scans every entry's directory each tick. Concurrent
|
|
1069
|
+
* dispatches (Wave-9: orchestrator + spawned worker on the same daemon)
|
|
1070
|
+
* each occupy their own slot — finishing one dispatch only removes its
|
|
1071
|
+
* slot, leaving every other concurrent dispatch's tracking intact.
|
|
1072
|
+
*
|
|
1073
|
+
* Replaces the v1 single `activeTask` slot which was last-wins and
|
|
1074
|
+
* caused cross-dispatch clobbering: an orchestrator finishing first
|
|
1075
|
+
* would `setActiveTask(null)` and silently discard the concurrent
|
|
1076
|
+
* worker's outbox tracking.
|
|
403
1077
|
*/
|
|
404
|
-
private
|
|
1078
|
+
private activeTasks;
|
|
405
1079
|
/**
|
|
406
|
-
*
|
|
1080
|
+
* Legacy container/sandbox task. Used only when an `ActiveTask` is set
|
|
1081
|
+
* **without** an `outboxDir` (i.e. the legacy `setActiveTask` shape
|
|
1082
|
+
* where the watcher's default `opts.outboxDir` is the scan target).
|
|
1083
|
+
* Single slot because the legacy container deployment only ever runs
|
|
1084
|
+
* one task at a time per container.
|
|
407
1085
|
*/
|
|
408
|
-
private
|
|
1086
|
+
private legacyContainerTask;
|
|
1087
|
+
private busy;
|
|
409
1088
|
/**
|
|
410
|
-
*
|
|
1089
|
+
* Per-task accumulated assetIds. Populated on each successful upload
|
|
1090
|
+
* keyed by the task whose outbox dir the file came from.
|
|
1091
|
+
* `flushPending(taskId)` drains and returns this list so dispatch.ts
|
|
1092
|
+
* can stamp it onto task.dispatch.reply.assetIds.
|
|
411
1093
|
*/
|
|
412
|
-
private
|
|
1094
|
+
private pendingByTask;
|
|
1095
|
+
constructor(opts: OutboxWatcherOptions);
|
|
413
1096
|
/**
|
|
414
|
-
*
|
|
1097
|
+
* Register a host-mode task envelope. Each call adds (or replaces) an
|
|
1098
|
+
* entry keyed by `task.taskId` so concurrent dispatches do not clobber
|
|
1099
|
+
* each other.
|
|
1100
|
+
*
|
|
1101
|
+
* Requires `task.outboxDir` to be set — the per-task workdir is the
|
|
1102
|
+
* isolation boundary. For the legacy container/sandbox single-slot
|
|
1103
|
+
* shape (no per-task dir), use `setActiveTask` instead.
|
|
415
1104
|
*/
|
|
416
|
-
|
|
1105
|
+
addActiveTask(task: ActiveTask): void;
|
|
417
1106
|
/**
|
|
418
|
-
*
|
|
1107
|
+
* Remove a host-mode task entry by id. No-op if the id was not
|
|
1108
|
+
* registered. Idempotent — dispatch.ts calls this in `finally{}` after
|
|
1109
|
+
* `flushPending` so a failed dispatch still clears its slot.
|
|
419
1110
|
*/
|
|
420
|
-
|
|
1111
|
+
removeActiveTask(taskId: string): void;
|
|
421
1112
|
/**
|
|
422
|
-
*
|
|
1113
|
+
* Legacy single-slot API.
|
|
1114
|
+
*
|
|
1115
|
+
* - Non-null with `outboxDir` → forwards to `addActiveTask` (host
|
|
1116
|
+
* mode). The caller is still responsible for `removeActiveTask`
|
|
1117
|
+
* (or another `setActiveTask(null)`) at teardown.
|
|
1118
|
+
* - Non-null **without** `outboxDir` → store in the legacy container
|
|
1119
|
+
* slot. Uploads from `opts.outboxDir` are tagged with this task.
|
|
1120
|
+
* - `null` → clear the legacy container slot only. Does NOT touch
|
|
1121
|
+
* the host-mode `activeTasks` map; concurrent host-mode dispatches
|
|
1122
|
+
* keep their own slots until they call `removeActiveTask`
|
|
1123
|
+
* themselves.
|
|
1124
|
+
*
|
|
1125
|
+
* The null-clears-only-legacy semantic is the core fix for the
|
|
1126
|
+
* Wave-9 concurrency race: previously a finishing orchestrator's
|
|
1127
|
+
* `setActiveTask(null)` would clobber a still-running worker's
|
|
1128
|
+
* tracking, dropping its uploads on the floor.
|
|
423
1129
|
*/
|
|
424
|
-
|
|
1130
|
+
setActiveTask(task: ActiveTask | null): void;
|
|
425
1131
|
/**
|
|
426
|
-
*
|
|
1132
|
+
* Drain and return the assetIds collected for `taskId` since the last
|
|
1133
|
+
* flush. Idempotent — calling twice with the same id returns `[]` the
|
|
1134
|
+
* second time. dispatch.ts calls this once at reply-build time.
|
|
427
1135
|
*/
|
|
428
|
-
|
|
1136
|
+
flushPending(taskId: string): string[];
|
|
1137
|
+
start(): void;
|
|
1138
|
+
stop(): void;
|
|
429
1139
|
/**
|
|
430
|
-
*
|
|
1140
|
+
* Force a synchronous scan — useful for tests + e2e harnesses that want to
|
|
1141
|
+
* trigger upload after writing a file rather than wait for the next tick.
|
|
1142
|
+
* Also used by dispatch.ts at reply-build time to drain any pending files
|
|
1143
|
+
* before flushPending() is called.
|
|
431
1144
|
*/
|
|
432
|
-
|
|
1145
|
+
scanNow(): Promise<void>;
|
|
1146
|
+
private log;
|
|
433
1147
|
/**
|
|
434
|
-
*
|
|
1148
|
+
* Build the list of directories to scan this tick — one entry per
|
|
1149
|
+
* concurrently-active task plus the legacy container slot (if any).
|
|
1150
|
+
* Each entry carries its own `task` envelope so uploads can be tagged
|
|
1151
|
+
* and accumulated per-task without sharing global mutable state.
|
|
1152
|
+
*
|
|
1153
|
+
* Dedup so a directory configured in both spots only scans once
|
|
1154
|
+
* (legacy single-task case where opts.outboxDir == legacyTask.outboxDir
|
|
1155
|
+
* is unlikely but cheap to handle).
|
|
435
1156
|
*/
|
|
436
|
-
private
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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;
|
|
1157
|
+
private currentScanDirs;
|
|
1158
|
+
private tick;
|
|
1159
|
+
private scanDir;
|
|
1160
|
+
private upload;
|
|
583
1161
|
}
|
|
584
1162
|
|
|
1163
|
+
interface DispatchDeps {
|
|
1164
|
+
registry: AdapterRegistry;
|
|
1165
|
+
cloud: CloudClient;
|
|
1166
|
+
uriResolver: UriResolver;
|
|
1167
|
+
assetCache: AssetCache;
|
|
1168
|
+
ws: WsClient;
|
|
1169
|
+
/** Cap on total chars of joined context history. Default 8000. */
|
|
1170
|
+
contextMaxChars?: number;
|
|
1171
|
+
/** Resolves a long-running adapter's service handle (cached across calls). */
|
|
1172
|
+
ensureService: (profile: AgentProfile, adapter: AdapterDef) => Promise<AdapterService>;
|
|
1173
|
+
/** Aborts the in-flight adapter dispatch when cloud sends task.cancel. */
|
|
1174
|
+
signal?: AbortSignal;
|
|
1175
|
+
/** Hook for tests / observability. */
|
|
1176
|
+
onProgress?: (taskId: string, p: TaskDispatchProgressPayload) => void;
|
|
1177
|
+
/**
|
|
1178
|
+
* Outbox uploader. When provided, dispatch creates a per-task outbox
|
|
1179
|
+
* directory under `paths.runsDir`, points the watcher at it via
|
|
1180
|
+
* setActiveTask, and drains its assetIds into reply.assetIds at flush
|
|
1181
|
+
* time. Optional so tests can omit it.
|
|
1182
|
+
*/
|
|
1183
|
+
outboxWatcher?: OutboxWatcher;
|
|
1184
|
+
/** Daemon paths — used to derive the per-task outbox dir. */
|
|
1185
|
+
paths?: ConfigPaths;
|
|
1186
|
+
}
|
|
1187
|
+
declare function handleDispatch(payload: TaskDispatchRequestPayload, requestId: string | undefined, deps: DispatchDeps): Promise<TaskDispatchReplyPayload>;
|
|
585
1188
|
/**
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
* Exposes evolution gateway operations via HTTP for daemon process.
|
|
589
|
-
* Integrates with LLM Dispatcher for distillation triggers.
|
|
1189
|
+
* Concatenate context entries + current prompt. Trims oldest entries first when
|
|
1190
|
+
* total chars exceed `maxChars` (matches Track C's `trimContextWindow` behavior).
|
|
590
1191
|
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
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
|
|
1192
|
+
* `assetBlocks` (Wave-8 W1) is prepended above the conversation history. They
|
|
1193
|
+
* are *not* counted against `maxChars` — assets are content the user
|
|
1194
|
+
* explicitly attached, so dropping them silently when the chat history
|
|
1195
|
+
* happens to be long would defeat the whole point of the attachment.
|
|
601
1196
|
*/
|
|
1197
|
+
declare function composePrompt(currentPrompt: string, context: TaskDispatchContextEntry[], maxChars: number, assetBlocks?: string[]): string;
|
|
602
1198
|
|
|
603
|
-
|
|
604
|
-
|
|
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;
|
|
1199
|
+
declare class ServicePool {
|
|
1200
|
+
private readonly services;
|
|
611
1201
|
/**
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
* omit the Authorization header (callers get 401 from cloud until the
|
|
615
|
-
* daemon is started with `apiKey`).
|
|
1202
|
+
* Returns a service handle for the profile, lazily calling `adapter.ensureService`
|
|
1203
|
+
* on first use or after a crash. Re-checks `healthy()` before returning a cached entry.
|
|
616
1204
|
*/
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
-
};
|
|
1205
|
+
ensureService(profile: AgentProfile, adapter: AdapterDef): Promise<AdapterService>;
|
|
1206
|
+
/** Drop a service handle (e.g. on profile delete or daemon shutdown). */
|
|
1207
|
+
drop(profileId: string): Promise<void>;
|
|
1208
|
+
/** Drop all handles, e.g. on graceful shutdown. */
|
|
1209
|
+
shutdown(): Promise<void>;
|
|
1210
|
+
size(): number;
|
|
663
1211
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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;
|
|
1212
|
+
|
|
1213
|
+
interface LocalServerOptions {
|
|
1214
|
+
port: number;
|
|
1215
|
+
/** Snapshot getter — runner provides current state on demand. */
|
|
1216
|
+
getState: () => LocalServerState;
|
|
698
1217
|
/**
|
|
699
|
-
* POST /
|
|
700
|
-
*
|
|
1218
|
+
* Optional dispatch sink called when POST /v1/runs receives a task envelope
|
|
1219
|
+
* from the sandbox controller. Phase 1 contract: the sink is fire-and-forget;
|
|
1220
|
+
* daemon acks immediately with `{ runId, status: 'accepted' }`. Cloud-side
|
|
1221
|
+
* task lifecycle (status → done/failed) is closed asynchronously by the
|
|
1222
|
+
* daemon's WS upstream channel in S4 follow-up. The runner provides this
|
|
1223
|
+
* sink; tests may pass a stub.
|
|
701
1224
|
*/
|
|
702
|
-
|
|
1225
|
+
onDispatch?: (payload: DispatchPayload, runId: string) => void;
|
|
703
1226
|
/**
|
|
704
|
-
*
|
|
705
|
-
*
|
|
1227
|
+
* Install or update one hosted agent/profile in the daemon's local mirror.
|
|
1228
|
+
* The runner persists it, reloads hostedAgents, and redeclares to cloud.
|
|
706
1229
|
*/
|
|
707
|
-
|
|
1230
|
+
onInstallAgent?: (payload: InstallAgentPayload) => Promise<InstallAgentResult>;
|
|
708
1231
|
/**
|
|
709
|
-
*
|
|
710
|
-
*
|
|
1232
|
+
* Filesystem root for snapshot manifests. Default `/workspace`. POST
|
|
1233
|
+
* /v1/snapshot walks this tree, computes per-file sha256 + size + mtime,
|
|
1234
|
+
* and returns the manifest. Cloud-side persistence (POST
|
|
1235
|
+
* /api/sandboxes/:id/snapshot/manifest) is a separate step in Phase 1.
|
|
711
1236
|
*/
|
|
712
|
-
|
|
1237
|
+
snapshotRoot?: string;
|
|
713
1238
|
/**
|
|
714
|
-
*
|
|
715
|
-
*
|
|
1239
|
+
* Optional first-pass handler for `/local/memory/*` routes. Returns true
|
|
1240
|
+
* if the request was handled (response written); false to let the normal
|
|
1241
|
+
* route table fall through. Wired by the daemon runner from
|
|
1242
|
+
* `attachMemoryRpc()` in `daemon/memory/rpc.ts` when memory is enabled.
|
|
1243
|
+
* When set, /healthz reports `memoryReady: true`.
|
|
716
1244
|
*/
|
|
717
|
-
|
|
1245
|
+
attachMemory?: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
|
|
718
1246
|
/**
|
|
719
|
-
*
|
|
720
|
-
*
|
|
1247
|
+
* Optional handler for `POST /local/asset/write` — agent-gen adapter
|
|
1248
|
+
* (daemon/asset/origin/agent-gen.ts). Synchronous: agent posts bytes,
|
|
1249
|
+
* daemon uploads to cloud, returns prismer:// URI in one round-trip.
|
|
1250
|
+
* Sink receives the parsed JSON body and returns a structured result.
|
|
721
1251
|
*/
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
private callCloudQueryGenes;
|
|
728
|
-
private callCloudGetPersonality;
|
|
729
|
-
private callCloudDistill;
|
|
730
|
-
private callCloudGetUnmatched;
|
|
731
|
-
private triggerLocalDistillation;
|
|
732
|
-
private extractSignalsLocal;
|
|
1252
|
+
onAssetWrite?: (body: unknown) => Promise<AssetWriteHandlerResult>;
|
|
1253
|
+
}
|
|
1254
|
+
interface AssetWriteHandlerResult {
|
|
1255
|
+
status: 200 | 400 | 502;
|
|
1256
|
+
body: unknown;
|
|
733
1257
|
}
|
|
734
|
-
|
|
735
1258
|
/**
|
|
736
|
-
*
|
|
1259
|
+
* Dispatch envelope received from the sandbox controller proxy. The schema
|
|
1260
|
+
* mirrors `DaemonDispatchSchema` in `infra/sandbox-controller/src/api/v1/
|
|
1261
|
+
* containers.ts` — kept permissive so the controller can pass through any
|
|
1262
|
+
* future fields without a daemon-side bump.
|
|
737
1263
|
*
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
*
|
|
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)
|
|
1264
|
+
* Phase 1 escape hatch: when `shellCommand` is present, the daemon spawns
|
|
1265
|
+
* it via `bash -c` with cwd=/workspace as a fire-and-forget subprocess.
|
|
1266
|
+
* This bridges the gap until S4 wires the WS-mediated adapter dispatch +
|
|
1267
|
+
* completion path. Production traffic does NOT set shellCommand — task
|
|
1268
|
+
* service forwards it only when present in IMTask.metadata.shellCommand
|
|
1269
|
+
* (a test escape hatch).
|
|
756
1270
|
*/
|
|
757
|
-
|
|
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 {
|
|
1271
|
+
interface DispatchPayload {
|
|
795
1272
|
taskId: string;
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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;
|
|
1273
|
+
adapter?: string;
|
|
1274
|
+
prompt?: string;
|
|
1275
|
+
env?: Record<string, string>;
|
|
1276
|
+
shellCommand?: string;
|
|
1277
|
+
[k: string]: unknown;
|
|
1278
|
+
}
|
|
1279
|
+
interface InstallAgentPayload {
|
|
1280
|
+
workspaceId: string;
|
|
1281
|
+
imUserId: string;
|
|
1282
|
+
name: string;
|
|
1283
|
+
adapterName: string;
|
|
1284
|
+
capabilities: string[];
|
|
1285
|
+
profile: {
|
|
1286
|
+
id: string;
|
|
1287
|
+
name: string;
|
|
1288
|
+
adapterName: string;
|
|
1289
|
+
config: Record<string, unknown>;
|
|
1290
|
+
version: number;
|
|
896
1291
|
};
|
|
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
1292
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
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;
|
|
1293
|
+
interface InstallAgentResult {
|
|
1294
|
+
ok: true;
|
|
1295
|
+
daemonId: string;
|
|
1296
|
+
installedAgent: {
|
|
1297
|
+
imUserId: string;
|
|
1298
|
+
name: string;
|
|
1299
|
+
adapterName: string;
|
|
1300
|
+
profileId: string;
|
|
1046
1301
|
};
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
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;
|
|
1302
|
+
hostedAgents: Array<{
|
|
1303
|
+
imUserId: string;
|
|
1304
|
+
name: string;
|
|
1305
|
+
adapterName: string;
|
|
1088
1306
|
}>;
|
|
1089
1307
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
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;
|
|
1308
|
+
interface LocalServerState {
|
|
1309
|
+
daemonId: string;
|
|
1310
|
+
cloudBaseUrl?: string;
|
|
1311
|
+
workspaceId?: string | null;
|
|
1312
|
+
pid: number;
|
|
1313
|
+
startedAt: number;
|
|
1314
|
+
wsConnected: boolean;
|
|
1315
|
+
hostedAgents: Array<{
|
|
1316
|
+
imUserId: string;
|
|
1317
|
+
name: string;
|
|
1318
|
+
adapterName: string;
|
|
1167
1319
|
}>;
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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;
|
|
1320
|
+
runningTaskIds: string[];
|
|
1321
|
+
observability?: {
|
|
1322
|
+
adapters?: Record<string, unknown>;
|
|
1323
|
+
lastTaskError?: {
|
|
1324
|
+
taskId: string;
|
|
1325
|
+
message: string;
|
|
1326
|
+
at: string;
|
|
1327
|
+
};
|
|
1287
1328
|
};
|
|
1288
1329
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
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;
|
|
1330
|
+
declare class LocalServer {
|
|
1331
|
+
private opts;
|
|
1332
|
+
private server?;
|
|
1333
|
+
constructor(opts: LocalServerOptions);
|
|
1334
|
+
start(): Promise<void>;
|
|
1335
|
+
stop(): Promise<void>;
|
|
1336
|
+
private route;
|
|
1337
|
+
private routeStandard;
|
|
1332
1338
|
/**
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1339
|
+
* POST /v1/agents/install — cloud/controller installs one hosted agent on
|
|
1340
|
+
* this daemon. The payload is intentionally explicit and idempotent: the
|
|
1341
|
+
* runner upserts local `agents` and `agent_profiles` rows, reloads the
|
|
1342
|
+
* in-memory declaration set, then sends `agent.host.declare` immediately.
|
|
1335
1343
|
*/
|
|
1336
|
-
|
|
1344
|
+
private handleInstallAgent;
|
|
1337
1345
|
/**
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1346
|
+
* POST /v1/snapshot — daemon-first FS manifest snapshot.
|
|
1347
|
+
*
|
|
1348
|
+
* Walks `snapshotRoot` (default `/workspace`), computes per-file
|
|
1349
|
+
* sha256 + sizeBytes + mtime, returns `{ files: [...], rootPath }`.
|
|
1350
|
+
* Skips dotfiles + the `_outbox/_uploaded` reserved subdir. Phase 1
|
|
1351
|
+
* caller (cloud or controller) is responsible for POSTing the result
|
|
1352
|
+
* to `/api/sandboxes/:id/snapshot/manifest`.
|
|
1340
1353
|
*/
|
|
1341
|
-
|
|
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;
|
|
1354
|
+
private handleSnapshot;
|
|
1359
1355
|
/**
|
|
1360
|
-
*
|
|
1361
|
-
*
|
|
1356
|
+
* POST /local/asset/write — agent-gen adapter RPC.
|
|
1357
|
+
*
|
|
1358
|
+
* Body: { workspaceId, bytes (base64), filename, mime?, path?, description?, sourceAgentImUserId? }
|
|
1359
|
+
* Response 200: { assetId, contentHash, prismerUri }
|
|
1360
|
+
* 400 on validation failure; 502 on cloud upstream failure; 501 if no sink wired.
|
|
1362
1361
|
*/
|
|
1363
|
-
|
|
1362
|
+
private handleAssetWrite;
|
|
1364
1363
|
/**
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1367
|
-
*
|
|
1364
|
+
* POST /v1/runs — dispatch ack-only (Cloud 3 S3 Phase 1).
|
|
1365
|
+
*
|
|
1366
|
+
* The sandbox controller resolves the pod IP and POSTs a task envelope here.
|
|
1367
|
+
* Phase 1 just acknowledges receipt and forwards the payload to onDispatch
|
|
1368
|
+
* (the runner). Actual task execution + cloud-side completion are wired in
|
|
1369
|
+
* S4 (daemon WS upstream emits task.dispatch.reply once the adapter finishes).
|
|
1370
|
+
*
|
|
1371
|
+
* Response on success: 202 + `{ runId, status: 'accepted' }`.
|
|
1372
|
+
* Bad JSON / missing taskId: 400. Internal error: 500.
|
|
1368
1373
|
*/
|
|
1369
|
-
|
|
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;
|
|
1374
|
+
private handleDispatch;
|
|
1400
1375
|
}
|
|
1401
1376
|
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
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>;
|
|
1377
|
+
interface RunnerOptions {
|
|
1378
|
+
/** Override config path; defaults to ~/.prismer. */
|
|
1379
|
+
paths?: ConfigPaths;
|
|
1380
|
+
/** Skip starting the local 127.0.0.1 server (useful in tests). */
|
|
1381
|
+
startLocalServer?: boolean;
|
|
1382
|
+
/** Local server port; defaults to 3210. */
|
|
1383
|
+
localPort?: number;
|
|
1384
|
+
/** Override daemonVersion reported in agent.host.declare. */
|
|
1385
|
+
daemonVersion?: string;
|
|
1386
|
+
/** Pre-loaded config (skip filesystem read). */
|
|
1387
|
+
configOverride?: Config;
|
|
1388
|
+
/** Override built-in adapter list (tests). */
|
|
1389
|
+
adaptersOverride?: AdapterDef[];
|
|
1390
|
+
}
|
|
1391
|
+
declare class Runner extends EventEmitter {
|
|
1392
|
+
private opts;
|
|
1393
|
+
private config;
|
|
1394
|
+
private paths;
|
|
1395
|
+
private db;
|
|
1396
|
+
private cloud;
|
|
1397
|
+
private ws;
|
|
1398
|
+
private syncWorker;
|
|
1399
|
+
private syncQueue;
|
|
1400
|
+
private assetCache;
|
|
1401
|
+
private uriResolver;
|
|
1402
|
+
private registry;
|
|
1403
|
+
private servicePool;
|
|
1404
|
+
private shellConfig;
|
|
1405
|
+
private localServer?;
|
|
1406
|
+
private outboxWatcher?;
|
|
1407
|
+
private memoryWiring?;
|
|
1408
|
+
private state;
|
|
1409
|
+
private startedAt;
|
|
1410
|
+
private workspaceId;
|
|
1411
|
+
private wsConnected;
|
|
1412
|
+
private readonly hostedAgents;
|
|
1413
|
+
private readonly runningTasks;
|
|
1414
|
+
private lastTaskError?;
|
|
1415
|
+
private heartbeatTimer?;
|
|
1416
|
+
private taskReaperTimer?;
|
|
1417
|
+
constructor(opts?: RunnerOptions);
|
|
1418
|
+
start(): Promise<void>;
|
|
1419
|
+
stop(): Promise<void>;
|
|
1420
|
+
isRunning(): boolean;
|
|
1543
1421
|
/**
|
|
1544
|
-
*
|
|
1422
|
+
* Phase 1 escape hatch — see `DispatchPayload.shellCommand`. Spawns
|
|
1423
|
+
* `bash -c <cmd>` with cwd=/workspace, mirrors output to pod logs.
|
|
1424
|
+
* Fire-and-forget; nothing here writes back to cloud.
|
|
1545
1425
|
*/
|
|
1546
|
-
|
|
1426
|
+
private runShellCommand;
|
|
1427
|
+
snapshotState(): LocalServerState;
|
|
1547
1428
|
/**
|
|
1548
|
-
*
|
|
1429
|
+
* Re-read the local `agents` table (populated by `prismer agent register`)
|
|
1430
|
+
* and re-populate `hostedAgents` map. Profiles per agent come from local
|
|
1431
|
+
* `agent_profiles` table (filled by host.acked sync).
|
|
1549
1432
|
*/
|
|
1550
|
-
|
|
1433
|
+
loadAgentsFromDb(): void;
|
|
1551
1434
|
/**
|
|
1552
|
-
*
|
|
1435
|
+
* Register an in-process AgentProfile snapshot (called by agent CLI / sync layer).
|
|
1436
|
+
* Used to populate the `agents` list in agent.host.declare.
|
|
1553
1437
|
*/
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1438
|
+
setHostedAgent(agent: {
|
|
1439
|
+
imUserId: string;
|
|
1440
|
+
name: string;
|
|
1441
|
+
adapterName: string;
|
|
1442
|
+
capabilities: string[];
|
|
1443
|
+
profiles: Array<{
|
|
1444
|
+
id: string;
|
|
1445
|
+
version: number;
|
|
1446
|
+
}>;
|
|
1447
|
+
}): void;
|
|
1448
|
+
private installHostedAgent;
|
|
1449
|
+
private installStaticHostedAgentFromEnv;
|
|
1450
|
+
private wireWsHandlers;
|
|
1451
|
+
private sendDeclare;
|
|
1452
|
+
private handleIncoming;
|
|
1453
|
+
private onHostAcked;
|
|
1454
|
+
private onTaskDispatch;
|
|
1455
|
+
private onTaskCancel;
|
|
1456
|
+
private onAgentChanged;
|
|
1457
|
+
private onAgentProfileChanged;
|
|
1458
|
+
private syncProfileFromCloud;
|
|
1459
|
+
private resolveOwnedAgent;
|
|
1460
|
+
private onWorkspaceChanged;
|
|
1461
|
+
private onWorkspaceFileChanged;
|
|
1462
|
+
private snapshotAdapterObservability;
|
|
1463
|
+
/**
|
|
1464
|
+
* SyncWorker FlushFn — pushes local writes to cloud.
|
|
1465
|
+
*
|
|
1466
|
+
* Maps:
|
|
1467
|
+
* workspace → PATCH /api/im/workspaces/:id
|
|
1468
|
+
* agent_profile → PATCH /api/im/agent_profiles/:id
|
|
1469
|
+
* agent → PATCH /api/im/agents/:imUserId
|
|
1470
|
+
* On 'create' we POST instead. On 'delete' we DELETE.
|
|
1557
1471
|
*/
|
|
1558
|
-
cleanupExpiredKeys(): Promise<void>;
|
|
1559
1472
|
/**
|
|
1560
|
-
*
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
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).
|
|
1473
|
+
* SyncWorker FlushFn — pushes one local sync row to cloud via CloudClient.
|
|
1474
|
+
*
|
|
1475
|
+
* resource_type × operation → endpoint:
|
|
1476
|
+
* workspace.create → POST /api/im/workspaces
|
|
1477
|
+
* workspace.update → PATCH /api/im/workspaces/:id
|
|
1478
|
+
* workspace.delete → DELETE /api/im/workspaces/:id
|
|
1479
|
+
* agent.create → POST /api/im/register (the only public path
|
|
1480
|
+
* that creates an
|
|
1481
|
+
* IMUser of role='agent')
|
|
1482
|
+
* agent.update → PATCH /api/im/agents/:id
|
|
1483
|
+
* agent.delete → DELETE /api/im/agents/:id
|
|
1484
|
+
* agent_profile.create → POST /api/im/agent_profiles
|
|
1485
|
+
* agent_profile.update → PATCH /api/im/agent_profiles/:id
|
|
1486
|
+
* agent_profile.delete → DELETE /api/im/agent_profiles/:id
|
|
1487
|
+
*
|
|
1488
|
+
* Error classification per docs/refactor/13-error-handling.md §2.1 / §2.7:
|
|
1489
|
+
* 2xx → ok:true (SyncWorker drops the row)
|
|
1490
|
+
* 408 / 429 → retryable (SyncWorker re-queues with exponential backoff)
|
|
1491
|
+
* 5xx / net err → retryable
|
|
1492
|
+
* 4xx (other) → permanent (SyncWorker marks failed; 409 is conflict)
|
|
1616
1493
|
*/
|
|
1617
|
-
|
|
1494
|
+
private flushSyncRow;
|
|
1618
1495
|
}
|
|
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
1496
|
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
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
|
-
*/
|
|
1497
|
+
interface PairOptions {
|
|
1498
|
+
cloudBaseUrl: string;
|
|
1499
|
+
/** Human-readable label visible to the approver in mobile UI. */
|
|
1500
|
+
deviceName?: string;
|
|
1501
|
+
/** Override config save path (tests). */
|
|
1502
|
+
paths?: ConfigPaths;
|
|
1503
|
+
/** Number of poll attempts (5 s each). Default 60 → 5 min. */
|
|
1504
|
+
maxPollAttempts?: number;
|
|
1505
|
+
/** Pluggable fetch (tests). */
|
|
1663
1506
|
fetchImpl?: typeof fetch;
|
|
1664
|
-
/**
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
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;
|
|
1507
|
+
/** Allow overwriting existing config. Default false. */
|
|
1508
|
+
force?: boolean;
|
|
1509
|
+
/** Hook for test capture of the QR URL. */
|
|
1510
|
+
onQrReady?: (qrUrl: string) => void;
|
|
1511
|
+
/**
|
|
1512
|
+
* LAN-dev bypass: skip QR + mobile approval and auto-approve the offer
|
|
1513
|
+
* as the named human IMUser. Requires `LOCAL_ONLY=1` in this process AND
|
|
1514
|
+
* cloud-side `LOCAL_ONLY=1 && NODE_ENV !== 'production'`. Without those
|
|
1515
|
+
* env conditions the cloud returns 403 and we surface the error.
|
|
1516
|
+
*/
|
|
1517
|
+
asUserEmail?: string;
|
|
1518
|
+
/** Override LOCAL_ONLY env read (tests). */
|
|
1519
|
+
isLocalOnly?: () => boolean;
|
|
1520
|
+
/** Override poll wait (tests). Default 5_000ms. */
|
|
1521
|
+
pollIntervalMs?: number;
|
|
1522
|
+
}
|
|
1523
|
+
interface PairResult {
|
|
1524
|
+
config: Config;
|
|
1525
|
+
paths: ConfigPaths;
|
|
1526
|
+
}
|
|
1527
|
+
declare function pair(opts: PairOptions): Promise<PairResult>;
|
|
1795
1528
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
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
|
-
*/
|
|
1529
|
+
declare const CCConfigSchema: z.ZodObject<{
|
|
1530
|
+
cwd: z.ZodString;
|
|
1531
|
+
model: z.ZodDefault<z.ZodString>;
|
|
1532
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
1533
|
+
envVars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1534
|
+
mcpServers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1535
|
+
name: z.ZodString;
|
|
1536
|
+
command: z.ZodString;
|
|
1537
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1538
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1539
|
+
}, "strip", z.ZodTypeAny, {
|
|
1540
|
+
name: string;
|
|
1541
|
+
command: string;
|
|
1542
|
+
env?: Record<string, string> | undefined;
|
|
1543
|
+
args?: string[] | undefined;
|
|
1544
|
+
}, {
|
|
1545
|
+
name: string;
|
|
1546
|
+
command: string;
|
|
1547
|
+
env?: Record<string, string> | undefined;
|
|
1548
|
+
args?: string[] | undefined;
|
|
1549
|
+
}>, "many">>;
|
|
1550
|
+
allowedTools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1551
|
+
maxTurns: z.ZodDefault<z.ZodNumber>;
|
|
1552
|
+
baseURL: z.ZodOptional<z.ZodString>;
|
|
1553
|
+
apiKeyRef: z.ZodOptional<z.ZodString>;
|
|
1554
|
+
route: z.ZodDefault<z.ZodEnum<["default", "prismer", "omniroute"]>>;
|
|
1555
|
+
}, "strip", z.ZodTypeAny, {
|
|
1556
|
+
model: string;
|
|
1557
|
+
cwd: string;
|
|
1558
|
+
maxTurns: number;
|
|
1559
|
+
route: "default" | "prismer" | "omniroute";
|
|
1560
|
+
systemPrompt?: string | undefined;
|
|
1561
|
+
allowedTools?: string[] | undefined;
|
|
1562
|
+
envVars?: Record<string, string> | undefined;
|
|
1563
|
+
mcpServers?: {
|
|
1564
|
+
name: string;
|
|
1565
|
+
command: string;
|
|
1566
|
+
env?: Record<string, string> | undefined;
|
|
1567
|
+
args?: string[] | undefined;
|
|
1568
|
+
}[] | undefined;
|
|
1569
|
+
baseURL?: string | undefined;
|
|
1570
|
+
apiKeyRef?: string | undefined;
|
|
1571
|
+
}, {
|
|
1572
|
+
cwd: string;
|
|
1573
|
+
model?: string | undefined;
|
|
1574
|
+
systemPrompt?: string | undefined;
|
|
1575
|
+
allowedTools?: string[] | undefined;
|
|
1576
|
+
envVars?: Record<string, string> | undefined;
|
|
1577
|
+
mcpServers?: {
|
|
1578
|
+
name: string;
|
|
1579
|
+
command: string;
|
|
1580
|
+
env?: Record<string, string> | undefined;
|
|
1581
|
+
args?: string[] | undefined;
|
|
1582
|
+
}[] | undefined;
|
|
1583
|
+
maxTurns?: number | undefined;
|
|
1584
|
+
baseURL?: string | undefined;
|
|
1585
|
+
apiKeyRef?: string | undefined;
|
|
1586
|
+
route?: "default" | "prismer" | "omniroute" | undefined;
|
|
1587
|
+
}>;
|
|
1588
|
+
type ClaudeCodeConfig = z.infer<typeof CCConfigSchema>;
|
|
1589
|
+
declare const claudeCodeAdapter: AdapterDef;
|
|
1880
1590
|
|
|
1881
|
-
declare const
|
|
1882
|
-
|
|
1883
|
-
|
|
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;
|
|
1591
|
+
declare const CodexConfigSchema: z.ZodObject<{
|
|
1592
|
+
/** Working directory for the codex subprocess. */
|
|
1593
|
+
cwd: z.ZodString;
|
|
2023
1594
|
/**
|
|
2024
|
-
*
|
|
1595
|
+
* Model identifier passed to --model (e.g. 'codex-mini-latest', 'o4-mini').
|
|
1596
|
+
* Default matches the Codex CLI built-in default as of 2026-05.
|
|
2025
1597
|
*/
|
|
2026
|
-
|
|
1598
|
+
model: z.ZodDefault<z.ZodString>;
|
|
2027
1599
|
/**
|
|
2028
|
-
*
|
|
1600
|
+
* Sandbox level for the codex subprocess.
|
|
1601
|
+
* read-only — analysis only; code-gen tasks will stall
|
|
1602
|
+
* workspace-write — write inside cwd, read-only outside (recommended)
|
|
1603
|
+
* danger-full-access — no restrictions; safe only inside isolated containers
|
|
2029
1604
|
*/
|
|
2030
|
-
|
|
1605
|
+
sandbox: z.ZodDefault<z.ZodEnum<["read-only", "workspace-write", "danger-full-access"]>>;
|
|
1606
|
+
/** Optional system prompt prepended to the user prompt as a preamble (see U4). */
|
|
1607
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
1608
|
+
/** Extra env vars merged into the codex subprocess environment. */
|
|
1609
|
+
envVars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
2031
1610
|
/**
|
|
2032
|
-
*
|
|
1611
|
+
* Name of the env var holding the OpenAI API key. Defaults to
|
|
1612
|
+
* 'OPENAI_API_KEY' (Codex CLI default). Override if using a workspace-
|
|
1613
|
+
* scoped key under a different name.
|
|
2033
1614
|
*/
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
private closeDataChannel;
|
|
2059
|
-
}
|
|
1615
|
+
apiKeyEnv: z.ZodDefault<z.ZodString>;
|
|
1616
|
+
}, "strip", z.ZodTypeAny, {
|
|
1617
|
+
model: string;
|
|
1618
|
+
sandbox: "read-only" | "workspace-write" | "danger-full-access";
|
|
1619
|
+
cwd: string;
|
|
1620
|
+
apiKeyEnv: string;
|
|
1621
|
+
systemPrompt?: string | undefined;
|
|
1622
|
+
envVars?: Record<string, string> | undefined;
|
|
1623
|
+
}, {
|
|
1624
|
+
cwd: string;
|
|
1625
|
+
model?: string | undefined;
|
|
1626
|
+
systemPrompt?: string | undefined;
|
|
1627
|
+
sandbox?: "read-only" | "workspace-write" | "danger-full-access" | undefined;
|
|
1628
|
+
envVars?: Record<string, string> | undefined;
|
|
1629
|
+
apiKeyEnv?: string | undefined;
|
|
1630
|
+
}>;
|
|
1631
|
+
type CodexConfig = z.infer<typeof CodexConfigSchema>;
|
|
1632
|
+
/**
|
|
1633
|
+
* Parse `codex exec --json` stdout. The CLI emits newline-delimited JSON
|
|
1634
|
+
* lines on stdout; we look for the final-message line and extract its
|
|
1635
|
+
* content. Tolerant: any parse failure falls back to raw stdout.
|
|
1636
|
+
*/
|
|
1637
|
+
declare function parseCodexOutput(stdout: string): string;
|
|
1638
|
+
declare const codexAdapter: AdapterDef;
|
|
2060
1639
|
|
|
2061
|
-
declare
|
|
1640
|
+
declare function buildProgram(): Command;
|
|
1641
|
+
declare function runCli(argv?: string[]): Promise<void>;
|
|
2062
1642
|
|
|
2063
|
-
export {
|
|
1643
|
+
export { type AcquireResult, type AdapterDef, type AdapterKind, AdapterRegistry, type AdapterService, type AgentChangedPayload, type AgentHostDeclarePayload, type AgentProfile, type AgentProfileChangedPayload, type AgentStatusChangedPayload, AssetCache, type AssetCacheOptions, 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 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 ValidationResult, WS_CLOSE, type WorkspaceChangedPayload, type WorkspaceFileBinding, type WorkspaceFileChangedPayload, WorkspaceMirror, type WorkspaceMirrorOptions, WsClient, type WsClientOptions, buildProgram, claudeCodeAdapter, codexAdapter, composePrompt, configExists, currentSchemaVersion, deriveWsUrl, envelope, getRoleTemplate, handleDispatch, hermesAdapter, isDaemonId, listRoleTemplates, loadConfig, newDaemonId, nextBackoffMs, openLocalDb, pair, parseCodexOutput, parseUris, resolvePaths, runCli, runMigrations, saveConfig };
|