@h-rig/runtime 0.0.6-alpha.22 → 0.0.6-alpha.23

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.
Files changed (25) hide show
  1. package/dist/bin/rig-agent-dispatch.js +333 -23
  2. package/dist/src/control-plane/agent-wrapper.js +336 -23
  3. package/dist/src/control-plane/harness-main.js +142 -17
  4. package/dist/src/control-plane/hooks/completion-verification.js +142 -17
  5. package/dist/src/control-plane/native/harness-cli.js +142 -17
  6. package/dist/src/control-plane/native/pr-automation.js +142 -17
  7. package/dist/src/control-plane/native/pr-review-gate.js +142 -17
  8. package/dist/src/control-plane/native/run-ops.js +1 -1
  9. package/dist/src/control-plane/native/task-ops.js +142 -17
  10. package/dist/src/control-plane/native/verifier.js +142 -17
  11. package/dist/src/control-plane/pi-sessiond/bin.js +793 -0
  12. package/dist/src/control-plane/pi-sessiond/client.js +41 -0
  13. package/dist/src/control-plane/pi-sessiond/event-hub.js +59 -0
  14. package/dist/src/control-plane/pi-sessiond/extension-ui-context.js +198 -0
  15. package/dist/src/control-plane/pi-sessiond/launcher.js +163 -0
  16. package/dist/src/control-plane/pi-sessiond/server.js +802 -0
  17. package/dist/src/control-plane/pi-sessiond/session-service.js +540 -0
  18. package/dist/src/control-plane/pi-sessiond/types.js +1 -0
  19. package/dist/src/control-plane/runtime/index.js +17 -0
  20. package/dist/src/control-plane/runtime/isolation/home.js +17 -0
  21. package/dist/src/control-plane/runtime/isolation/index.js +17 -0
  22. package/dist/src/control-plane/runtime/isolation/runner.js +17 -0
  23. package/dist/src/control-plane/runtime/isolation.js +17 -0
  24. package/dist/src/control-plane/runtime/queue.js +17 -0
  25. package/package.json +7 -7
