@prismer/runtime 1.9.1 → 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 -14988
- 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.cts
ADDED
|
@@ -0,0 +1,1643 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
|
|
7
|
+
type AdapterKind = 'long-running' | 'interactive';
|
|
8
|
+
/**
|
|
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`).
|
|
19
|
+
*/
|
|
20
|
+
interface AdapterDef {
|
|
21
|
+
/** Unique name, e.g. 'hermes', 'claude-code'. Matches `IMAgentCard.adapterName`. */
|
|
22
|
+
name: string;
|
|
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>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* AgentProfile mirrors the cloud `im_agent_profiles` row.
|
|
40
|
+
*
|
|
41
|
+
* Field names match Track A's `IMAgentProfile` Prisma model + `AgentProfileDTO`
|
|
42
|
+
* in `src/im/api/agent-profiles.ts` (m1, merged in d5186ca / a4b3d89).
|
|
43
|
+
*
|
|
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.
|
|
47
|
+
*/
|
|
48
|
+
interface AgentProfile {
|
|
49
|
+
id: string;
|
|
50
|
+
workspaceId: string;
|
|
51
|
+
agentImUserId: string;
|
|
52
|
+
adapterName: string;
|
|
53
|
+
name: string;
|
|
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;
|
|
67
|
+
}
|
|
68
|
+
interface TaskInput {
|
|
69
|
+
taskId: string;
|
|
70
|
+
prompt: string;
|
|
71
|
+
metadata?: Record<string, unknown>;
|
|
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 {
|
|
81
|
+
ok: boolean;
|
|
82
|
+
output?: string;
|
|
83
|
+
error?: {
|
|
84
|
+
code: string;
|
|
85
|
+
message: string;
|
|
86
|
+
};
|
|
87
|
+
artifacts?: Array<{
|
|
88
|
+
kind: string;
|
|
89
|
+
storageUri: string;
|
|
90
|
+
mime?: string;
|
|
91
|
+
size?: number;
|
|
92
|
+
}>;
|
|
93
|
+
metrics?: {
|
|
94
|
+
tokensUsed?: number;
|
|
95
|
+
durationMs?: number;
|
|
96
|
+
};
|
|
97
|
+
/** Adapter-private metadata used by daemon-side bridges/observability. */
|
|
98
|
+
metadata?: Record<string, unknown>;
|
|
99
|
+
}
|
|
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[];
|
|
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
|
+
*/
|
|
137
|
+
declare class AdapterRegistry {
|
|
138
|
+
private readonly adapters;
|
|
139
|
+
register(adapter: AdapterDef): void;
|
|
140
|
+
unregister(name: string): boolean;
|
|
141
|
+
has(name: string): boolean;
|
|
142
|
+
get(name: string): AdapterDef | undefined;
|
|
143
|
+
list(): AdapterDef[];
|
|
144
|
+
size(): number;
|
|
145
|
+
/**
|
|
146
|
+
* Find adapters that satisfy a capability tag.
|
|
147
|
+
*
|
|
148
|
+
* Match rules (deterministic, sorted by name):
|
|
149
|
+
* - exact: adapter declared the tag verbatim
|
|
150
|
+
* - wildcard: adapter declared `code.*` → matches `code.write`
|
|
151
|
+
*/
|
|
152
|
+
findByCapability(capability: string): AdapterDef[];
|
|
153
|
+
}
|
|
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
|
+
};
|
|
270
|
+
/**
|
|
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.
|
|
303
|
+
*
|
|
304
|
+
* Pass `:memory:` for in-test usage.
|
|
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;
|
|
310
|
+
|
|
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[];
|
|
348
|
+
}
|
|
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;
|
|
365
|
+
}
|
|
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;
|
|
379
|
+
/**
|
|
380
|
+
* Drain up to `batchSize` ready rows. Public for test injection.
|
|
381
|
+
* Re-entrant guarded — concurrent ticks return immediately.
|
|
382
|
+
*/
|
|
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;
|
|
475
|
+
} | undefined;
|
|
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;
|
|
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;
|
|
526
|
+
|
|
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;
|
|
537
|
+
}
|
|
538
|
+
interface CloudResponse<T> {
|
|
539
|
+
ok: boolean;
|
|
540
|
+
status: number;
|
|
541
|
+
data?: T;
|
|
542
|
+
error?: {
|
|
543
|
+
code: string;
|
|
544
|
+
message: string;
|
|
545
|
+
};
|
|
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>;
|
|
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
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Local asset cache.
|
|
604
|
+
*
|
|
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.
|
|
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
|
+
}
|
|
652
|
+
|
|
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;
|
|
666
|
+
/**
|
|
667
|
+
* Per-workspace state dir. Caller should pass ~/.prismer/<wid>/ so different
|
|
668
|
+
* paired workspaces don't race on the cursor file.
|
|
669
|
+
*/
|
|
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;
|
|
746
|
+
/**
|
|
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.
|
|
751
|
+
*/
|
|
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);
|
|
763
|
+
/**
|
|
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).
|
|
768
|
+
*/
|
|
769
|
+
acquire(input: {
|
|
770
|
+
assetId: string;
|
|
771
|
+
ingestVersion: number;
|
|
772
|
+
}): Promise<AcquireResult>;
|
|
773
|
+
/**
|
|
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.
|
|
777
|
+
*/
|
|
778
|
+
sendHeartbeat(claimId: string): Promise<ParseClaim>;
|
|
779
|
+
/**
|
|
780
|
+
* Mark a claim complete. Throws `ClaimLostError` on 410 (claim was
|
|
781
|
+
* stolen before the worker finished — output should be discarded).
|
|
782
|
+
*/
|
|
783
|
+
complete(claimId: string): Promise<ParseClaim>;
|
|
784
|
+
/**
|
|
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.
|
|
793
|
+
*/
|
|
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;
|
|
1055
|
+
/**
|
|
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).
|
|
1059
|
+
*/
|
|
1060
|
+
outboxDir?: string;
|
|
1061
|
+
}
|
|
1062
|
+
declare class OutboxWatcher {
|
|
1063
|
+
private opts;
|
|
1064
|
+
private timer?;
|
|
1065
|
+
private uploaded;
|
|
1066
|
+
/**
|
|
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.
|
|
1077
|
+
*/
|
|
1078
|
+
private activeTasks;
|
|
1079
|
+
/**
|
|
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.
|
|
1085
|
+
*/
|
|
1086
|
+
private legacyContainerTask;
|
|
1087
|
+
private busy;
|
|
1088
|
+
/**
|
|
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.
|
|
1093
|
+
*/
|
|
1094
|
+
private pendingByTask;
|
|
1095
|
+
constructor(opts: OutboxWatcherOptions);
|
|
1096
|
+
/**
|
|
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.
|
|
1104
|
+
*/
|
|
1105
|
+
addActiveTask(task: ActiveTask): void;
|
|
1106
|
+
/**
|
|
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.
|
|
1110
|
+
*/
|
|
1111
|
+
removeActiveTask(taskId: string): void;
|
|
1112
|
+
/**
|
|
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.
|
|
1129
|
+
*/
|
|
1130
|
+
setActiveTask(task: ActiveTask | null): void;
|
|
1131
|
+
/**
|
|
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.
|
|
1135
|
+
*/
|
|
1136
|
+
flushPending(taskId: string): string[];
|
|
1137
|
+
start(): void;
|
|
1138
|
+
stop(): void;
|
|
1139
|
+
/**
|
|
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.
|
|
1144
|
+
*/
|
|
1145
|
+
scanNow(): Promise<void>;
|
|
1146
|
+
private log;
|
|
1147
|
+
/**
|
|
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).
|
|
1156
|
+
*/
|
|
1157
|
+
private currentScanDirs;
|
|
1158
|
+
private tick;
|
|
1159
|
+
private scanDir;
|
|
1160
|
+
private upload;
|
|
1161
|
+
}
|
|
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>;
|
|
1188
|
+
/**
|
|
1189
|
+
* Concatenate context entries + current prompt. Trims oldest entries first when
|
|
1190
|
+
* total chars exceed `maxChars` (matches Track C's `trimContextWindow` behavior).
|
|
1191
|
+
*
|
|
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.
|
|
1196
|
+
*/
|
|
1197
|
+
declare function composePrompt(currentPrompt: string, context: TaskDispatchContextEntry[], maxChars: number, assetBlocks?: string[]): string;
|
|
1198
|
+
|
|
1199
|
+
declare class ServicePool {
|
|
1200
|
+
private readonly services;
|
|
1201
|
+
/**
|
|
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.
|
|
1204
|
+
*/
|
|
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;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
interface LocalServerOptions {
|
|
1214
|
+
port: number;
|
|
1215
|
+
/** Snapshot getter — runner provides current state on demand. */
|
|
1216
|
+
getState: () => LocalServerState;
|
|
1217
|
+
/**
|
|
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.
|
|
1224
|
+
*/
|
|
1225
|
+
onDispatch?: (payload: DispatchPayload, runId: string) => void;
|
|
1226
|
+
/**
|
|
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.
|
|
1229
|
+
*/
|
|
1230
|
+
onInstallAgent?: (payload: InstallAgentPayload) => Promise<InstallAgentResult>;
|
|
1231
|
+
/**
|
|
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.
|
|
1236
|
+
*/
|
|
1237
|
+
snapshotRoot?: string;
|
|
1238
|
+
/**
|
|
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`.
|
|
1244
|
+
*/
|
|
1245
|
+
attachMemory?: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
|
|
1246
|
+
/**
|
|
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.
|
|
1251
|
+
*/
|
|
1252
|
+
onAssetWrite?: (body: unknown) => Promise<AssetWriteHandlerResult>;
|
|
1253
|
+
}
|
|
1254
|
+
interface AssetWriteHandlerResult {
|
|
1255
|
+
status: 200 | 400 | 502;
|
|
1256
|
+
body: unknown;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
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.
|
|
1263
|
+
*
|
|
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).
|
|
1270
|
+
*/
|
|
1271
|
+
interface DispatchPayload {
|
|
1272
|
+
taskId: string;
|
|
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;
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
interface InstallAgentResult {
|
|
1294
|
+
ok: true;
|
|
1295
|
+
daemonId: string;
|
|
1296
|
+
installedAgent: {
|
|
1297
|
+
imUserId: string;
|
|
1298
|
+
name: string;
|
|
1299
|
+
adapterName: string;
|
|
1300
|
+
profileId: string;
|
|
1301
|
+
};
|
|
1302
|
+
hostedAgents: Array<{
|
|
1303
|
+
imUserId: string;
|
|
1304
|
+
name: string;
|
|
1305
|
+
adapterName: string;
|
|
1306
|
+
}>;
|
|
1307
|
+
}
|
|
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;
|
|
1319
|
+
}>;
|
|
1320
|
+
runningTaskIds: string[];
|
|
1321
|
+
observability?: {
|
|
1322
|
+
adapters?: Record<string, unknown>;
|
|
1323
|
+
lastTaskError?: {
|
|
1324
|
+
taskId: string;
|
|
1325
|
+
message: string;
|
|
1326
|
+
at: string;
|
|
1327
|
+
};
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
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;
|
|
1338
|
+
/**
|
|
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.
|
|
1343
|
+
*/
|
|
1344
|
+
private handleInstallAgent;
|
|
1345
|
+
/**
|
|
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`.
|
|
1353
|
+
*/
|
|
1354
|
+
private handleSnapshot;
|
|
1355
|
+
/**
|
|
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.
|
|
1361
|
+
*/
|
|
1362
|
+
private handleAssetWrite;
|
|
1363
|
+
/**
|
|
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.
|
|
1373
|
+
*/
|
|
1374
|
+
private handleDispatch;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
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;
|
|
1421
|
+
/**
|
|
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.
|
|
1425
|
+
*/
|
|
1426
|
+
private runShellCommand;
|
|
1427
|
+
snapshotState(): LocalServerState;
|
|
1428
|
+
/**
|
|
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).
|
|
1432
|
+
*/
|
|
1433
|
+
loadAgentsFromDb(): void;
|
|
1434
|
+
/**
|
|
1435
|
+
* Register an in-process AgentProfile snapshot (called by agent CLI / sync layer).
|
|
1436
|
+
* Used to populate the `agents` list in agent.host.declare.
|
|
1437
|
+
*/
|
|
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.
|
|
1471
|
+
*/
|
|
1472
|
+
/**
|
|
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)
|
|
1493
|
+
*/
|
|
1494
|
+
private flushSyncRow;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
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). */
|
|
1506
|
+
fetchImpl?: typeof fetch;
|
|
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>;
|
|
1528
|
+
|
|
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;
|
|
1590
|
+
|
|
1591
|
+
declare const CodexConfigSchema: z.ZodObject<{
|
|
1592
|
+
/** Working directory for the codex subprocess. */
|
|
1593
|
+
cwd: z.ZodString;
|
|
1594
|
+
/**
|
|
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.
|
|
1597
|
+
*/
|
|
1598
|
+
model: z.ZodDefault<z.ZodString>;
|
|
1599
|
+
/**
|
|
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
|
|
1604
|
+
*/
|
|
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>>;
|
|
1610
|
+
/**
|
|
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.
|
|
1614
|
+
*/
|
|
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;
|
|
1639
|
+
|
|
1640
|
+
declare function buildProgram(): Command;
|
|
1641
|
+
declare function runCli(argv?: string[]): Promise<void>;
|
|
1642
|
+
|
|
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 };
|