@pasko70/pibo 1.16.6 → 2.1.1

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 (156) hide show
  1. package/dist/agent-runtime/auth.js +28 -0
  2. package/dist/agent-runtime/capabilities.js +221 -0
  3. package/dist/agent-runtime/context-build.js +313 -0
  4. package/dist/agent-runtime/contract.js +45 -0
  5. package/dist/agent-runtime/errors.js +57 -0
  6. package/dist/agent-runtime/events.js +1 -0
  7. package/dist/agent-runtime/history.js +1 -0
  8. package/dist/agent-runtime/profile-validation.js +109 -0
  9. package/dist/agent-runtime/registry.js +608 -0
  10. package/dist/agent-runtime/resource-files.js +87 -0
  11. package/dist/agent-runtime/resource-service.js +635 -0
  12. package/dist/agent-runtime/resources.js +1 -0
  13. package/dist/agent-runtime/routed-session.js +931 -0
  14. package/dist/agent-runtime/testing/contract.js +51 -0
  15. package/dist/agent-runtime/testing/fake-adapter.js +192 -0
  16. package/dist/agent-runtime/types.js +3 -0
  17. package/dist/agent-runtimes/codex-native/adapter.js +905 -0
  18. package/dist/agent-runtimes/codex-native/auth.js +476 -0
  19. package/dist/agent-runtimes/codex-native/client.js +734 -0
  20. package/dist/agent-runtimes/codex-native/config.js +152 -0
  21. package/dist/agent-runtimes/codex-native/history.js +283 -0
  22. package/dist/agent-runtimes/codex-native/mcp-stdio-launcher.js +89 -0
  23. package/dist/agent-runtimes/codex-native/models.js +581 -0
  24. package/dist/agent-runtimes/codex-native/process.js +459 -0
  25. package/dist/agent-runtimes/codex-native/protocol-types.js +1 -0
  26. package/dist/agent-runtimes/codex-native/protocol-version.js +7 -0
  27. package/dist/agent-runtimes/codex-native/redaction.js +31 -0
  28. package/dist/agent-runtimes/codex-native/requests.js +378 -0
  29. package/dist/agent-runtimes/codex-native/resource-delivery.js +445 -0
  30. package/dist/agent-runtimes/codex-native/thread.js +388 -0
  31. package/dist/agent-runtimes/codex-native/turn.js +849 -0
  32. package/dist/agent-runtimes/omp/adapter.js +581 -0
  33. package/dist/agent-runtimes/omp/auth.js +109 -0
  34. package/dist/agent-runtimes/omp/client.js +460 -0
  35. package/dist/agent-runtimes/omp/config.js +188 -0
  36. package/dist/agent-runtimes/omp/history.js +108 -0
  37. package/dist/agent-runtimes/omp/host-tools.js +201 -0
  38. package/dist/agent-runtimes/omp/models.js +86 -0
  39. package/dist/agent-runtimes/omp/process.js +283 -0
  40. package/dist/agent-runtimes/omp/protocol-types.js +17 -0
  41. package/dist/agent-runtimes/omp/resource-delivery.js +162 -0
  42. package/dist/agent-runtimes/omp/thread.js +141 -0
  43. package/dist/agent-runtimes/omp/turn.js +325 -0
  44. package/dist/agent-runtimes/pi/adapter.js +653 -0
  45. package/dist/agent-runtimes/pi/auth.js +455 -0
  46. package/dist/agent-runtimes/pi/history.js +640 -0
  47. package/dist/agent-runtimes/pi/model-catalog.js +87 -0
  48. package/dist/agent-runtimes/pi/routed-session.js +1464 -0
  49. package/dist/agent-runtimes/pi/runtime.js +534 -0
  50. package/dist/agent-runtimes/pi/tool-compiler.js +69 -0
  51. package/dist/apps/chat/agent-profiles.js +40 -23
  52. package/dist/apps/chat/agent-store.js +111 -64
  53. package/dist/apps/chat/chat-api-routes.js +1 -1
  54. package/dist/apps/chat/chat-request-normalizers.js +47 -0
  55. package/dist/apps/chat/data/chat-data-mappers.js +25 -9
  56. package/dist/apps/chat/data/history-query-service.js +108 -0
  57. package/dist/apps/chat/data/session-query-service.js +24 -3
  58. package/dist/apps/chat/data/timeline-query-service.js +3 -3
  59. package/dist/apps/chat/model-catalog.js +1 -55
  60. package/dist/apps/chat/provider-auth-actions.js +171 -23
  61. package/dist/apps/chat/stream.js +12 -0
  62. package/dist/apps/chat/trace-v2.js +1 -0
  63. package/dist/apps/chat/trace.js +126 -415
  64. package/dist/apps/chat/web-app.js +582 -157
  65. package/dist/apps/chat-ui/assets/{dist-BTLtT_Vf.js → dist-9v2ZXT6V.js} +1 -1
  66. package/dist/apps/chat-ui/assets/{dist-CMk1aPug.js → dist-B1LVAytl.js} +1 -1
  67. package/dist/apps/chat-ui/assets/{dist-CcwA_IWN.js → dist-C6LPDFXU.js} +1 -1
  68. package/dist/apps/chat-ui/assets/{dist-DKOthbxr.js → dist-DVtXxfHc.js} +1 -1
  69. package/dist/apps/chat-ui/assets/{dist-ls2I6BIw.js → dist-DW4Qrfbd.js} +1 -1
  70. package/dist/apps/chat-ui/assets/{dist-D1rAtRWs.js → dist-DmbsHdMC.js} +1 -1
  71. package/dist/apps/chat-ui/assets/{dist-C1JQRskW.js → dist-DyvwuSFO.js} +1 -1
  72. package/dist/apps/chat-ui/assets/{dist-DfpaylQ-.js → dist-I26VB0QO.js} +1 -1
  73. package/dist/apps/chat-ui/assets/{dist-D10bTFD5.js → dist-RtHbJRPV.js} +1 -1
  74. package/dist/apps/chat-ui/assets/{dist-5KpmmBBZ.js → dist-sZgJkWZb.js} +1 -1
  75. package/dist/apps/chat-ui/assets/index-BPbNvcZe.css +1 -0
  76. package/dist/apps/chat-ui/assets/index-CIOf2uL2.js +237 -0
  77. package/dist/apps/chat-ui/index.html +2 -2
  78. package/dist/apps/chat-vscode-web/assets/index-1ivRyjFs.js +41 -0
  79. package/dist/apps/chat-vscode-web/index.html +1 -1
  80. package/dist/auth/login-actions.js +5 -251
  81. package/dist/cli-session/localSessionSource.js +8 -1
  82. package/dist/core/context-build.js +124 -20
  83. package/dist/core/default-profile.js +8 -0
  84. package/dist/core/profiles.js +30 -3
  85. package/dist/core/routed-session.js +5 -1446
  86. package/dist/core/runtime.js +5 -561
  87. package/dist/core/session-router.js +637 -83
  88. package/dist/data/cli.js +20 -3
  89. package/dist/data/schema.js +104 -1
  90. package/dist/data/session-store.js +24 -1
  91. package/dist/debug/events.js +25 -10
  92. package/dist/debug/failures.js +23 -3
  93. package/dist/debug/index.js +24 -6
  94. package/dist/debug/messages.js +87 -7
  95. package/dist/debug/persisted-payloads.js +31 -0
  96. package/dist/debug/pty.js +7 -6
  97. package/dist/debug/runtime-binding.js +29 -0
  98. package/dist/debug/session.js +137 -0
  99. package/dist/debug/summary.js +12 -0
  100. package/dist/debug/telemetry.js +28 -0
  101. package/dist/debug/tools.js +12 -2
  102. package/dist/debug/trace.js +154 -76
  103. package/dist/gateway/server.js +87 -2
  104. package/dist/gateway/tool.js +5 -5
  105. package/dist/index.js +21 -2
  106. package/dist/loops/tools.js +13 -12
  107. package/dist/mcp/agent-context.js +13 -4
  108. package/dist/mcp/client.js +10 -6
  109. package/dist/mcp/config.js +5 -2
  110. package/dist/mcp/runtime-session.js +166 -0
  111. package/dist/plugins/builtin.js +193 -53
  112. package/dist/plugins/codex-native.js +29 -0
  113. package/dist/plugins/omp.js +57 -0
  114. package/dist/plugins/registry.js +66 -2
  115. package/dist/runs/tools.js +26 -25
  116. package/dist/session-ui/terminalRows.js +2 -2
  117. package/dist/sessions/pibo-data-store.js +125 -16
  118. package/dist/sessions/runtime-binding.js +107 -0
  119. package/dist/sessions/sqlite-store.js +239 -18
  120. package/dist/sessions/store.js +86 -7
  121. package/dist/shared/trace-engine.js +18 -9
  122. package/dist/shared/trace-event-projection.js +29 -11
  123. package/dist/shared/{trace-transcript.js → trace-history.js} +193 -195
  124. package/dist/shared/trace-order.js +9 -4
  125. package/dist/shared/trace-page-merge.js +1 -1
  126. package/dist/signals/projector.js +20 -2
  127. package/dist/signals/registry.js +11 -0
  128. package/dist/subagents/tool.js +8 -6
  129. package/dist/tools/codex-browser.js +20 -19
  130. package/dist/tools/codex-compat.js +10 -9
  131. package/dist/tools/codex-image-generation.js +8 -6
  132. package/dist/tools/contract.js +45 -0
  133. package/dist/tools/credential-registry.js +182 -0
  134. package/dist/tools/mcp-bridge.js +511 -0
  135. package/dist/tools/payload-writer.js +17 -0
  136. package/dist/tools/runtime/tool.js +10 -9
  137. package/dist/tools/schema.js +9 -0
  138. package/dist/tools/session-service.js +193 -0
  139. package/dist/tools/session-tool-set.js +85 -0
  140. package/dist/web-annotations/tools.js +17 -16
  141. package/docs/README.md +11 -0
  142. package/package.json +3 -2
  143. package/skills/builtin/pibo-agent-runtime-adapter/SKILL.md +183 -0
  144. package/skills/builtin/pibo-agent-runtime-adapter/evals/README.md +10 -0
  145. package/skills/builtin/pibo-agent-runtime-adapter/evals/evals.json +45 -0
  146. package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/orion-full-harness.md +60 -0
  147. package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/relay-partial-harness.md +52 -0
  148. package/skills/builtin/pibo-agent-runtime-adapter/references/capabilities-and-designer.md +194 -0
  149. package/skills/builtin/pibo-agent-runtime-adapter/references/history-debug-and-security.md +213 -0
  150. package/skills/builtin/pibo-agent-runtime-adapter/references/interfaces-and-registration.md +214 -0
  151. package/skills/builtin/pibo-agent-runtime-adapter/references/lifecycle-bindings-and-events.md +193 -0
  152. package/skills/builtin/pibo-agent-runtime-adapter/references/portable-delivery-and-native-behavior.md +226 -0
  153. package/skills/builtin/pibo-agent-runtime-adapter/references/testing-migration-and-validation.md +256 -0
  154. package/dist/apps/chat-ui/assets/index-C2YI0xfw.js +0 -237
  155. package/dist/apps/chat-ui/assets/index-E0RmuCkY.css +0 -1
  156. package/dist/apps/chat-vscode-web/assets/index-D-tSsXLG.js +0 -41