@@ -0,0 +1,540 @@
1
+ // @bun
2
+ // packages/runtime/src/control-plane/pi-sessiond/session-service.ts
3
+ import { randomUUID as randomUUID2 } from "crypto";
4
+ import { mkdirSync } from "fs";
5
+ import { join } from "path";
6
+ import {
7
+ AuthStorage,
8
+ createAgentSessionFromServices,
9
+ createAgentSessionRuntime,
10
+ createAgentSessionServices,
11
+ ModelRegistry,
12
+ SessionManager
13
+ } from "@earendil-works/pi-coding-agent";
14
+
15
+ // packages/runtime/src/control-plane/pi-sessiond/extension-ui-context.ts
16
+ import { randomUUID } from "crypto";
17
+ import {
18
+ initTheme
19
+ } from "@earendil-works/pi-coding-agent";
20
+ var headlessTheme = {
21
+ fg: (_color, text) => text,
22
+ bg: (_color, text) => text,
23
+ dim: (text) => text,
24
+ bold: (text) => text,
25
+ italic: (text) => text,
26
+ underline: (text) => text,
27
+ strikethrough: (text) => text,
28
+ reset: (text) => text,
29
+ getFgAnsi: () => "",
30
+ getBgAnsi: () => "",
31
+ name: "rig-headless"
32
+ };
33
+ function ensureThemeInitialized() {
34
+ try {
35
+ initTheme(undefined, false);
36
+ } catch {}
37
+ }
38
+
39
+ class RigPiExtensionUiBridge {
40
+ sessionId;
41
+ runId;
42
+ publish;
43
+ pending = new Map;
44
+ constructor(sessionId, runId, publish) {
45
+ this.sessionId = sessionId;
46
+ this.runId = runId;
47
+ this.publish = publish;
48
+ ensureThemeInitialized();
49
+ }
50
+ createContext() {
51
+ const dialog = (opts, defaultValue, request, parse) => {
52
+ if (opts?.signal?.aborted)
53
+ return Promise.resolve(defaultValue);
54
+ const id = randomUUID();
55
+ return new Promise((resolve) => {
56
+ let timeout;
57
+ const cleanup = () => {
58
+ if (timeout)
59
+ clearTimeout(timeout);
60
+ opts?.signal?.removeEventListener("abort", onAbort);
61
+ this.pending.delete(id);
62
+ };
63
+ const finish = (value) => {
64
+ cleanup();
65
+ resolve(value);
66
+ };
67
+ const onAbort = () => finish(defaultValue);
68
+ opts?.signal?.addEventListener("abort", onAbort, { once: true });
69
+ if (opts?.timeout)
70
+ timeout = setTimeout(() => finish(defaultValue), opts.timeout);
71
+ this.pending.set(id, {
72
+ timeout,
73
+ abort: onAbort,
74
+ resolve: (raw) => finish(parse(normalizeResponse(raw)))
75
+ });
76
+ this.publish({
77
+ type: "extension_ui_request",
78
+ sessionId: this.sessionId,
79
+ runId: this.runId,
80
+ request: { id, timeout: opts?.timeout, ...request }
81
+ });
82
+ });
83
+ };
84
+ return {
85
+ select: (title, options, opts) => dialog(opts, undefined, { method: "select", title, options }, (response) => {
86
+ if (response.cancelled)
87
+ return;
88
+ return typeof response.value === "string" ? response.value : undefined;
89
+ }),
90
+ confirm: (title, message, opts) => dialog(opts, false, { method: "confirm", title, message }, (response) => {
91
+ if (response.cancelled)
92
+ return false;
93
+ return response.confirmed === true || response.value === true;
94
+ }),
95
+ input: (title, placeholder, opts) => dialog(opts, undefined, { method: "input", title, placeholder }, (response) => {
96
+ if (response.cancelled)
97
+ return;
98
+ return typeof response.value === "string" ? response.value : undefined;
99
+ }),
100
+ notify: (message, type) => {
101
+ this.fire({ method: "notify", message, notifyType: type });
102
+ },
103
+ onTerminalInput: () => () => {},
104
+ setStatus: (key, text) => {
105
+ this.fire({ method: "setStatus", statusKey: key, statusText: text });
106
+ },
107
+ setWorkingMessage: (message) => {
108
+ this.fire({ method: "setWorkingMessage", message });
109
+ },
110
+ setWorkingVisible: (visible) => {
111
+ this.fire({ method: "setWorkingVisible", visible });
112
+ },
113
+ setWorkingIndicator: (options) => {
114
+ this.fire({ method: "setWorkingIndicator", options });
115
+ },
116
+ setHiddenThinkingLabel: (label) => {
117
+ this.fire({ method: "setHiddenThinkingLabel", label });
118
+ },
119
+ setWidget: (key, content, options) => {
120
+ if (content === undefined || Array.isArray(content)) {
121
+ this.fire({ method: "setWidget", widgetKey: key, widgetLines: content, widgetPlacement: options?.placement });
122
+ return;
123
+ }
124
+ this.fire({ method: "notify", message: `Extension widget '${key}' uses a component factory that Rig daemon mode cannot render.`, notifyType: "warning" });
125
+ },
126
+ setFooter: (factory) => {
127
+ if (factory !== undefined)
128
+ this.fire({ method: "notify", message: "Custom extension footers are not rendered in Rig daemon mode.", notifyType: "warning" });
129
+ },
130
+ setHeader: (factory) => {
131
+ if (factory !== undefined)
132
+ this.fire({ method: "notify", message: "Custom extension headers are not rendered in Rig daemon mode.", notifyType: "warning" });
133
+ },
134
+ setTitle: (title) => {
135
+ this.fire({ method: "setTitle", title });
136
+ },
137
+ custom: async () => {
138
+ this.fire({ method: "notify", message: "Custom extension UI components are not supported in Rig daemon mode.", notifyType: "warning" });
139
+ return;
140
+ },
141
+ pasteToEditor: (text) => {
142
+ this.fire({ method: "set_editor_text", text });
143
+ },
144
+ setEditorText: (text) => {
145
+ this.fire({ method: "set_editor_text", text });
146
+ },
147
+ getEditorText: () => "",
148
+ editor: (title, prefill) => dialog(undefined, undefined, { method: "editor", title, prefill }, (response) => {
149
+ if (response.cancelled)
150
+ return;
151
+ return typeof response.value === "string" ? response.value : undefined;
152
+ }),
153
+ addAutocompleteProvider: () => {},
154
+ setEditorComponent: (factory) => {
155
+ if (factory !== undefined)
156
+ this.fire({ method: "notify", message: "Custom editor components are not supported in Rig daemon mode.", notifyType: "warning" });
157
+ },
158
+ getEditorComponent: () => {
159
+ return;
160
+ },
161
+ get theme() {
162
+ return headlessTheme;
163
+ },
164
+ getAllThemes: () => [],
165
+ getTheme: () => {
166
+ return;
167
+ },
168
+ setTheme: () => ({ success: false, error: "Theme switching is not supported in Rig daemon mode" }),
169
+ getToolsExpanded: () => false,
170
+ setToolsExpanded: () => {}
171
+ };
172
+ }
173
+ respond(input) {
174
+ const pending = this.pending.get(input.requestId);
175
+ if (!pending)
176
+ return false;
177
+ pending.resolve(input);
178
+ return true;
179
+ }
180
+ cancelAll() {
181
+ for (const [id, pending] of this.pending.entries()) {
182
+ if (pending.timeout)
183
+ clearTimeout(pending.timeout);
184
+ pending.abort?.();
185
+ this.pending.delete(id);
186
+ pending.resolve({ requestId: id, cancelled: true });
187
+ }
188
+ }
189
+ fire(request) {
190
+ this.publish({
191
+ type: "extension_ui_request",
192
+ sessionId: this.sessionId,
193
+ runId: this.runId,
194
+ request: { id: randomUUID(), ...request }
195
+ });
196
+ }
197
+ }
198
+ function normalizeResponse(value) {
199
+ if (!value || typeof value !== "object" || Array.isArray(value))
200
+ return { requestId: "", cancelled: true };
201
+ const record = value;
202
+ return {
203
+ requestId: typeof record.requestId === "string" ? record.requestId : "",
204
+ value: record.value,
205
+ confirmed: typeof record.confirmed === "boolean" ? record.confirmed : undefined,
206
+ cancelled: record.cancelled === true
207
+ };
208
+ }
209
+
210
+ // packages/runtime/src/control-plane/pi-sessiond/session-service.ts
211
+ var BUILTIN_COMMANDS = [
212
+ { name: "session", description: "Show session info and stats", source: "builtin" },
213
+ { name: "name", description: "Set session display name", source: "builtin" },
214
+ { name: "compact", description: "Manually compact session context", source: "builtin" },
215
+ { name: "reload", description: "Reload keybindings, extensions, skills, prompts, and themes", source: "builtin" },
216
+ { name: "quit", description: "Detach from the current local Pi frontend", source: "builtin" }
217
+ ];
218
+
219
+ class RigPiSessionService {
220
+ hub;
221
+ config;
222
+ activeBySession = new Map;
223
+ sessionIdByRun = new Map;
224
+ heartbeat;
225
+ constructor(hub, config) {
226
+ this.hub = hub;
227
+ this.config = config;
228
+ this.heartbeat = setInterval(() => this.publishHeartbeats(), 2000);
229
+ }
230
+ activeCount() {
231
+ return this.activeBySession.size;
232
+ }
233
+ async dispose() {
234
+ clearInterval(this.heartbeat);
235
+ const sessions = [...this.activeBySession.values()];
236
+ this.activeBySession.clear();
237
+ this.sessionIdByRun.clear();
238
+ await Promise.all(sessions.map(async (active) => {
239
+ active.unsubscribe();
240
+ active.ui.cancelAll();
241
+ try {
242
+ await active.runtime.session.abort();
243
+ } catch {}
244
+ await active.runtime.dispose();
245
+ }));
246
+ }
247
+ async startSession(input) {
248
+ const existing = this.getByRun(input.runId);
249
+ if (existing)
250
+ return existing.metadata;
251
+ mkdirSync(input.agentDir, { recursive: true });
252
+ mkdirSync(input.sessionDir, { recursive: true });
253
+ const authStorage = AuthStorage.create(join(input.agentDir, "auth.json"));
254
+ const modelRegistry = ModelRegistry.create(authStorage, join(input.agentDir, "models.json"));
255
+ const createRuntime = async ({ cwd, agentDir, sessionManager: sessionManager2, sessionStartEvent }) => {
256
+ const services = await createAgentSessionServices({ cwd, agentDir, authStorage, modelRegistry });
257
+ const result = await createAgentSessionFromServices({ services, sessionManager: sessionManager2, sessionStartEvent });
258
+ return { ...result, services, diagnostics: services.diagnostics };
259
+ };
260
+ const sessionManager = SessionManager.create(input.cwd, input.sessionDir);
261
+ const runtime = await createAgentSessionRuntime(createRuntime, {
262
+ cwd: input.cwd,
263
+ agentDir: input.agentDir,
264
+ sessionManager,
265
+ sessionStartEvent: { type: "session_start", reason: "new" }
266
+ });
267
+ const now = new Date().toISOString();
268
+ const metadata = {
269
+ runId: input.runId,
270
+ sessionId: runtime.session.sessionId,
271
+ daemonId: this.config.daemonId,
272
+ cwd: input.cwd,
273
+ agentDir: input.agentDir,
274
+ sessionDir: input.sessionDir,
275
+ transport: "http-control-ws-events",
276
+ serverBasePath: `/api/runs/${encodeURIComponent(input.runId)}/pi`,
277
+ eventsPath: `/api/runs/${encodeURIComponent(input.runId)}/pi/events`,
278
+ createdAt: now,
279
+ updatedAt: now,
280
+ backend: {
281
+ kind: "worker-pi-sessiond",
282
+ version: this.config.version,
283
+ commit: this.config.commit,
284
+ pid: process.pid
285
+ }
286
+ };
287
+ const active = {
288
+ runId: input.runId,
289
+ runtime,
290
+ metadata,
291
+ unsubscribe: () => {},
292
+ ui: new RigPiExtensionUiBridge(runtime.session.sessionId, input.runId, (event) => this.publish(runtime.session.sessionId, input.runId, event))
293
+ };
294
+ await this.bindRuntime(active);
295
+ if (input.sessionName?.trim())
296
+ runtime.session.setSessionName(input.sessionName.trim());
297
+ this.activeBySession.set(runtime.session.sessionId, active);
298
+ this.sessionIdByRun.set(input.runId, runtime.session.sessionId);
299
+ this.publish(runtime.session.sessionId, input.runId, { type: "ready", metadata });
300
+ this.publishStatus(active);
301
+ this.publishActivity(active, "ready", "idle");
302
+ return metadata;
303
+ }
304
+ getByRun(runId) {
305
+ const sessionId = this.sessionIdByRun.get(runId);
306
+ return sessionId ? this.activeBySession.get(sessionId) : undefined;
307
+ }
308
+ getBySession(sessionId) {
309
+ return this.activeBySession.get(sessionId);
310
+ }
311
+ messages(sessionId) {
312
+ const active = this.requireActive(sessionId);
313
+ return [...active.runtime.session.messages];
314
+ }
315
+ status(sessionId) {
316
+ return this.statusFromActive(this.requireActive(sessionId));
317
+ }
318
+ commands(sessionId) {
319
+ const session = this.requireActive(sessionId).runtime.session;
320
+ const commands = [...BUILTIN_COMMANDS];
321
+ for (const command of session.extensionRunner.getRegisteredCommands()) {
322
+ commands.push({ name: command.invocationName, description: command.description, source: "extension" });
323
+ }
324
+ for (const template of session.promptTemplates) {
325
+ commands.push({ name: template.name, description: template.description, source: "prompt" });
326
+ }
327
+ for (const skill of session.resourceLoader.getSkills().skills) {
328
+ commands.push({ name: `skill:${skill.name}`, description: skill.description, source: "skill" });
329
+ }
330
+ return commands.sort((a, b) => a.name.localeCompare(b.name));
331
+ }
332
+ async prompt(sessionId, text, streamingBehavior) {
333
+ const active = this.requireActive(sessionId);
334
+ const session = active.runtime.session;
335
+ const behavior = session.isStreaming || session.isCompacting ? streamingBehavior ?? "steer" : undefined;
336
+ this.publishActivity(active, behavior === "followUp" ? "follow-up queued" : behavior === "steer" ? "steering queued" : "prompt accepted", "active");
337
+ session.prompt(text, behavior ? { streamingBehavior: behavior, source: "rpc" } : { source: "rpc" }).catch((error) => {
338
+ const message = error instanceof Error ? error.message : String(error);
339
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "error", message });
340
+ this.publishActivity(active, "prompt failed", "error", message);
341
+ });
342
+ this.publishStatus(active);
343
+ return { accepted: true };
344
+ }
345
+ async shell(sessionId, text) {
346
+ const active = this.requireActive(sessionId);
347
+ const session = active.runtime.session;
348
+ const excluded = text.startsWith("!!");
349
+ const command = (excluded ? text.slice(2) : text.startsWith("!") ? text.slice(1) : text).trim();
350
+ if (!command)
351
+ throw new Error("Usage: !<shell command>");
352
+ if (session.isBashRunning)
353
+ throw new Error("A bash command is already running");
354
+ this.publishActivity(active, "running bash", "active", command);
355
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.start", command, excludeFromContext: excluded } });
356
+ session.executeBash(command, (chunk) => {
357
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.chunk", chunk } });
358
+ this.publishActivity(active, "running bash", "active", command);
359
+ this.publishStatus(active);
360
+ }, { excludeFromContext: excluded }).then((result) => {
361
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.end", ...result } });
362
+ this.publishActivity(active, "bash complete", result.exitCode === 0 ? "idle" : "error", command);
363
+ this.publishStatus(active);
364
+ }).catch((error) => {
365
+ const message = error instanceof Error ? error.message : String(error);
366
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.end", output: message, isError: true } });
367
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "error", message });
368
+ this.publishActivity(active, "bash failed", "error", message);
369
+ this.publishStatus(active);
370
+ });
371
+ return { accepted: true };
372
+ }
373
+ async runCommand(sessionId, text) {
374
+ const active = this.requireActive(sessionId);
375
+ const trimmed = text.trim();
376
+ const [rawName = "", ...args] = trimmed.replace(/^\//, "").split(/\s+/);
377
+ const rest = args.join(" ").trim();
378
+ if (rawName === "session")
379
+ return { type: "done", message: formatSessionStats(active.runtime.session) };
380
+ if (rawName === "name") {
381
+ if (!rest)
382
+ return { type: "unsupported", message: "Usage: /name <session name>" };
383
+ active.runtime.session.setSessionName(rest);
384
+ this.publishStatus(active);
385
+ return { type: "done", message: `Session named: ${rest}` };
386
+ }
387
+ if (rawName === "compact") {
388
+ active.runtime.session.compact(rest || undefined).catch((error) => {
389
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "error", message: error instanceof Error ? error.message : String(error) });
390
+ });
391
+ return { type: "done", message: "Compaction started\u2026" };
392
+ }
393
+ if (rawName === "reload") {
394
+ await active.runtime.session.reload();
395
+ await this.bindRuntime(active);
396
+ return { type: "done", message: "Pi resources reloaded" };
397
+ }
398
+ if (rawName === "quit")
399
+ return { type: "done", message: "Detach locally with /detach or /quit." };
400
+ await this.prompt(sessionId, trimmed, active.runtime.session.isStreaming ? "steer" : undefined);
401
+ return { type: "done", message: `Accepted ${trimmed}` };
402
+ }
403
+ respondToCommand(_sessionId, _requestId, _value) {
404
+ return { type: "unsupported", message: "No pending command selection is active." };
405
+ }
406
+ respondToExtensionUi(sessionId, input) {
407
+ const active = this.requireActive(sessionId);
408
+ return { accepted: active.ui.respond(input) };
409
+ }
410
+ async abort(sessionId) {
411
+ const active = this.requireActive(sessionId);
412
+ active.runtime.session.clearQueue();
413
+ await active.runtime.session.abort();
414
+ this.publishActivity(active, "stopped", "idle");
415
+ this.publishStatus(active);
416
+ return { aborted: true };
417
+ }
418
+ async bindRuntime(active) {
419
+ active.unsubscribe();
420
+ const session = active.runtime.session;
421
+ active.ui.cancelAll();
422
+ active.ui = new RigPiExtensionUiBridge(session.sessionId, active.runId, (event) => this.publish(session.sessionId, active.runId, event));
423
+ active.metadata = { ...active.metadata, sessionId: session.sessionId, updatedAt: new Date().toISOString() };
424
+ this.sessionIdByRun.set(active.runId, session.sessionId);
425
+ this.activeBySession.set(session.sessionId, active);
426
+ active.runtime.setRebindSession(async () => this.bindRuntime(active));
427
+ await session.bindExtensions({
428
+ uiContext: active.ui.createContext(),
429
+ commandContextActions: {
430
+ waitForIdle: () => session.agent.waitForIdle(),
431
+ newSession: async (options) => active.runtime.newSession(options),
432
+ fork: async (entryId, options) => {
433
+ const result = await active.runtime.fork(entryId, options);
434
+ return { cancelled: result.cancelled };
435
+ },
436
+ navigateTree: async (targetId, options) => session.navigateTree(targetId, options),
437
+ switchSession: async (sessionPath, options) => active.runtime.switchSession(sessionPath, options),
438
+ reload: async () => {
439
+ await session.reload();
440
+ }
441
+ },
442
+ shutdownHandler: () => {
443
+ this.abort(session.sessionId);
444
+ },
445
+ onError: (error) => {
446
+ this.publish(session.sessionId, active.runId, { type: "error", message: error.error, detail: error });
447
+ }
448
+ });
449
+ active.unsubscribe = session.subscribe((event) => {
450
+ this.publish(session.sessionId, active.runId, { type: "pi.event", sessionId: session.sessionId, runId: active.runId, event });
451
+ this.publishActivityForEvent(active, event);
452
+ this.publishStatus(active);
453
+ });
454
+ }
455
+ publish(sessionId, runId, event) {
456
+ this.hub.publish(sessionId, runId, event);
457
+ }
458
+ publishStatus(active) {
459
+ const status = this.statusFromActive(active);
460
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "status.update", sessionId: active.runtime.session.sessionId, runId: active.runId, status });
461
+ }
462
+ publishActivity(active, label, phase, detail) {
463
+ const activity = {
464
+ sessionId: active.runtime.session.sessionId,
465
+ runId: active.runId,
466
+ phase,
467
+ label,
468
+ detail,
469
+ at: new Date().toISOString()
470
+ };
471
+ active.activity = activity;
472
+ this.publish(active.runtime.session.sessionId, active.runId, { type: "activity.update", sessionId: active.runtime.session.sessionId, runId: active.runId, activity });
473
+ }
474
+ publishActivityForEvent(active, event) {
475
+ const type = event && typeof event === "object" && !Array.isArray(event) ? event.type : undefined;
476
+ if (type === "agent_start")
477
+ this.publishActivity(active, "agent running", "active");
478
+ else if (type === "agent_end")
479
+ this.publishActivity(active, "idle", "idle");
480
+ else if (type === "tool_execution_start")
481
+ this.publishActivity(active, `tool: ${String(event.toolName ?? "tool")}`, "active");
482
+ else if (type === "compaction_start")
483
+ this.publishActivity(active, "compacting", "active");
484
+ else if (type === "compaction_end")
485
+ this.publishActivity(active, "compaction complete", "idle");
486
+ }
487
+ publishHeartbeats() {
488
+ for (const active of this.activeBySession.values()) {
489
+ if (active.runtime.session.isStreaming || active.runtime.session.isCompacting || active.runtime.session.isBashRunning || active.runtime.session.pendingMessageCount > 0) {
490
+ this.publishStatus(active);
491
+ this.publishActivity(active, active.activity?.label ?? "active", "active", active.activity?.detail);
492
+ }
493
+ }
494
+ }
495
+ statusFromActive(active) {
496
+ const session = active.runtime.session;
497
+ return {
498
+ sessionId: session.sessionId,
499
+ runId: active.runId,
500
+ cwd: active.runtime.cwd,
501
+ sessionFile: session.sessionFile,
502
+ sessionName: session.sessionName,
503
+ model: session.model,
504
+ thinkingLevel: session.thinkingLevel,
505
+ isStreaming: session.isStreaming,
506
+ isCompacting: session.isCompacting,
507
+ isBashRunning: session.isBashRunning,
508
+ pendingMessageCount: session.pendingMessageCount,
509
+ messageCount: session.messages.length,
510
+ steeringMessages: session.getSteeringMessages(),
511
+ followUpMessages: session.getFollowUpMessages(),
512
+ contextUsage: session.getContextUsage(),
513
+ stats: session.getSessionStats()
514
+ };
515
+ }
516
+ requireActive(sessionId) {
517
+ const active = this.activeBySession.get(sessionId);
518
+ if (!active)
519
+ throw new Error("Pi session not found");
520
+ return active;
521
+ }
522
+ }
523
+ function formatSessionStats(session) {
524
+ const stats = session.getSessionStats();
525
+ return [
526
+ `Session: ${stats.sessionId}`,
527
+ `Messages: ${stats.totalMessages} (${stats.userMessages} user, ${stats.assistantMessages} assistant)`,
528
+ `Tool calls: ${stats.toolCalls}`,
529
+ `Tokens: \u2191${stats.tokens.input} \u2193${stats.tokens.output} total ${stats.tokens.total}`,
530
+ `Cost: $${stats.cost.toFixed(4)}`
531
+ ].join(`
532
+ `);
533
+ }
534
+ function createDaemonId() {
535
+ return `rig-pi-sessiond-${randomUUID2()}`;
536
+ }
537
+ export {
538
+ createDaemonId,
539
+ RigPiSessionService
540
+ };
@@ -0,0 +1 @@
1
+ // @bun
@@ -6218,6 +6218,21 @@ var GITHUB_KNOWN_HOSTS = [
6218
6218
  "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
6219
6219
  ].join(`
6220
6220
  `);
