@pontia/pi-client-plugin 0.1.0

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/src/index.ts ADDED
@@ -0,0 +1,586 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { defaultHookLogFile, loadTurnContext, type EnvLike, type LoadTurnContextResult, type TurnContext } from "./context.js";
3
+ import { appendDiagnostic, type DiagnosticEntry } from "./diagnostics.js";
4
+ import { pontiaHomeFromEnv, resolvePontiaConnection } from "./discovery.js";
5
+ import { buildSessionContextUsageUpdatedEvent, buildSessionExitedEvent, buildSessionMessageUpdatedEvent, buildSessionReadyEvent, buildTurnCompletedEvent, buildTurnFailedEvent, buildTurnInterruptedEvent, buildTurnOutputEvent, buildTurnStartedEvent, contextUsageFromPiHook, type InternalEvent, type PiTopologyContext, type PiTopologyEntryKind, type SessionMessageUpdatedReason } from "./events.js";
6
+ import { asRecord, optionalString, parseJsonResponse } from "./internal-api.js";
7
+ import { hasTmuxPaneEnvironment, isPontiaManagedTmuxPane, loadPontiaManagedRuntimeIdentity, type ManagedRuntimeIdentity } from "./managed-runtime.js";
8
+ import { agentEndWasInterrupted, assistantDeltaFromEvent, assistantTextFromMessage, errorMessageFromAgentEnd, isTranscriptBoundaryMessageUpdate, lastAssistantTextFromMessages } from "./pi-message.js";
9
+ import { loadProfileSystemPrompt } from "./profile.js";
10
+ import { EventReporter, type EventReportResult } from "./reporter.js";
11
+ import { bindSession, loadExistingSessionContext, piSessionDetailsFromHookContext, type PiSessionDetails } from "./runtime-binding.js";
12
+ import type { SessionContext } from "./session.js";
13
+ import { isActiveRegisteredWorkspace } from "./workspace.js";
14
+
15
+ interface ReporterLike {
16
+ report(context: { internalEventUrl: string }, event: InternalEvent): Promise<EventReportResult | boolean>;
17
+ }
18
+
19
+ function reportAccepted(result: EventReportResult | boolean): boolean {
20
+ return typeof result === "boolean" ? result : result.accepted;
21
+ }
22
+
23
+ export interface PontiaPiExtensionDependencies {
24
+ env?: EnvLike;
25
+ loadContext?: (env: EnvLike, sessionContext?: SessionContext) => Promise<LoadTurnContextResult>;
26
+ makeReporter?: (logFile: string) => ReporterLike;
27
+ logDiagnostic?: (logFile: string, entry: DiagnosticEntry) => Promise<void>;
28
+ fetch?: typeof fetch;
29
+ loadManagedRuntime?: (env: EnvLike) => Promise<ManagedRuntimeIdentity | undefined>;
30
+ isManagedPane?: (env: EnvLike) => Promise<boolean>;
31
+ }
32
+
33
+ interface ActiveTurnState {
34
+ context: TurnContext & { turnId: string };
35
+ logFile: string;
36
+ reporter: ReporterLike;
37
+ output: string;
38
+ ended: boolean;
39
+ }
40
+
41
+ function leafIdFromHookContext(ctx: unknown): string | null {
42
+ if (!ctx || typeof ctx !== "object") return null;
43
+ const sessionManager = (ctx as Record<string, unknown>).sessionManager;
44
+ if (!sessionManager || typeof sessionManager !== "object") return null;
45
+ const getLeafId = (sessionManager as Record<string, unknown>).getLeafId;
46
+ if (typeof getLeafId !== "function") return null;
47
+ const leafId = getLeafId.call(sessionManager);
48
+ return typeof leafId === "string" && leafId.length > 0 ? leafId : null;
49
+ }
50
+
51
+ const NON_MESSAGE_ENTRY_KINDS = new Set([
52
+ "thinking_level_change",
53
+ "model_change",
54
+ "compaction",
55
+ "branch_summary",
56
+ "custom",
57
+ "custom_message",
58
+ "label",
59
+ "session_info",
60
+ ]);
61
+
62
+ function topologyEntryKind(entry: Record<string, unknown>): PiTopologyEntryKind {
63
+ if (entry.type !== "message") {
64
+ return typeof entry.type === "string" && NON_MESSAGE_ENTRY_KINDS.has(entry.type)
65
+ ? entry.type as PiTopologyEntryKind
66
+ : "other";
67
+ }
68
+ const message = entry.message;
69
+ const role = message && typeof message === "object"
70
+ ? (message as Record<string, unknown>).role
71
+ : undefined;
72
+ if (role === "user") return "user_message";
73
+ if (role === "assistant") return "assistant_message";
74
+ if (role === "toolResult") return "tool_result_message";
75
+ return "other_message";
76
+ }
77
+
78
+ function topologyContextFromHookContext(ctx: unknown): PiTopologyContext | undefined {
79
+ if (!ctx || typeof ctx !== "object") return undefined;
80
+ const sessionManager = (ctx as Record<string, unknown>).sessionManager;
81
+ if (!sessionManager || typeof sessionManager !== "object") return undefined;
82
+ const getBranch = (sessionManager as Record<string, unknown>).getBranch;
83
+ if (typeof getBranch !== "function") return undefined;
84
+ try {
85
+ const branch = getBranch.call(sessionManager);
86
+ if (!Array.isArray(branch)) return undefined;
87
+ const entries = branch.map((value) => {
88
+ if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
89
+ const entry = value as Record<string, unknown>;
90
+ if (typeof entry.id !== "string" || entry.id.length === 0) return undefined;
91
+ return { id: entry.id, kind: topologyEntryKind(entry) };
92
+ });
93
+ if (entries.some((entry) => entry === undefined)) return undefined;
94
+ return { entries: entries as PiTopologyContext["entries"] };
95
+ } catch {
96
+ return undefined;
97
+ }
98
+ }
99
+
100
+ export function createPontiaPiExtension(pi: ExtensionAPI, dependencies: PontiaPiExtensionDependencies = {}): void {
101
+ const sourceEnv = dependencies.env ?? process.env;
102
+ if (!pontiaHomeFromEnv(sourceEnv) || !hasTmuxPaneEnvironment(sourceEnv)) return;
103
+ // Environment extensions may load PONTIA_HOME after extension registration.
104
+ const currentPontiaHome = () => pontiaHomeFromEnv(sourceEnv)!;
105
+ const currentEnv = () => ({ ...sourceEnv, PONTIA_HOME: currentPontiaHome() });
106
+ const currentHookLogFile = () => defaultHookLogFile(currentPontiaHome());
107
+
108
+ const contextLoader = dependencies.loadContext ?? ((contextEnv, sessionContext) => loadTurnContext(contextEnv, { sessionContext }));
109
+ const makeReporter = dependencies.makeReporter ?? ((logFile: string) => new EventReporter({ logFile }));
110
+ const logDiagnostic = dependencies.logDiagnostic ?? appendDiagnostic;
111
+ const fetchImpl = dependencies.fetch ?? fetch;
112
+ const loadManagedRuntime = dependencies.loadManagedRuntime ?? loadPontiaManagedRuntimeIdentity;
113
+ const isManagedPane = dependencies.isManagedPane ?? isPontiaManagedTmuxPane;
114
+
115
+ let activeTurn: ActiveTurnState | undefined;
116
+ let readyReported = false;
117
+ let boundSessionContext: SessionContext | undefined;
118
+ let deferredManualSessionDetails: PiSessionDetails | undefined;
119
+ let reportingDisabled = false;
120
+ let managedPaneConfirmed = false;
121
+ let lastContextUsageJson: string | undefined;
122
+ let pendingPrompt: string | undefined;
123
+
124
+ async function confirmManagedPane(refresh = false): Promise<boolean> {
125
+ if (managedPaneConfirmed && !refresh) return true;
126
+ managedPaneConfirmed = await isManagedPane(currentEnv());
127
+ return managedPaneConfirmed;
128
+ }
129
+
130
+ async function currentManagedSessionContext(): Promise<SessionContext | undefined> {
131
+ if (boundSessionContext) return boundSessionContext;
132
+ const runtimeIdentity = await loadManagedRuntime(currentEnv());
133
+ if (!runtimeIdentity) return undefined;
134
+ const connection = await resolvePontiaConnection({ pontiaHome: currentPontiaHome(), fetch: fetchImpl });
135
+ if (!connection?.internalEventUrl) return undefined;
136
+ return {
137
+ sessionId: runtimeIdentity.sessionId,
138
+ runtimeInstanceId: runtimeIdentity.runtimeInstanceId,
139
+ clientType: "pi",
140
+ internalEventUrl: connection.internalEventUrl,
141
+ };
142
+ }
143
+
144
+ pi.registerCommand("pontia-edit", {
145
+ description: "Replay a Pontia Inbox message from a historical Pi entry",
146
+ handler: async (args, ctx) => {
147
+ const inboxMessageId = args.trim();
148
+ const logFile = currentHookLogFile();
149
+ if (!/^msg_[^\s]+$/.test(inboxMessageId)) {
150
+ await logDiagnostic(logFile, {
151
+ level: "error",
152
+ code: "branch_replay_invalid_command",
153
+ message: "pontia-edit requires exactly one Inbox Message identifier",
154
+ });
155
+ return;
156
+ }
157
+
158
+ const commandContext = await currentManagedSessionContext();
159
+ if (!commandContext) {
160
+ await logDiagnostic(logFile, {
161
+ level: "error",
162
+ code: "branch_replay_stale_context",
163
+ message: "pontia-edit requires a bound Pontia Session and Runtime",
164
+ });
165
+ return;
166
+ }
167
+ const loaded = { logFile };
168
+ const connection = await resolvePontiaConnection({ pontiaHome: currentPontiaHome() });
169
+ if (!connection?.externalApiToken) {
170
+ await logDiagnostic(loaded.logFile, {
171
+ level: "error",
172
+ code: "branch_replay_stale_context",
173
+ message: "pontia-edit requires an authenticated Pontia connection",
174
+ });
175
+ return;
176
+ }
177
+ const resolveUrl = commandContext.internalEventUrl.replace(
178
+ /\/events\/?$/,
179
+ "/inbox/branch-replay/resolve",
180
+ );
181
+
182
+ let replacementInput: string;
183
+ let targetEntryId: string;
184
+ try {
185
+ const response = await fetchImpl(resolveUrl, {
186
+ method: "POST",
187
+ headers: {
188
+ Authorization: `Bearer ${connection.externalApiToken}`,
189
+ "Content-Type": "application/json",
190
+ },
191
+ body: JSON.stringify({
192
+ inbox_message_id: inboxMessageId,
193
+ session_id: commandContext.sessionId,
194
+ runtime_instance_id: commandContext.runtimeInstanceId,
195
+ client_type: "pi",
196
+ }),
197
+ });
198
+ const body = await parseJsonResponse(response);
199
+ if (!response.ok) {
200
+ throw new Error(`${response.status} ${response.statusText}`);
201
+ }
202
+ const replay = asRecord(asRecord(asRecord(body)?.data)?.branch_replay);
203
+ replacementInput = optionalString(replay?.replacement_input) ?? "";
204
+ targetEntryId = optionalString(replay?.target_entry_id) ?? "";
205
+ if (
206
+ optionalString(replay?.inbox_message_id) !== inboxMessageId ||
207
+ optionalString(replay?.session_id) !== commandContext.sessionId ||
208
+ optionalString(replay?.runtime_instance_id) !== commandContext.runtimeInstanceId ||
209
+ optionalString(replay?.client_type) !== "pi" ||
210
+ !replacementInput ||
211
+ !targetEntryId
212
+ ) {
213
+ throw new Error("branch replay response does not match the bound command context");
214
+ }
215
+ } catch (error) {
216
+ await logDiagnostic(loaded.logFile, {
217
+ level: "error",
218
+ code: "branch_replay_resolve_failed",
219
+ message: "failed to resolve pontia-edit command",
220
+ details: error instanceof Error ? error.message : String(error),
221
+ });
222
+ return;
223
+ }
224
+
225
+ try {
226
+ await ctx.waitForIdle();
227
+ const navigation = await ctx.navigateTree(targetEntryId, { summarize: false });
228
+ if (navigation.cancelled) {
229
+ await logDiagnostic(loaded.logFile, {
230
+ level: "warn",
231
+ code: "branch_replay_navigation_cancelled",
232
+ message: "pontia-edit tree navigation was cancelled",
233
+ details: { inbox_message_id: inboxMessageId },
234
+ });
235
+ return;
236
+ }
237
+ } catch (error) {
238
+ await logDiagnostic(loaded.logFile, {
239
+ level: "error",
240
+ code: "branch_replay_navigation_failed",
241
+ message: "pontia-edit tree navigation failed",
242
+ details: error instanceof Error ? error.message : String(error),
243
+ });
244
+ return;
245
+ }
246
+
247
+ try {
248
+ ctx.ui.setEditorText("");
249
+ await pi.sendUserMessage(replacementInput);
250
+ } catch (error) {
251
+ await logDiagnostic(loaded.logFile, {
252
+ level: "error",
253
+ code: "branch_replay_submission_failed",
254
+ message: "pontia-edit replacement submission failed",
255
+ details: error instanceof Error ? error.message : String(error),
256
+ });
257
+ }
258
+ },
259
+ });
260
+
261
+ async function scheduleMessageRefresh(reason: SessionMessageUpdatedReason): Promise<void> {
262
+ if (!activeTurn || activeTurn.ended) return;
263
+ await activeTurn.reporter.report(activeTurn.context, buildSessionMessageUpdatedEvent(activeTurn.context, reason));
264
+ }
265
+
266
+ async function reportFinalMessageRefresh(state: ActiveTurnState): Promise<void> {
267
+ await state.reporter.report(state.context, buildSessionMessageUpdatedEvent(state.context, "final"));
268
+ }
269
+
270
+ async function reportContextUsageFromHookEvent(event: unknown, ctx?: unknown): Promise<void> {
271
+ if (!activeTurn || activeTurn.ended) return;
272
+ const observation = contextUsageFromPiHook(event, ctx);
273
+ if (!observation) return;
274
+ const usageJson = JSON.stringify(observation);
275
+ if (usageJson === lastContextUsageJson) return;
276
+ lastContextUsageJson = usageJson;
277
+ await activeTurn.reporter.report(activeTurn.context, buildSessionContextUsageUpdatedEvent(activeTurn.context, observation.context_usage, observation.model));
278
+ }
279
+
280
+ pi.on("before_agent_start", async (event) => {
281
+ const eventRecord = event as unknown as Record<string, unknown>;
282
+ pendingPrompt = optionalString(eventRecord.prompt);
283
+ if (reportingDisabled || !await confirmManagedPane()) {
284
+ return { systemPrompt: typeof eventRecord.systemPrompt === "string" ? eventRecord.systemPrompt : "" };
285
+ }
286
+ const currentSystemPrompt = typeof eventRecord.systemPrompt === "string" ? eventRecord.systemPrompt : "";
287
+ try {
288
+ const profilePrompt = await loadProfileSystemPrompt(
289
+ currentPontiaHome(),
290
+ fetchImpl,
291
+ boundSessionContext?.sessionId,
292
+ );
293
+ if (!profilePrompt) return { systemPrompt: currentSystemPrompt };
294
+ return { systemPrompt: `${currentSystemPrompt}\n\n${profilePrompt}` };
295
+ } catch (error) {
296
+ await logDiagnostic(currentHookLogFile(), {
297
+ level: "warn",
298
+ code: "system_prompt_append_failed",
299
+ message: "failed to append pontia execution profile system prompt",
300
+ details: error instanceof Error ? error.message : String(error),
301
+ });
302
+ return { systemPrompt: currentSystemPrompt };
303
+ }
304
+ });
305
+
306
+ pi.on("session_start", async (event, ctx) => {
307
+ if (reportingDisabled) return;
308
+ const reason = (event as unknown as Record<string, unknown> | undefined)?.reason;
309
+ if (readyReported && reason !== "fork" && reason !== "resume") return;
310
+ if (reason !== "startup" && reason !== "new" && reason !== "resume" && reason !== "fork") return;
311
+
312
+ try {
313
+ const sessionDetails = piSessionDetailsFromHookContext(ctx);
314
+ deferredManualSessionDetails = sessionDetails;
315
+ const logFile = currentHookLogFile();
316
+ let context: SessionContext | undefined;
317
+ const pontiaHome = currentPontiaHome();
318
+ const env = currentEnv();
319
+
320
+ const workspaceActive = await isActiveRegisteredWorkspace(pontiaHome, fetchImpl, sessionDetails.clientCwd);
321
+ if (workspaceActive !== true) {
322
+ reportingDisabled = true;
323
+ await logDiagnostic(logFile, {
324
+ level: "info",
325
+ code: workspaceActive === false ? "workspace_not_active" : "workspace_check_unavailable",
326
+ message: workspaceActive === false
327
+ ? "current pi workspace is not an active registered pontia workspace; pontia reporting disabled"
328
+ : "could not verify active registered pontia workspace; pontia reporting disabled",
329
+ details: { client_cwd: sessionDetails.clientCwd },
330
+ });
331
+ return;
332
+ }
333
+
334
+ if (reason === "fork") {
335
+ const parentSessionId = boundSessionContext?.sessionId;
336
+ if (!parentSessionId) {
337
+ await logDiagnostic(logFile, {
338
+ level: "error",
339
+ code: "missing_fork_parent_session",
340
+ message: "pi fork session_start requires an existing pontia parent session",
341
+ });
342
+ return;
343
+ }
344
+ context = await bindSession(pontiaHome, env, fetchImpl, sessionDetails, { startKind: "fork", parentSessionId });
345
+ readyReported = false;
346
+ } else {
347
+ const existingSession = await loadExistingSessionContext(pontiaHome, fetchImpl, sessionDetails);
348
+ if (
349
+ existingSession
350
+ && ["idle", "busy", "interrupted"].includes(existingSession.sessionState)
351
+ ) {
352
+ reportingDisabled = true;
353
+ boundSessionContext = undefined;
354
+ readyReported = false;
355
+ await logDiagnostic(logFile, {
356
+ level: "info",
357
+ code: "duplicate_active_client_session",
358
+ message: "native pi session is already bound to an active pontia session; duplicate TUI reporting disabled",
359
+ details: {
360
+ client_session_key: sessionDetails.clientSessionKey,
361
+ session_id: existingSession.sessionId,
362
+ session_state: existingSession.sessionState,
363
+ },
364
+ });
365
+ return;
366
+ }
367
+ if (!existingSession && !await confirmManagedPane()) {
368
+ boundSessionContext = undefined;
369
+ readyReported = false;
370
+ return;
371
+ }
372
+
373
+ context = await bindSession(pontiaHome, env, fetchImpl, sessionDetails, {
374
+ runtimeInstanceId: existingSession?.runtimeInstanceId,
375
+ });
376
+ if (reason === "resume" || reason === "new") readyReported = false;
377
+ }
378
+ if (!context || !await confirmManagedPane(true)) return;
379
+
380
+ boundSessionContext = context;
381
+ readyReported = reportAccepted(await makeReporter(logFile).report(context, buildSessionReadyEvent(context)));
382
+ } catch (error) {
383
+ const logFile = currentHookLogFile();
384
+ await logDiagnostic(logFile, {
385
+ level: "error",
386
+ code: "unexpected_extension_exception",
387
+ message: "failed to report pontia ready signal",
388
+ details: error instanceof Error ? error.message : String(error),
389
+ });
390
+ }
391
+ });
392
+
393
+ pi.on("session_shutdown", async (event) => {
394
+ if (reportingDisabled || !await confirmManagedPane()) return;
395
+ const reason = (event as unknown as Record<string, unknown> | undefined)?.reason;
396
+ if (reason !== "quit" && reason !== "new" && reason !== "resume" && reason !== "fork") return;
397
+
398
+ try {
399
+ const logFile = currentHookLogFile();
400
+ if (!boundSessionContext) return;
401
+ await makeReporter(logFile).report(boundSessionContext, buildSessionExitedEvent(boundSessionContext, reason));
402
+ } catch (error) {
403
+ const logFile = currentHookLogFile();
404
+ await logDiagnostic(logFile, {
405
+ level: "error",
406
+ code: "unexpected_extension_exception",
407
+ message: "failed to report pontia exited signal",
408
+ details: error instanceof Error ? error.message : String(error),
409
+ });
410
+ }
411
+ });
412
+
413
+ pi.on("agent_start", async (_event, ctx) => {
414
+ const topologyContext = topologyContextFromHookContext(ctx);
415
+ const previousLeafId = leafIdFromHookContext(ctx);
416
+ if (reportingDisabled) return;
417
+ try {
418
+ const loaded = await contextLoader(currentEnv(), boundSessionContext);
419
+ let turnContext: TurnContext | undefined;
420
+ let logFile: string;
421
+ if (loaded.ok) {
422
+ turnContext = loaded.context;
423
+ logFile = loaded.logFile;
424
+ } else if (loaded.silent) {
425
+ if (!loaded.logFile) {
426
+ activeTurn = undefined;
427
+ pendingPrompt = undefined;
428
+ return;
429
+ }
430
+ logFile = loaded.logFile;
431
+ if (!boundSessionContext) {
432
+ const hookSessionDetails = piSessionDetailsFromHookContext(ctx);
433
+ const sessionDetails = hookSessionDetails.clientSessionKey ? hookSessionDetails : deferredManualSessionDetails;
434
+ if (!sessionDetails) {
435
+ activeTurn = undefined;
436
+ pendingPrompt = undefined;
437
+ return;
438
+ }
439
+ const pontiaHome = currentPontiaHome();
440
+ const workspaceActive = await isActiveRegisteredWorkspace(pontiaHome, fetchImpl, sessionDetails.clientCwd);
441
+ if (workspaceActive !== true) {
442
+ reportingDisabled = true;
443
+ await logDiagnostic(logFile, {
444
+ level: "info",
445
+ code: workspaceActive === false ? "workspace_not_active" : "workspace_check_unavailable",
446
+ message: workspaceActive === false
447
+ ? "current pi workspace is not an active registered pontia workspace; pontia reporting disabled"
448
+ : "could not verify active registered pontia workspace; pontia reporting disabled",
449
+ details: { client_cwd: sessionDetails.clientCwd },
450
+ });
451
+ activeTurn = undefined;
452
+ pendingPrompt = undefined;
453
+ return;
454
+ }
455
+ boundSessionContext = await bindSession(pontiaHome, currentEnv(), fetchImpl, sessionDetails);
456
+ if (boundSessionContext && !readyReported) {
457
+ readyReported = reportAccepted(await makeReporter(logFile).report(boundSessionContext, buildSessionReadyEvent(boundSessionContext)));
458
+ }
459
+ }
460
+ if (boundSessionContext) {
461
+ turnContext = {
462
+ sessionId: boundSessionContext.sessionId,
463
+ runtimeInstanceId: boundSessionContext.runtimeInstanceId,
464
+ clientType: "pi",
465
+ internalEventUrl: boundSessionContext.internalEventUrl,
466
+ input: pendingPrompt,
467
+ };
468
+ } else {
469
+ activeTurn = undefined;
470
+ pendingPrompt = undefined;
471
+ return;
472
+ }
473
+ } else {
474
+ activeTurn = undefined;
475
+ if (!loaded.silent && ctx?.hasUI) ctx.ui.notify(`pontia: ${loaded.reason}`, "warning");
476
+ return;
477
+ }
478
+
479
+ pendingPrompt = undefined;
480
+ if (!await confirmManagedPane(true)) {
481
+ activeTurn = undefined;
482
+ return;
483
+ }
484
+ const reporter = makeReporter(logFile);
485
+ lastContextUsageJson = undefined;
486
+ const started = await reporter.report(turnContext, buildTurnStartedEvent(turnContext, previousLeafId, topologyContext));
487
+ const canonicalTurnId = typeof started === "boolean" ? turnContext.turnId : started.turnId;
488
+ if (!reportAccepted(started) || !canonicalTurnId) {
489
+ activeTurn = undefined;
490
+ await logDiagnostic(logFile, {
491
+ level: "error",
492
+ code: "turn_start_not_normalized",
493
+ message: "pontia did not return a canonical turn_id for turn.started",
494
+ });
495
+ return;
496
+ }
497
+ activeTurn = {
498
+ context: { ...turnContext, turnId: canonicalTurnId },
499
+ logFile,
500
+ reporter,
501
+ output: "",
502
+ ended: false,
503
+ };
504
+ } catch (error) {
505
+ pendingPrompt = undefined;
506
+ activeTurn = undefined;
507
+ const logFile = currentHookLogFile();
508
+ await logDiagnostic(logFile, {
509
+ level: "error",
510
+ code: "unexpected_extension_exception",
511
+ message: "failed to initialize pontia active turn",
512
+ details: error instanceof Error ? error.message : String(error),
513
+ });
514
+ }
515
+ });
516
+
517
+ pi.on("message_update", async (event, ctx) => {
518
+ if (!activeTurn || activeTurn.ended) return;
519
+ await reportContextUsageFromHookEvent(event, ctx);
520
+
521
+ const fullText = assistantTextFromMessage((event as unknown as Record<string, unknown> | undefined)?.message);
522
+ if (fullText) {
523
+ activeTurn.output = fullText;
524
+ } else {
525
+ const delta = assistantDeltaFromEvent(event);
526
+ if (delta) activeTurn.output += delta;
527
+ }
528
+
529
+ if (isTranscriptBoundaryMessageUpdate(event)) await scheduleMessageRefresh("update");
530
+ });
531
+
532
+ pi.on("tool_execution_start", async () => {
533
+ await scheduleMessageRefresh("update");
534
+ });
535
+
536
+ pi.on("tool_execution_end", async () => {
537
+ await scheduleMessageRefresh("update");
538
+ });
539
+
540
+ pi.on("message_end", async (event, ctx) => {
541
+ if (!activeTurn || activeTurn.ended) return;
542
+ await reportContextUsageFromHookEvent(event, ctx);
543
+ const fullText = assistantTextFromMessage((event as unknown as Record<string, unknown> | undefined)?.message);
544
+ if (fullText) activeTurn.output = fullText;
545
+ await scheduleMessageRefresh("append");
546
+ });
547
+
548
+ pi.on("agent_end", async (event, ctx) => {
549
+ if (!activeTurn || activeTurn.ended) return;
550
+ await reportContextUsageFromHookEvent(event, ctx);
551
+ activeTurn.ended = true;
552
+
553
+ const state = activeTurn;
554
+ const terminalLeafId = leafIdFromHookContext(ctx);
555
+ if (agentEndWasInterrupted(event)) {
556
+ await state.reporter.report(state.context, buildTurnInterruptedEvent(state.context, terminalLeafId));
557
+ await reportFinalMessageRefresh(state);
558
+ return;
559
+ }
560
+
561
+ const failureMessage = errorMessageFromAgentEnd(event);
562
+ if (failureMessage) {
563
+ await state.reporter.report(state.context, buildTurnFailedEvent(state.context, failureMessage, terminalLeafId));
564
+ await reportFinalMessageRefresh(state);
565
+ return;
566
+ }
567
+
568
+ if (!state.output) {
569
+ const finalText = lastAssistantTextFromMessages((event as unknown as Record<string, unknown> | undefined)?.messages);
570
+ if (finalText) state.output = finalText;
571
+ }
572
+
573
+ const output = state.output.trim();
574
+ if (output.length > 0) {
575
+ const outputResult = await state.reporter.report(state.context, buildTurnOutputEvent(state.context, output));
576
+ if (!reportAccepted(outputResult)) return;
577
+ }
578
+
579
+ await state.reporter.report(state.context, buildTurnCompletedEvent(state.context, terminalLeafId));
580
+ await reportFinalMessageRefresh(state);
581
+ });
582
+ }
583
+
584
+ export default function pontiaPiExtension(pi: ExtensionAPI): void {
585
+ createPontiaPiExtension(pi);
586
+ }
@@ -0,0 +1,29 @@
1
+ export function optionalString(value: unknown): string | undefined {
2
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
3
+ }
4
+
5
+ export function asRecord(value: unknown): Record<string, unknown> | undefined {
6
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
7
+ }
8
+
9
+ export async function parseJsonResponse(response: Response): Promise<unknown> {
10
+ const text = await response.text().catch(() => "");
11
+ if (!text) return null;
12
+ try {
13
+ return JSON.parse(text);
14
+ } catch {
15
+ return text;
16
+ }
17
+ }
18
+
19
+ export function responseDataRecord(body: unknown): Record<string, unknown> | undefined {
20
+ const record = asRecord(body);
21
+ return asRecord(record?.data);
22
+ }
23
+
24
+ export async function fetchJson(fetchImpl: typeof fetch, url: string, token: string): Promise<unknown> {
25
+ const response = await fetchImpl(url, { headers: { Authorization: `Bearer ${token}` } });
26
+ const body = await parseJsonResponse(response);
27
+ if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
28
+ return body;
29
+ }
@@ -0,0 +1,52 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import type { EnvLike } from "./context.js";
4
+
5
+ const execFileAsync = promisify(execFile);
6
+
7
+ export interface ManagedRuntimeIdentity {
8
+ sessionId: string;
9
+ runtimeInstanceId: string;
10
+ }
11
+
12
+ export function hasTmuxPaneEnvironment(env: EnvLike = process.env): boolean {
13
+ const socketPath = env.TMUX?.trim().split(",", 1)[0]?.trim();
14
+ return Boolean(socketPath && env.TMUX_PANE?.trim());
15
+ }
16
+
17
+ async function paneOption(socketPath: string, paneId: string, option: string): Promise<string | undefined> {
18
+ try {
19
+ const { stdout } = await execFileAsync("tmux", [
20
+ "-S",
21
+ socketPath,
22
+ "show-options",
23
+ "-p",
24
+ "-v",
25
+ "-t",
26
+ paneId,
27
+ option,
28
+ ]);
29
+ return stdout.trim() || undefined;
30
+ } catch {
31
+ return undefined;
32
+ }
33
+ }
34
+
35
+ export async function loadPontiaManagedRuntimeIdentity(
36
+ env: EnvLike = process.env,
37
+ ): Promise<ManagedRuntimeIdentity | undefined> {
38
+ if (!hasTmuxPaneEnvironment(env)) return undefined;
39
+ const socketPath = env.TMUX!.trim().split(",", 1)[0]!.trim();
40
+ const paneId = env.TMUX_PANE!.trim();
41
+
42
+ const [sessionId, runtimeInstanceId] = await Promise.all([
43
+ paneOption(socketPath, paneId, "@pontia_session_id"),
44
+ paneOption(socketPath, paneId, "@pontia_runtime_instance_id"),
45
+ ]);
46
+ if (!sessionId || !runtimeInstanceId) return undefined;
47
+ return { sessionId, runtimeInstanceId };
48
+ }
49
+
50
+ export async function isPontiaManagedTmuxPane(env: EnvLike = process.env): Promise<boolean> {
51
+ return (await loadPontiaManagedRuntimeIdentity(env)) !== undefined;
52
+ }