@@ -1,72 +1,34 @@
1
1
  import { createHash } from "node:crypto";
2
- import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, statSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { join } from "node:path";
5
- import { parseSessionEntries, SessionManager } from "@earendil-works/pi-coding-agent";
6
- import { buildTraceViewFromEvents } from "../../shared/trace-engine.js";
2
+ import { PI_HISTORY_PAGE_MAX_BYTES, PI_HISTORY_SCAN_MAX_BYTES, PI_HISTORY_TAIL_MAX_BYTES, listPiHistorySessions, loadPiHistoryFastMetadata, loadPiHistoryMetadata, piSessionEntriesToAgentRuntimeHistoryEntries, readPiTranscriptHistoryPage, readPiTranscriptTailEntries, } from "../../agent-runtimes/pi/history.js";
3
+ import { buildTraceViewFromEvents, traceNodesFromHistoryEntries } from "../../shared/trace-engine.js";
7
4
  import { isChatWebSessionArchived } from "./session-metadata.js";
8
5
  import { workflowSessionKindFromMetadata } from "../../sessions/workflow-session-kind.js";
9
- export { compareTraceNodes, sortTraceNodes, nestTraceNodes, flattenTraceNodes, mapTraceNodesById, buildTraceViewFromEvents, traceNodesFromEntries, } from "../../shared/trace-engine.js";
10
- export const TRACE_TRANSCRIPT_TAIL_MAX_BYTES = 2 * 1024 * 1024;
11
- export const TRACE_TRANSCRIPT_HISTORY_PAGE_MAX_BYTES = 512 * 1024;
12
- export const TRACE_TRANSCRIPT_HISTORY_SCAN_MAX_BYTES = 16 * 1024 * 1024;
13
- export async function loadPiSessionMetadata(session, cwd = process.cwd()) {
14
- const piSession = await findPiSession(session, cwd);
15
- return metadataFromPiSession(piSession);
16
- }
17
- export async function loadPiSessionFastMetadata(session, cwd = process.cwd()) {
18
- const piSession = findPiSessionDirectFast(session.piSessionId, cwd)
19
- ?? findPiSessionDirect(session.piSessionId, cwd, { fast: true });
20
- return metadataFromPiSession(piSession);
21
- }
22
- function metadataFromPiSession(piSession) {
23
- if (!piSession)
24
- return {};
25
- let sessionSize;
26
- let sessionMtimeMs;
27
- try {
28
- const stats = statSync(piSession.path);
29
- sessionSize = stats.size;
30
- sessionMtimeMs = stats.mtimeMs;
31
- }
32
- catch {
33
- // The session list can contain a file that was removed before trace rendering.
34
- }
35
- return {
36
- sessionPath: piSession.path,
37
- sessionSize,
38
- sessionMtimeMs,
39
- name: piSession.name,
40
- firstMessage: piSession.firstMessage,
41
- modified: piSession.modified,
42
- };
43
- }
44
- export async function buildSessionNodes(sessions, indexItems, cwd = process.cwd(), unreadCounts = new Map(), options = {}) {
6
+ export { compareTraceNodes, sortTraceNodes, nestTraceNodes, flattenTraceNodes, mapTraceNodesById, buildTraceViewFromEvents, traceNodesFromHistoryEntries, } from "../../shared/trace-engine.js";
7
+ export const TRACE_TRANSCRIPT_TAIL_MAX_BYTES = PI_HISTORY_TAIL_MAX_BYTES;
8
+ export const TRACE_TRANSCRIPT_HISTORY_PAGE_MAX_BYTES = PI_HISTORY_PAGE_MAX_BYTES;
9
+ export const TRACE_TRANSCRIPT_HISTORY_SCAN_MAX_BYTES = PI_HISTORY_SCAN_MAX_BYTES;
10
+ export async function buildSessionNodes(sessions, indexItems, _cwd = process.cwd(), unreadCounts = new Map(), options = {}) {
45
11
  const indexByKey = new Map(indexItems.map((item) => [item.piboSessionId, item]));
46
12
  const nodes = new Map();
47
- const piSessionsByCwd = new Map();
48
13
  for (const session of sessions) {
49
- let metadata = {};
50
- if (!session.title && !options.skipPiMetadataFallback) {
51
- const sessionCwd = session.workspace ?? cwd;
52
- let piSessions = piSessionsByCwd.get(sessionCwd);
53
- if (!piSessions) {
54
- piSessions = listPiSessions(sessionCwd);
55
- piSessionsByCwd.set(sessionCwd, piSessions);
56
- }
57
- metadata = metadataFromPiSession((await piSessions).find((piSession) => piSession.id === session.piSessionId));
58
- }
14
+ const inspection = !session.title && !options.skipPiMetadataFallback && options.loadHistoryInspection
15
+ ? await options.loadHistoryInspection(session)
16
+ : undefined;
59
17
  const indexed = indexByKey.get(session.id);
60
18
  nodes.set(session.id, {
61
19
  piboSessionId: session.id,
62
20
  piSessionId: session.piSessionId,
21
+ runtimeInstanceId: session.runtimeBinding?.runtimeInstanceId ?? indexed?.runtimeInstanceId,
22
+ runtimeAdapterId: session.runtimeBinding?.adapterId ?? indexed?.runtimeAdapterId,
23
+ runtimeBindingState: session.runtimeBinding?.state ?? indexed?.runtimeBindingState,
24
+ nativeSessionId: session.runtimeBinding?.nativeSessionId ?? indexed?.nativeSessionId,
63
25
  parentId: session.parentId,
64
26
  originId: session.originId,
65
27
  profile: session.profile,
66
28
  activeModel: session.activeModel,
67
29
  subagentName: stringValue(session.metadata?.subagentName),
68
30
  workflowSessionKind: workflowSessionKindFromMetadata(session.metadata),
69
- title: createSessionTitle(session, metadata),
31
+ title: createSessionTitle(session, historyMetadataFromInspection(inspection)),
70
32
  subtitle: session.id,
71
33
  archived: isChatWebSessionArchived(session),
72
34
  status: sessionNodeStatus(indexed?.status),
@@ -79,12 +41,10 @@ export async function buildSessionNodes(sessions, indexItems, cwd = process.cwd(
79
41
  const roots = [];
80
42
  for (const node of nodes.values()) {
81
43
  const parent = node.parentId ? nodes.get(node.parentId) : undefined;
82
- if (parent) {
44
+ if (parent)
83
45
  parent.children.push(node);
84
- }
85
- else {
46
+ else
86
47
  roots.push(node);
87
- }
88
48
  }
89
49
  for (const node of nodes.values()) {
90
50
  if (!node.originId)
@@ -95,6 +55,9 @@ export async function buildSessionNodes(sessions, indexItems, cwd = process.cwd(
95
55
  origin.derivedSessions.push({
96
56
  piboSessionId: node.piboSessionId,
97
57
  profile: node.profile,
58
+ runtimeInstanceId: node.runtimeInstanceId,
59
+ runtimeAdapterId: node.runtimeAdapterId,
60
+ runtimeBindingState: node.runtimeBindingState,
98
61
  subagentName: node.subagentName,
99
62
  workflowSessionKind: node.workflowSessionKind,
100
63
  title: node.title,
@@ -116,81 +79,78 @@ function sessionNodeStatus(indexedStatus) {
116
79
  return indexedStatus ?? "idle";
117
80
  }
118
81
  export async function buildTraceView(input) {
119
- const metadata = input.metadata ?? (await loadPiSessionMetadata(input.session, input.session.workspace ?? input.cwd));
120
- const allEntries = input.transcriptEntries ?? (metadata.sessionPath ? readEntries(metadata.sessionPath) : []);
82
+ const historyMetadata = input.historyInspection
83
+ ? historyMetadataFromInspection(input.historyInspection)
84
+ : historyMetadataFromPiCompatibility(input.metadata);
85
+ const entries = [...(input.historyEntries ?? (input.transcriptEntries
86
+ ? piSessionEntriesToAgentRuntimeHistoryEntries(input.transcriptEntries)
87
+ : []))];
121
88
  const sessionStatus = input.status ?? "idle";
122
89
  const view = buildTraceViewFromEvents({
123
90
  session: {
124
91
  id: input.session.id,
125
92
  piSessionId: input.session.piSessionId,
126
- title: createSessionTitle(input.session, metadata),
93
+ title: createSessionTitle(input.session, historyMetadata),
127
94
  },
128
95
  events: input.events,
129
96
  turnTimings: input.turnTimings,
130
- transcriptEntries: allEntries,
131
- sessions: input.sessions.map((s) => ({
132
- id: s.id,
133
- parentId: s.parentId ?? null,
134
- originId: s.originId ?? null,
135
- updatedAt: s.updatedAt,
136
- title: s.title ?? null,
137
- metadata: s.metadata,
97
+ historyEntries: entries,
98
+ sessions: input.sessions.map((session) => ({
99
+ id: session.id,
100
+ parentId: session.parentId ?? null,
101
+ originId: session.originId ?? null,
102
+ updatedAt: session.updatedAt,
103
+ title: session.title ?? null,
104
+ metadata: session.metadata,
138
105
  })),
139
106
  status: sessionStatus,
140
107
  latestStreamId: input.latestStreamId,
141
108
  includeRawEvents: input.includeRawEvents,
142
109
  rawEventsLimit: input.rawEventsLimit,
143
110
  });
144
- if (input.transcriptOrderOffset && input.transcriptOrderOffset > 0) {
145
- offsetTranscriptTraceOrder(view.nodes, input.transcriptOrderOffset);
146
- }
147
- annotateForkableUserMessageNodes(view.nodes, allEntries);
111
+ const historyOrderOffset = input.historyOrderOffset ?? input.transcriptOrderOffset;
112
+ if (historyOrderOffset && historyOrderOffset > 0)
113
+ offsetHistoryTraceOrder(view.nodes, historyOrderOffset);
114
+ annotateForkableUserMessageNodes(view.nodes, entries);
148
115
  return {
149
116
  ...view,
117
+ runtimeBinding: traceRuntimeBinding(input.session),
150
118
  version: createTraceViewVersion({
151
119
  session: input.session,
152
120
  sessions: input.sessions,
153
121
  events: input.events,
154
122
  status: sessionStatus,
155
- metadata,
123
+ history: historyMetadata,
156
124
  latestStreamId: input.latestStreamId,
157
125
  }),
158
126
  };
159
127
  }
160
- export async function loadPiSessionTailEntries(session, cwd = process.cwd(), maxBytes = TRACE_TRANSCRIPT_TAIL_MAX_BYTES) {
161
- const metadata = await loadPiSessionMetadata(session, cwd);
162
- if (!metadata.sessionPath)
163
- return { metadata, entries: [] };
164
- return { metadata, entries: readTailEntries(metadata.sessionPath, maxBytes) };
165
- }
166
128
  export function createTraceViewVersion(input) {
167
- const relevantSessions = input.sessions
168
- .map((session) => ({
129
+ const history = input.history ?? historyMetadataFromPiCompatibility(input.metadata);
130
+ const relevantSessions = input.sessions.map((session) => ({
169
131
  id: session.id,
170
132
  parentId: session.parentId ?? null,
171
133
  originId: session.originId ?? null,
172
134
  updatedAt: session.updatedAt,
173
135
  title: session.title ?? null,
174
- }))
175
- .sort((left, right) => left.id.localeCompare(right.id));
136
+ })).sort((left, right) => left.id.localeCompare(right.id));
176
137
  const eventTail = input.events.at(-1);
177
- return createHash("sha1")
178
- .update(JSON.stringify({
179
- traceProjection: "turn-timing-v2",
138
+ return createHash("sha1").update(JSON.stringify({
139
+ traceProjection: "runtime-history-v1",
180
140
  session: {
181
141
  id: input.session.id,
182
142
  piSessionId: input.session.piSessionId,
183
143
  profile: input.session.profile,
184
144
  title: input.session.title ?? null,
185
145
  updatedAt: input.session.updatedAt,
146
+ runtimeBinding: traceRuntimeBinding(input.session),
186
147
  },
187
- metadata: {
188
- sessionPath: input.metadata?.sessionPath ?? null,
189
- sessionSize: input.metadata?.sessionSize ?? null,
190
- sessionMtimeMs: input.metadata?.sessionMtimeMs ?? null,
191
- name: input.metadata?.name ?? null,
192
- firstMessage: input.metadata?.firstMessage ?? null,
193
- modified: input.metadata?.modified ?? null,
148
+ history: {
149
+ version: history.version ?? null,
150
+ sizeBytes: history.sizeBytes ?? null,
151
+ updatedAt: history.updatedAt ?? null,
152
+ title: history.title ?? null,
153
+ firstMessage: history.firstMessage ?? null,
194
154
  },
195
155
  status: input.status ?? "idle",
196
156
  events: {
@@ -199,40 +159,53 @@ export function createTraceViewVersion(input) {
199
159
  latestStreamId: input.latestStreamId ?? null,
200
160
  },
201
161
  sessions: relevantSessions,
202
- }))
203
- .digest("hex");
162
+ })).digest("hex");
163
+ }
164
+ function traceRuntimeBinding(session) {
165
+ const binding = session.runtimeBinding;
166
+ return binding ? {
167
+ runtimeInstanceId: binding.runtimeInstanceId,
168
+ adapterId: binding.adapterId,
169
+ nativeSessionId: binding.nativeSessionId,
170
+ state: binding.state,
171
+ protocol: binding.protocol,
172
+ protocolVersion: binding.protocolVersion,
173
+ adapterVersion: binding.adapterVersion,
174
+ revision: binding.revision,
175
+ } : undefined;
176
+ }
177
+ function historyMetadataFromPiCompatibility(metadata) {
178
+ if (!metadata)
179
+ return {};
180
+ return {
181
+ title: metadata.name,
182
+ firstMessage: metadata.firstMessage,
183
+ createdAt: metadata.created,
184
+ updatedAt: metadata.modified,
185
+ sizeBytes: metadata.sessionSize,
186
+ version: metadata.sessionPath ? createHash("sha1").update(JSON.stringify({
187
+ path: metadata.sessionPath,
188
+ size: metadata.sessionSize ?? null,
189
+ mtime: metadata.sessionMtimeMs ?? null,
190
+ })).digest("hex") : undefined,
191
+ locator: metadata.sessionPath ? { kind: "local-file", value: metadata.sessionPath } : undefined,
192
+ };
204
193
  }
205
- const PI_SESSION_LIST_CACHE_TTL_MS = 5_000;
206
- const PI_SESSION_DIRECT_CACHE_TTL_MS = 5_000;
207
- const PI_SESSION_FAST_HEAD_BYTES = 64 * 1024;
208
- const piSessionListCache = new Map();
209
- const piSessionDirectCache = new Map();
210
- export async function listPiSessions(cwd = process.cwd()) {
211
- const now = Date.now();
212
- const cached = piSessionListCache.get(cwd);
213
- if (cached && cached.expiresAt > now)
214
- return cached.promise;
215
- const promise = SessionManager.list(cwd).then((sessions) => sessions.map((session) => ({
216
- path: session.path,
217
- id: session.id,
218
- cwd: session.cwd,
219
- name: session.name,
220
- parentSessionPath: session.parentSessionPath,
221
- created: session.created.toISOString(),
222
- modified: session.modified.toISOString(),
223
- messageCount: session.messageCount,
224
- firstMessage: session.firstMessage,
225
- })));
226
- piSessionListCache.set(cwd, { expiresAt: now + PI_SESSION_LIST_CACHE_TTL_MS, promise });
227
- promise.catch(() => {
228
- if (piSessionListCache.get(cwd)?.promise === promise)
229
- piSessionListCache.delete(cwd);
230
- });
231
- return promise;
194
+ function historyMetadataFromInspection(inspection) {
195
+ if (!inspection)
196
+ return {};
197
+ return {
198
+ title: inspection.title,
199
+ firstMessage: inspection.firstMessage,
200
+ createdAt: inspection.createdAt,
201
+ updatedAt: inspection.updatedAt,
202
+ sizeBytes: inspection.sizeBytes,
203
+ version: inspection.version,
204
+ locator: inspection.locator,
205
+ };
232
206
  }
233
207
  function createSessionTitle(session, metadata) {
234
- const candidate = session.title || metadata.name || metadata.firstMessage || session.id;
235
- return truncateTitle(candidate);
208
+ return truncateTitle(session.title || metadata.title || metadata.firstMessage || session.id);
236
209
  }
237
210
  function truncateTitle(title, maxLength = 56) {
238
211
  const normalized = title.replace(/\s+/g, " ").trim();
@@ -240,267 +213,9 @@ function truncateTitle(title, maxLength = 56) {
240
213
  return normalized || "Untitled Session";
241
214
  return `${normalized.slice(0, maxLength - 1)}…`;
242
215
  }
243
- async function findPiSession(piboSession, cwd) {
244
- return (findPiSessionDirect(piboSession.piSessionId, cwd) ??
245
- (await listPiSessions(cwd)).find((session) => session.id === piboSession.piSessionId));
246
- }
247
- function findPiSessionDirectFast(piSessionId, cwd) {
248
- const cacheKey = `${cwd}\0${piSessionId}`;
249
- const cached = piSessionDirectCache.get(cacheKey);
250
- if (!cached || cached.expiresAt <= Date.now())
251
- return undefined;
252
- try {
253
- const stats = statSync(cached.path);
254
- if (stats.size === cached.size && stats.mtimeMs === cached.mtimeMs)
255
- return cached.item;
256
- }
257
- catch {
258
- piSessionDirectCache.delete(cacheKey);
259
- }
260
- return undefined;
261
- }
262
- function setPiSessionDirectFastCache(piSessionId, cwd, item, stats) {
263
- piSessionDirectCache.set(`${cwd}\0${piSessionId}`, {
264
- expiresAt: Date.now() + PI_SESSION_DIRECT_CACHE_TTL_MS,
265
- path: item.path,
266
- size: stats.size,
267
- mtimeMs: stats.mtimeMs,
268
- item,
269
- });
270
- }
271
- function findPiSessionDirect(piSessionId, cwd, options = {}) {
272
- const sessionDir = defaultPiSessionDir(cwd);
273
- if (!existsSync(sessionDir))
274
- return undefined;
275
- try {
276
- const file = readdirSync(sessionDir).find((candidate) => candidate.endsWith(`_${piSessionId}.jsonl`));
277
- if (!file)
278
- return undefined;
279
- const sessionPath = join(sessionDir, file);
280
- const stats = statSync(sessionPath);
281
- const entries = options.fast
282
- ? readHeadEntries(sessionPath, stats.size, PI_SESSION_FAST_HEAD_BYTES)
283
- : parseSessionEntries(readFileSync(sessionPath, "utf8"));
284
- const header = entries.find((entry) => entry.type === "session");
285
- if (header?.id !== piSessionId)
286
- return undefined;
287
- let name;
288
- let firstMessage = "";
289
- let messageCount = 0;
290
- for (const entry of entries) {
291
- if (entry.type === "session_info")
292
- name = stringValue(entry.name)?.trim() || undefined;
293
- if (entry.type !== "message")
294
- continue;
295
- messageCount++;
296
- if (firstMessage || messageRole(entry) !== "user")
297
- continue;
298
- firstMessage = extractMessageText(messageContent(entry));
299
- }
300
- const item = {
301
- path: sessionPath,
302
- id: piSessionId,
303
- cwd: stringValue(header.cwd) ?? cwd,
304
- name,
305
- parentSessionPath: stringValue(header.parentSession),
306
- created: stringValue(header.timestamp) ?? stats.birthtime.toISOString(),
307
- modified: options.fast
308
- ? stats.mtime.toISOString()
309
- : sessionModifiedIso(entries.filter((entry) => entry.type !== "session"), stringValue(header.timestamp), stats.mtime),
310
- messageCount,
311
- firstMessage: firstMessage || "(no messages)",
312
- };
313
- if (options.fast)
314
- setPiSessionDirectFastCache(piSessionId, cwd, item, stats);
315
- return item;
316
- }
317
- catch {
318
- return undefined;
319
- }
320
- }
321
- function defaultPiSessionDir(cwd) {
322
- const agentDir = process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
323
- const safePath = `--${cwd.replace(/^[\\/]/, "").replace(/[\\/:]/g, "-")}--`;
324
- return join(agentDir, "sessions", safePath);
325
- }
326
- function sessionModifiedIso(entries, headerTimestamp, statsMtime) {
327
- let lastActivityTime;
328
- for (const entry of entries) {
329
- if (entry.type !== "message")
330
- continue;
331
- const timestamp = entry.message.timestamp;
332
- if (typeof timestamp === "number") {
333
- lastActivityTime = Math.max(lastActivityTime ?? 0, timestamp);
334
- continue;
335
- }
336
- const entryTime = new Date(entry.timestamp).getTime();
337
- if (!Number.isNaN(entryTime))
338
- lastActivityTime = Math.max(lastActivityTime ?? 0, entryTime);
339
- }
340
- if (lastActivityTime)
341
- return new Date(lastActivityTime).toISOString();
342
- const headerTime = headerTimestamp ? new Date(headerTimestamp).getTime() : NaN;
343
- return !Number.isNaN(headerTime) ? new Date(headerTime).toISOString() : statsMtime.toISOString();
344
- }
345
- function readEntries(path) {
346
- if (!existsSync(path))
347
- return [];
348
- const content = readFileSync(path, "utf8");
349
- return parseSessionEntries(content).filter((entry) => entry.type !== "session");
350
- }
351
- function readHeadEntries(path, fileSize, maxBytes) {
352
- const length = Math.max(1, Math.min(maxBytes, fileSize));
353
- const buffer = Buffer.alloc(length);
354
- const fd = openSync(path, "r");
355
- try {
356
- const bytesRead = readSync(fd, buffer, 0, length, 0);
357
- let content = buffer.subarray(0, bytesRead).toString("utf8");
358
- if (bytesRead < fileSize && !content.endsWith("\n")) {
359
- const lastNewline = content.lastIndexOf("\n");
360
- if (lastNewline >= 0)
361
- content = content.slice(0, lastNewline + 1);
362
- }
363
- if (!content.trim())
364
- return [];
365
- return parseSessionEntries(content.endsWith("\n") ? content : `${content}\n`);
366
- }
367
- finally {
368
- closeSync(fd);
369
- }
370
- }
371
- export function readTailEntries(path, maxBytes = TRACE_TRANSCRIPT_TAIL_MAX_BYTES) {
372
- if (!existsSync(path))
373
- return [];
374
- const stats = statSync(path);
375
- if (stats.size <= maxBytes)
376
- return readEntries(path);
377
- const length = Math.max(1, Math.min(maxBytes, stats.size));
378
- const start = stats.size - length;
379
- const buffer = Buffer.alloc(length);
380
- const fd = openSync(path, "r");
381
- try {
382
- const bytesRead = readSync(fd, buffer, 0, length, start);
383
- let content = buffer.subarray(0, bytesRead).toString("utf8");
384
- const firstNewline = content.indexOf("\n");
385
- if (start > 0 && firstNewline >= 0)
386
- content = content.slice(firstNewline + 1);
387
- return parseSessionEntries(content).filter((entry) => entry.type !== "session");
388
- }
389
- finally {
390
- closeSync(fd);
391
- }
392
- }
393
- export function readTranscriptHistoryPage(path, input = {}) {
394
- if (!existsSync(path))
395
- return { entries: [], hasOlder: false, scannedBytes: 0, startByte: 0, endByte: 0 };
396
- const stats = statSync(path);
397
- const fileSize = Math.max(0, stats.size);
398
- const initialEnd = input.beforeByte === undefined
399
- ? fileSize
400
- : Math.max(0, Math.min(input.beforeByte, fileSize));
401
- const limit = Math.max(1, Math.min(input.limit ?? 50, 500));
402
- const pageBytes = Math.max(1024, Math.min(input.pageBytes ?? TRACE_TRANSCRIPT_HISTORY_PAGE_MAX_BYTES, TRACE_TRANSCRIPT_HISTORY_SCAN_MAX_BYTES));
403
- const maxScanBytes = Math.max(pageBytes, Math.min(input.maxScanBytes ?? TRACE_TRANSCRIPT_HISTORY_SCAN_MAX_BYTES, 32 * 1024 * 1024));
404
- const beforeTime = input.beforeTimestamp ? Date.parse(input.beforeTimestamp) : undefined;
405
- const entries = [];
406
- let cursorEnd = initialEnd;
407
- let scannedBytes = 0;
408
- let nextBeforeByte;
409
- while (cursorEnd > 0 && entries.length < limit && scannedBytes < maxScanBytes) {
410
- const chunkStart = Math.max(0, cursorEnd - pageBytes);
411
- const chunk = readUtf8Range(path, chunkStart, cursorEnd);
412
- scannedBytes += cursorEnd - chunkStart;
413
- const records = transcriptLineRecords(chunk, chunkStart, cursorEnd);
414
- for (let index = records.length - 1; index >= 0; index -= 1) {
415
- const record = records[index];
416
- const parsed = parseTranscriptLine(record.text);
417
- for (let entryIndex = parsed.length - 1; entryIndex >= 0; entryIndex -= 1) {
418
- const entry = parsed[entryIndex];
419
- if (beforeTime !== undefined && entryTimestampMs(entry) >= beforeTime)
420
- continue;
421
- entries.push({ entry, startByte: record.startByte });
422
- if (entries.length >= limit) {
423
- nextBeforeByte = record.startByte;
424
- break;
425
- }
426
- }
427
- if (entries.length >= limit)
428
- break;
429
- }
430
- cursorEnd = chunkStart;
431
- }
432
- if (nextBeforeByte === undefined)
433
- nextBeforeByte = cursorEnd > 0 ? cursorEnd : undefined;
434
- return {
435
- entries: entries.reverse().map((item) => item.entry),
436
- nextBeforeByte,
437
- hasOlder: nextBeforeByte !== undefined && nextBeforeByte > 0,
438
- scannedBytes,
439
- startByte: nextBeforeByte ?? 0,
440
- endByte: initialEnd,
441
- };
442
- }
443
- function readUtf8Range(path, start, end) {
444
- const length = Math.max(0, end - start);
445
- if (length === 0)
446
- return "";
447
- const buffer = Buffer.alloc(length);
448
- const fd = openSync(path, "r");
449
- try {
450
- const bytesRead = readSync(fd, buffer, 0, length, start);
451
- return buffer.subarray(0, bytesRead).toString("utf8");
452
- }
453
- finally {
454
- closeSync(fd);
455
- }
456
- }
457
- function transcriptLineRecords(content, startByte, endByte) {
458
- let text = content;
459
- let byteOffset = startByte;
460
- if (startByte > 0) {
461
- const firstNewline = text.indexOf("\n");
462
- if (firstNewline < 0)
463
- return [];
464
- const skipped = text.slice(0, firstNewline + 1);
465
- byteOffset += Buffer.byteLength(skipped, "utf8");
466
- text = text.slice(firstNewline + 1);
467
- }
468
- if (endByte > startByte && text && !text.endsWith("\n")) {
469
- const lastNewline = text.lastIndexOf("\n");
470
- if (lastNewline >= 0)
471
- text = text.slice(0, lastNewline + 1);
472
- }
473
- const records = [];
474
- const segments = text.match(/[^\n]*(?:\n|$)/g) ?? [];
475
- for (const segment of segments) {
476
- if (!segment)
477
- continue;
478
- const lineStart = byteOffset;
479
- byteOffset += Buffer.byteLength(segment, "utf8");
480
- const line = segment.replace(/\r?\n$/, "");
481
- if (line.trim())
482
- records.push({ text: line, startByte: lineStart });
483
- }
484
- return records;
485
- }
486
- function parseTranscriptLine(line) {
487
- try {
488
- return parseSessionEntries(`${line}\n`).filter((entry) => entry.type !== "session");
489
- }
490
- catch {
491
- return [];
492
- }
493
- }
494
- function entryTimestampMs(entry) {
495
- const timestamp = entry.timestamp;
496
- if (typeof timestamp !== "string")
497
- return Number.POSITIVE_INFINITY;
498
- const parsed = Date.parse(timestamp);
499
- return Number.isFinite(parsed) ? parsed : Number.POSITIVE_INFINITY;
500
- }
501
- function offsetTranscriptTraceOrder(nodes, offset) {
216
+ function offsetHistoryTraceOrder(nodes, offset) {
502
217
  for (const node of nodes) {
503
- if (node.source === "transcript" && node.orderKey) {
218
+ if ((node.source === "transcript" || node.source === "product-history") && node.orderKey) {
504
219
  const transcriptIndex = node.orderKey.transcriptIndex;
505
220
  node.orderKey = {
506
221
  ...node.orderKey,
@@ -508,11 +223,13 @@ function offsetTranscriptTraceOrder(nodes, offset) {
508
223
  transcriptIndex: transcriptIndex === undefined ? undefined : transcriptIndex + offset,
509
224
  };
510
225
  }
511
- offsetTranscriptTraceOrder(node.children, offset);
226
+ offsetHistoryTraceOrder(node.children, offset);
512
227
  }
513
228
  }
514
229
  function annotateForkableUserMessageNodes(nodes, entries) {
515
- const candidates = userMessageForkCandidates(entries);
230
+ const candidates = entries.flatMap((entry) => entry.type === "message" && entry.role === "user" && entry.nativeEntryId
231
+ ? [{ entryId: entry.nativeEntryId, text: historyMessageText(entry) }]
232
+ : []);
516
233
  if (!candidates.length)
517
234
  return;
518
235
  const used = new Set();
@@ -538,37 +255,31 @@ function flattenTraceNodesForForkAnnotation(nodes) {
538
255
  visit(nodes);
539
256
  return flattened;
540
257
  }
541
- function userMessageForkCandidates(entries) {
542
- const candidates = [];
543
- for (const entry of entries) {
544
- if (entry.type !== "message" || messageRole(entry) !== "user")
545
- continue;
546
- const text = extractMessageText(messageContent(entry));
547
- if (text)
548
- candidates.push({ entryId: entry.id, text });
549
- }
550
- return candidates;
258
+ function historyMessageText(entry) {
259
+ if (typeof entry.content === "string")
260
+ return entry.content;
261
+ return entry.content.map((part) => part.type === "text" ? part.text : "").join("");
551
262
  }
552
- function messageRole(entry) {
553
- return entry.type === "message" ? entry.message.role : undefined;
263
+ function stringValue(value) {
264
+ return typeof value === "string" ? value : undefined;
554
265
  }
555
- function messageContent(entry) {
556
- return entry.type === "message" ? entry.message.content : undefined;
266
+ // Pi compatibility exports. Normal Chat Web history resolves through the selected runtime adapter.
267
+ export async function loadPiSessionMetadata(session, cwd = process.cwd()) {
268
+ return await loadPiHistoryMetadata(session.runtimeBinding?.nativeSessionId ?? session.piSessionId, cwd);
557
269
  }
558
- function extractMessageText(content) {
559
- if (typeof content === "string")
560
- return content;
561
- if (!Array.isArray(content))
562
- return "";
563
- return content
564
- .map((part) => {
565
- if (!part || typeof part !== "object")
566
- return "";
567
- const typed = part;
568
- return typed.type === "text" && typeof typed.text === "string" ? typed.text : "";
569
- })
570
- .join("");
270
+ export async function loadPiSessionFastMetadata(session, cwd = process.cwd()) {
271
+ return loadPiHistoryFastMetadata(session.runtimeBinding?.nativeSessionId ?? session.piSessionId, cwd);
571
272
  }
572
- function stringValue(value) {
573
- return typeof value === "string" ? value : undefined;
273
+ export async function loadPiSessionTailEntries(session, cwd = process.cwd(), maxBytes = TRACE_TRANSCRIPT_TAIL_MAX_BYTES) {
274
+ const metadata = await loadPiSessionMetadata(session, cwd);
275
+ if (!metadata.sessionPath)
276
+ return { metadata, entries: [] };
277
+ return { metadata, entries: readPiTranscriptTailEntries(metadata.sessionPath, maxBytes) };
278
+ }
279
+ export const listPiSessions = listPiHistorySessions;
280
+ export const readTailEntries = readPiTranscriptTailEntries;
281
+ export const readTranscriptHistoryPage = readPiTranscriptHistoryPage;
282
+ /** @deprecated Pi compatibility helper. Use traceNodesFromHistoryEntries. */
283
+ export function traceNodesFromEntries(piboSessionId, entries, turnTimings = []) {
284
+ return traceNodesFromHistoryEntries(piboSessionId, piSessionEntriesToAgentRuntimeHistoryEntries(entries), turnTimings);
574
285
  }