6221
+ function resolveControlPlaneSourceRoot(projectRoot) {
6222
+ const candidates = [
6223
+ process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
6224
+ process.env.RIG_HOST_PROJECT_ROOT?.trim(),
6225
+ resolve27(import.meta.dir, "../../../../.."),
6226
+ projectRoot
6227
+ ].filter((value) => Boolean(value));
6228
+ for (const candidate of candidates) {
6229
+ const root = resolve27(candidate);
6230
+ if (existsSync25(resolve27(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
6231
+ return root;
6232
+ }
6233
+ }
6234
+ return "";
6235
+ }
6221
6236
  async function runtimeEnv(projectRoot, runtime) {
6222
6237
  const bunBinaryPath = resolveBunBinaryPath();
6223
6238
  const bunDir = resolveBunInstallDir(bunBinaryPath);
@@ -6263,9 +6278,11 @@ async function runtimeEnv(projectRoot, runtime) {
6263
6278
  const runtimeRigGit = resolve27(runtime.binDir, runtimeRigGitFileName());
6264
6279
  const preferredShell = existsSync25(runtimeBash) ? runtimeBash : "/bin/bash";
6265
6280
  const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
6281
+ const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
6266
6282
  const env = {
6267
6283
  PROJECT_RIG_ROOT: projectRoot,
6268
6284
  RIG_HOST_PROJECT_ROOT: projectRoot,
6285
+ ...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
6269
6286
  HOME: runtime.homeDir,
6270
6287
  TMPDIR: runtime.tmpDir,
6271
6288
  XDG_CACHE_HOME: runtime.cacheDir,
@@ -750,6 +750,21 @@ var GITHUB_KNOWN_HOSTS = [
750
750
  "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
751
751
  ].join(`
752
752
  `);
753
+ function resolveControlPlaneSourceRoot(projectRoot) {
754
+ const candidates = [
755
+ process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
756
+ process.env.RIG_HOST_PROJECT_ROOT?.trim(),
757
+ resolve7(import.meta.dir, "../../../../.."),
758
+ projectRoot
759
+ ].filter((value) => Boolean(value));
760
+ for (const candidate of candidates) {
761
+ const root = resolve7(candidate);
762
+ if (existsSync6(resolve7(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
763
+ return root;
764
+ }
765
+ }
766
+ return "";
767
+ }
753
768
  async function runtimeEnv(projectRoot, runtime) {
754
769
  const bunBinaryPath = resolveBunBinaryPath();
755
770
  const bunDir = resolveBunInstallDir(bunBinaryPath);
@@ -795,9 +810,11 @@ async function runtimeEnv(projectRoot, runtime) {
795
810
  const runtimeRigGit = resolve7(runtime.binDir, runtimeRigGitFileName());
796
811
  const preferredShell = existsSync6(runtimeBash) ? runtimeBash : "/bin/bash";
797
812
  const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
813
+ const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
798
814
  const env = {
799
815
  PROJECT_RIG_ROOT: projectRoot,
800
816
  RIG_HOST_PROJECT_ROOT: projectRoot,
817
+ ...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
801
818
  HOME: runtime.homeDir,
802
819
  TMPDIR: runtime.tmpDir,
803
820
  XDG_CACHE_HOME: runtime.cacheDir,
@@ -5518,6 +5518,21 @@ var GITHUB_KNOWN_HOSTS = [
5518
5518
  "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
5519
5519
  ].join(`
5520
5520
  `);
5521
+ function resolveControlPlaneSourceRoot(projectRoot) {
5522
+ const candidates = [
5523
+ process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
5524
+ process.env.RIG_HOST_PROJECT_ROOT?.trim(),
5525
+ resolve25(import.meta.dir, "../../../../.."),
5526
+ projectRoot
5527
+ ].filter((value) => Boolean(value));
5528
+ for (const candidate of candidates) {
5529
+ const root = resolve25(candidate);
5530
+ if (existsSync24(resolve25(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
5531
+ return root;
5532
+ }
5533
+ }
5534
+ return "";
5535
+ }
5521
5536
  async function runtimeEnv(projectRoot, runtime) {
5522
5537
  const bunBinaryPath = resolveBunBinaryPath();
5523
5538
  const bunDir = resolveBunInstallDir(bunBinaryPath);
@@ -5563,9 +5578,11 @@ async function runtimeEnv(projectRoot, runtime) {
5563
5578
  const runtimeRigGit = resolve25(runtime.binDir, runtimeRigGitFileName());
5564
5579
  const preferredShell = existsSync24(runtimeBash) ? runtimeBash : "/bin/bash";
5565
5580
  const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
5581
+ const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
5566
5582
  const env = {
5567
5583
  PROJECT_RIG_ROOT: projectRoot,
5568
5584
  RIG_HOST_PROJECT_ROOT: projectRoot,
5585
+ ...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
5569
5586
  HOME: runtime.homeDir,
5570
5587
  TMPDIR: runtime.tmpDir,
5571
5588
  XDG_CACHE_HOME: runtime.cacheDir,
@@ -2148,6 +2148,21 @@ var GITHUB_KNOWN_HOSTS = [
2148
2148
  "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
2149
2149
  ].join(`
2150
2150
  `);
2151
+ function resolveControlPlaneSourceRoot(projectRoot) {
2152
+ const candidates = [
2153
+ process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
2154
+ process.env.RIG_HOST_PROJECT_ROOT?.trim(),
2155
+ resolve11(import.meta.dir, "../../../../.."),
2156
+ projectRoot
2157
+ ].filter((value) => Boolean(value));
2158
+ for (const candidate of candidates) {
2159
+ const root = resolve11(candidate);
2160
+ if (existsSync9(resolve11(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
2161
+ return root;
2162
+ }
2163
+ }
2164
+ return "";
2165
+ }
2151
2166
  async function runtimeEnv(projectRoot, runtime) {
2152
2167
  const bunBinaryPath = resolveBunBinaryPath();
2153
2168
  const bunDir = resolveBunInstallDir(bunBinaryPath);
@@ -2193,9 +2208,11 @@ async function runtimeEnv(projectRoot, runtime) {
2193
2208
  const runtimeRigGit = resolve11(runtime.binDir, runtimeRigGitFileName());
2194
2209
  const preferredShell = existsSync9(runtimeBash) ? runtimeBash : "/bin/bash";
2195
2210
  const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
2211
+ const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
2196
2212
  const env = {
2197
2213
  PROJECT_RIG_ROOT: projectRoot,
2198
2214
  RIG_HOST_PROJECT_ROOT: projectRoot,
2215
+ ...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
2199
2216
  HOME: runtime.homeDir,
2200
2217
  TMPDIR: runtime.tmpDir,
2201
2218
  XDG_CACHE_HOME: runtime.cacheDir,