@pontia/pi-client-plugin 0.2.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,7 @@ Open the Pontia dashboard and create a pi session. You can then view the session
26
26
 
27
27
  You can also start pi in a tmux pane inside a workspace registered with Pontia.
28
28
 
29
- For Pontia installation and setup, see the [getting started guide](../../README.md#get-started).
29
+ For Pontia installation and setup, see the [quick start guide](../../README.md#quick-start).
30
30
 
31
31
  ## Troubleshooting
32
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pontia/pi-client-plugin",
3
- "version": "0.2.2",
3
+ "version": "0.4.3",
4
4
  "description": "Pi extension that connects coding-agent sessions to Pontia.",
5
5
  "keywords": [
6
6
  "pi-package",
package/src/context.ts CHANGED
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  export interface TurnContext {
4
4
  sessionId: string;
5
5
  turnId?: string;
6
- runtimeInstanceId: string;
6
+ runtimeId: string;
7
7
  input?: string;
8
8
  inboxMessageId?: string;
9
9
  clientType: "pi";
@@ -1,7 +1,7 @@
1
1
  import { createConnection, type Socket } from "node:net";
2
2
  import { isAbsolute, join } from "node:path";
3
3
 
4
- export const CONTROL_VERSION = 6;
4
+ export const CONTROL_VERSION = 7;
5
5
  export const MAX_CONTROL_FRAME_BYTES = 64 * 1024;
6
6
  // Match the former HTTP event body limit, including the RPC envelope.
7
7
  export const MAX_RPC_FRAME_BYTES = 2 * 1024 * 1024 + 1024;
@@ -9,7 +9,7 @@ const REQUEST_TIMEOUT_MS = 5_000;
9
9
 
10
10
  export interface ControlIdentity {
11
11
  sessionId: string;
12
- runtimeInstanceId: string;
12
+ runtimeId: string;
13
13
  clientSessionKey?: string;
14
14
  }
15
15
  export interface ControlInput {
@@ -392,12 +392,12 @@ export async function connectPi(
392
392
  const result = (await peer.request("runtime.attach", {
393
393
  version: CONTROL_VERSION,
394
394
  session_id: identity!.sessionId,
395
- runtime_instance_id: identity!.runtimeInstanceId,
395
+ runtime_id: identity!.runtimeId,
396
396
  client_session_key: identity!.clientSessionKey,
397
397
  })) as Record<string, unknown>;
398
398
  if (
399
399
  result?.session_id !== identity!.sessionId ||
400
- result?.runtime_instance_id !== identity!.runtimeInstanceId
400
+ result?.runtime_id !== identity!.runtimeId
401
401
  ) {
402
402
  throw new RpcError(-32009, "Pi reconnect identity mismatch");
403
403
  }
@@ -425,15 +425,12 @@ export async function connectPi(
425
425
  if (method === "turn.startFailure") {
426
426
  if (!identity?.clientSessionKey || stopped)
427
427
  throw new Error("Pi reporting identity is unavailable");
428
- const peer = await open(true);
429
- try {
430
- return await peer.request(method, {
431
- ...params,
432
- client_session_key: identity.clientSessionKey,
433
- });
434
- } finally {
435
- peer.close();
436
- }
428
+ await ready();
429
+ if (!current || stopped) throw new Error("Pi connection is unavailable");
430
+ return current.request(method, {
431
+ ...params,
432
+ client_session_key: identity.clientSessionKey,
433
+ });
437
434
  }
438
435
  await ready();
439
436
  if (!current || stopped) return Promise.reject(new Error("Pi connection is unavailable"));
package/src/events.ts CHANGED
@@ -329,7 +329,7 @@ export function buildTurnStartedEvent(
329
329
  topologyContext?: PiTopologyContext,
330
330
  ): InternalEvent {
331
331
  const data: Record<string, unknown> = {
332
- runtime_instance_id: context.runtimeInstanceId,
332
+ runtime_id: context.runtimeId,
333
333
  input_summary:
334
334
  context.input === undefined
335
335
  ? undefined
@@ -378,7 +378,7 @@ export function buildTurnInterruptedEvent(
378
378
 
379
379
  export function buildSessionReadyEvent(context: SessionContext): InternalEvent {
380
380
  const payload: Record<string, unknown> = {
381
- runtime_instance_id: context.runtimeInstanceId,
381
+ runtime_id: context.runtimeId,
382
382
  };
383
383
  if (context.clientSessionKey) payload.client_session_key = context.clientSessionKey;
384
384
  if (context.clientSessionFile) payload.client_session_file = context.clientSessionFile;
@@ -398,7 +398,7 @@ export function buildSessionExitedEvent(context: SessionContext, reason: string)
398
398
  type: "session.exited",
399
399
  data: {
400
400
  reason,
401
- runtime_instance_id: context.runtimeInstanceId,
401
+ runtime_id: context.runtimeId,
402
402
  },
403
403
  };
404
404
  }
package/src/index.ts CHANGED
@@ -53,7 +53,7 @@ import { isActiveRegisteredWorkspace } from "./workspace.js";
53
53
 
54
54
  interface ReporterLike {
55
55
  report(
56
- context: { runtimeInstanceId: string },
56
+ context: { runtimeId: string },
57
57
  event: InternalEvent,
58
58
  ): Promise<EventReportResult | boolean>;
59
59
  }
@@ -205,7 +205,7 @@ export function createPontiaPiExtension(
205
205
  const result = await makeReporter(currentHookLogFile()).report(context, {
206
206
  session_id: context.sessionId,
207
207
  type: "session.model_updated",
208
- data: { model: modelId(model), runtime_instance_id: context.runtimeInstanceId },
208
+ data: { model: modelId(model), runtime_id: context.runtimeId },
209
209
  });
210
210
  if (!reportAccepted(result)) throw new Error("Pi model observation was not accepted");
211
211
  }
@@ -361,7 +361,7 @@ export function createPontiaPiExtension(
361
361
  if (!runtimeIdentity) return undefined;
362
362
  return {
363
363
  sessionId: runtimeIdentity.sessionId,
364
- runtimeInstanceId: runtimeIdentity.runtimeInstanceId,
364
+ runtimeId: runtimeIdentity.runtimeId,
365
365
  clientType: "pi",
366
366
  };
367
367
  }
@@ -399,7 +399,7 @@ export function createPontiaPiExtension(
399
399
  const body = await connection.request("branch.resolve", {
400
400
  inbox_message_id: inboxMessageId,
401
401
  session_id: commandContext.sessionId,
402
- runtime_instance_id: commandContext.runtimeInstanceId,
402
+ runtime_id: commandContext.runtimeId,
403
403
  client_type: "pi",
404
404
  });
405
405
  const replay = asRecord(asRecord(body)?.branch_replay);
@@ -408,7 +408,7 @@ export function createPontiaPiExtension(
408
408
  if (
409
409
  optionalString(replay?.inbox_message_id) !== inboxMessageId ||
410
410
  optionalString(replay?.session_id) !== commandContext.sessionId ||
411
- optionalString(replay?.runtime_instance_id) !== commandContext.runtimeInstanceId ||
411
+ optionalString(replay?.runtime_id) !== commandContext.runtimeId ||
412
412
  optionalString(replay?.client_type) !== "pi" ||
413
413
  !replacementInput ||
414
414
  !targetEntryId
@@ -452,7 +452,7 @@ export function createPontiaPiExtension(
452
452
  const current = await currentManagedSessionContext();
453
453
  if (
454
454
  current?.sessionId !== commandContext.sessionId ||
455
- current?.runtimeInstanceId !== commandContext.runtimeInstanceId
455
+ current?.runtimeId !== commandContext.runtimeId
456
456
  ) {
457
457
  throw new Error("branch replay Runtime is no longer current");
458
458
  }
@@ -636,15 +636,15 @@ export function createPontiaPiExtension(
636
636
  }
637
637
 
638
638
  context = await bindSession(await registrationConnection(), env, sessionDetails, {
639
- runtimeInstanceId:
639
+ runtimeId:
640
640
  reason === "reload" || existingSession?.sessionState === "starting"
641
- ? existingSession?.runtimeInstanceId
641
+ ? existingSession?.runtimeId
642
642
  : undefined,
643
643
  });
644
644
  if (
645
645
  reason === "reload" &&
646
646
  (context?.sessionId !== existingSession!.sessionId ||
647
- context.runtimeInstanceId !== existingSession!.runtimeInstanceId)
647
+ context.runtimeId !== existingSession!.runtimeId)
648
648
  ) {
649
649
  reportingDisabled = true;
650
650
  deferredManualSessionDetails = undefined;
@@ -770,7 +770,7 @@ export function createPontiaPiExtension(
770
770
  }
771
771
  const turnContext: TurnContext = {
772
772
  sessionId: boundSessionContext.sessionId,
773
- runtimeInstanceId: boundSessionContext.runtimeInstanceId,
773
+ runtimeId: boundSessionContext.runtimeId,
774
774
  clientType: "pi",
775
775
  ...(submission ?? { input: pendingPrompt }),
776
776
  };
@@ -236,7 +236,7 @@ export class LiveOutputPublisher implements LiveOutputPublisherLike {
236
236
  return {
237
237
  session_id: this.context.sessionId,
238
238
  turn_id: this.context.turnId,
239
- runtime_instance_id: this.context.runtimeInstanceId,
239
+ runtime_id: this.context.runtimeId,
240
240
  stream_id: this.streamId,
241
241
  };
242
242
  }
@@ -6,7 +6,7 @@ const execFileAsync = promisify(execFile);
6
6
 
7
7
  export interface ManagedRuntimeIdentity {
8
8
  sessionId: string;
9
- runtimeInstanceId: string;
9
+ runtimeId: string;
10
10
  }
11
11
 
12
12
  export function hasTmuxPaneEnvironment(env: EnvLike = process.env): boolean {
@@ -43,12 +43,12 @@ export async function loadPontiaManagedRuntimeIdentity(
43
43
  const socketPath = env.TMUX!.trim().split(",", 1)[0]!.trim();
44
44
  const paneId = env.TMUX_PANE!.trim();
45
45
 
46
- const [sessionId, runtimeInstanceId] = await Promise.all([
46
+ const [sessionId, runtimeId] = await Promise.all([
47
47
  paneOption(socketPath, paneId, "@pontia_session_id"),
48
- paneOption(socketPath, paneId, "@pontia_runtime_instance_id"),
48
+ paneOption(socketPath, paneId, "@pontia_runtime_id"),
49
49
  ]);
50
- if (!sessionId || !runtimeInstanceId) return undefined;
51
- return { sessionId, runtimeInstanceId };
50
+ if (!sessionId || !runtimeId) return undefined;
51
+ return { sessionId, runtimeId };
52
52
  }
53
53
 
54
54
  export async function isPontiaManagedTmuxPane(env: EnvLike = process.env): Promise<boolean> {
package/src/reporter.ts CHANGED
@@ -22,13 +22,10 @@ export class EventReporter {
22
22
  this.logFile = options.logFile;
23
23
  }
24
24
 
25
- async report(
26
- context: { runtimeInstanceId: string },
27
- event: InternalEvent,
28
- ): Promise<EventReportResult> {
25
+ async report(context: { runtimeId: string }, event: InternalEvent): Promise<EventReportResult> {
29
26
  try {
30
27
  const body = await this.connection.request("event.report", {
31
- runtime_instance_id: context.runtimeInstanceId,
28
+ runtime_id: context.runtimeId,
32
29
  event,
33
30
  });
34
31
  const record =
@@ -69,7 +66,7 @@ export class EventReporter {
69
66
  event: InternalEvent,
70
67
  reason: "event_rejected" | "transport_failed" | "missing_turn_id",
71
68
  ): Promise<void> {
72
- if (event.type !== "turn.started" || typeof event.data.runtime_instance_id !== "string") return;
69
+ if (event.type !== "turn.started" || typeof event.data.runtime_id !== "string") return;
73
70
  // A lost turn.started response is ambiguous: retrying it could create a
74
71
  // second Turn. Only this fenced, idempotent failure notification is retried.
75
72
  for (let attempt = 0; attempt < 3; attempt++) {
@@ -77,7 +74,7 @@ export class EventReporter {
77
74
  try {
78
75
  const result = (await this.connection.request("turn.startFailure", {
79
76
  session_id: event.session_id,
80
- runtime_instance_id: event.data.runtime_instance_id,
77
+ runtime_id: event.data.runtime_id,
81
78
  reason,
82
79
  })) as { accepted?: unknown } | null;
83
80
  if (result?.accepted === true) return;
@@ -42,7 +42,7 @@ export async function bindSession(
42
42
  connection: PiConnection,
43
43
  env: EnvLike,
44
44
  sessionDetails: PiSessionDetails,
45
- options: { startKind?: "fork"; parentSessionId?: string; runtimeInstanceId?: string } = {},
45
+ options: { startKind?: "fork"; parentSessionId?: string; runtimeId?: string } = {},
46
46
  ): Promise<SessionContext | undefined> {
47
47
  if (!sessionDetails.clientSessionKey) return undefined;
48
48
  const tmux = tmuxBindingFromEnv(env);
@@ -57,7 +57,7 @@ export async function bindSession(
57
57
  launch_cwd: sessionDetails.clientCwd,
58
58
  ...(options.startKind ? { start_kind: options.startKind } : {}),
59
59
  ...(options.parentSessionId ? { parent_session_id: options.parentSessionId } : {}),
60
- ...(options.runtimeInstanceId ? { runtime_instance_id: options.runtimeInstanceId } : {}),
60
+ ...(options.runtimeId ? { runtime_id: options.runtimeId } : {}),
61
61
  ...(tmux ? { tmux } : {}),
62
62
  },
63
63
  });
@@ -66,14 +66,14 @@ export async function bindSession(
66
66
  const session = asRecord(record?.session);
67
67
  const runtime = asRecord(record?.runtime);
68
68
  const sessionId = optionalString(session?.session_id);
69
- const resolvedRuntimeInstanceId = optionalString(runtime?.runtime_instance_id);
69
+ const resolvedRuntimeId = optionalString(runtime?.runtime_id);
70
70
  if (!sessionId) throw new Error("runtime binding upsert response missing session.session_id");
71
- if (!resolvedRuntimeInstanceId)
72
- throw new Error("runtime binding upsert response missing runtime.runtime_instance_id");
71
+ if (!resolvedRuntimeId)
72
+ throw new Error("runtime binding upsert response missing runtime.runtime_id");
73
73
  return {
74
74
  sessionId,
75
75
  clientType: "pi",
76
- runtimeInstanceId: resolvedRuntimeInstanceId,
76
+ runtimeId: resolvedRuntimeId,
77
77
  ...sessionDetails,
78
78
  };
79
79
  }
@@ -95,15 +95,15 @@ export async function loadExistingSessionContext(
95
95
  const sessionId = optionalString(record?.session_id);
96
96
  const sessionState = optionalString(record?.session_state);
97
97
  const clientType = optionalString(record?.client_type);
98
- const runtimeInstanceId = optionalString(record?.runtime_instance_id);
99
- if (!sessionId || !sessionState || clientType !== "pi" || !runtimeInstanceId) {
98
+ const runtimeId = optionalString(record?.runtime_id);
99
+ if (!sessionId || !sessionState || clientType !== "pi" || !runtimeId) {
100
100
  throw new Error("agent binding session context lookup returned an invalid context");
101
101
  }
102
102
  return {
103
103
  sessionId,
104
104
  sessionState,
105
105
  clientType: "pi",
106
- runtimeInstanceId,
106
+ runtimeId,
107
107
  ...sessionDetails,
108
108
  };
109
109
  }
package/src/session.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export interface SessionContext {
2
2
  sessionId: string;
3
3
  clientType: "pi";
4
- runtimeInstanceId: string;
4
+ runtimeId: string;
5
5
  clientSessionKey?: string;
6
6
  clientSessionFile?: string;
7
7
  clientSessionDir?: string;