@herbertgao/pi-extensions 2026.8.12 → 2026.8.13

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 (112) hide show
  1. package/README.md +8 -3
  2. package/THIRD_PARTY_NOTICES.md +4 -0
  3. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +376 -0
  4. package/node_modules/@luxusai/pi-hindsight/README.md +92 -0
  5. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +56 -0
  6. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +169 -0
  7. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +107 -0
  8. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +89 -0
  9. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +189 -0
  10. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  11. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  12. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +54 -0
  13. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +75 -0
  14. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +300 -0
  15. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +158 -0
  16. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +164 -0
  17. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +139 -0
  18. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +74 -0
  19. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +261 -0
  20. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +151 -0
  21. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +18 -0
  22. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +46 -0
  23. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +383 -0
  24. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +240 -0
  25. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +176 -0
  26. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +178 -0
  27. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +41 -0
  28. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +386 -0
  29. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +39 -0
  30. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +34 -0
  31. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +140 -0
  32. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +61 -0
  33. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +925 -0
  34. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +247 -0
  35. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +547 -0
  36. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +480 -0
  37. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +182 -0
  38. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +86 -0
  39. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +1010 -0
  40. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +175 -0
  41. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +425 -0
  42. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +210 -0
  43. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +817 -0
  44. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +25 -0
  45. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +319 -0
  46. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +196 -0
  47. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +189 -0
  48. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +65 -0
  49. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +260 -0
  50. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +244 -0
  51. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +59 -0
  52. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +82 -0
  53. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +45 -0
  54. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +331 -0
  55. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +354 -0
  56. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +66 -0
  57. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +132 -0
  58. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +228 -0
  59. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +74 -0
  60. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +16 -0
  61. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +648 -0
  62. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +105 -0
  63. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +68 -0
  64. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +71 -0
  65. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +13 -0
  66. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +162 -0
  67. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +131 -0
  68. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +104 -0
  69. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +37 -0
  70. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +893 -0
  71. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +35 -0
  72. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +177 -0
  73. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +7 -0
  74. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +21 -0
  75. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +112 -0
  76. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +111 -0
  77. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +214 -0
  78. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +70 -0
  79. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +392 -0
  80. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +45 -0
  81. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +9 -0
  82. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +860 -0
  83. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +105 -0
  84. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +189 -0
  85. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +299 -0
  86. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +201 -0
  87. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +47 -0
  88. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +239 -0
  89. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +50 -0
  90. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +259 -0
  91. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +77 -0
  92. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +534 -0
  93. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +319 -0
  94. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +325 -0
  95. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +42 -0
  96. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +244 -0
  97. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +56 -0
  98. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +50 -0
  99. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +167 -0
  100. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +207 -0
  101. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +120 -0
  102. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +8 -0
  103. package/node_modules/@luxusai/pi-hindsight/package.json +118 -0
  104. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +75 -0
  105. package/node_modules/@narumitw/pi-btw/package.json +2 -2
  106. package/node_modules/pi-mcp-adapter/CHANGELOG.md +13 -0
  107. package/node_modules/pi-mcp-adapter/config.ts +10 -0
  108. package/node_modules/pi-mcp-adapter/dist/config.js +10 -0
  109. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  110. package/node_modules/pi-mcp-adapter/init.ts +5 -1
  111. package/node_modules/pi-mcp-adapter/package.json +1 -1
  112. package/package.json +10 -4
@@ -0,0 +1,386 @@
1
+ import {
2
+ CLIENT_VERSION,
3
+ HindsightClient,
4
+ HindsightError,
5
+ createClient,
6
+ createConfig,
7
+ sdk,
8
+ } from "@vectorize-io/hindsight-client";
9
+ import type { BankTemplateManifest, Client, ReflectRequest } from "@vectorize-io/hindsight-client";
10
+ import { redactError } from "../utils/sanitize.js";
11
+ import type { HindsightLikeClient, ResolvedConfig } from "../types.js";
12
+ import { PI_HINDSIGHT_USER_AGENT } from "../version.js";
13
+ import { withRetry } from "./client-retry.js";
14
+ import { installFetchRequestCompat } from "./fetch-compat.js";
15
+ import { withTimeout } from "./timeout.js";
16
+
17
+ type ReflectOptions = Parameters<HindsightLikeClient["reflect"]>[2];
18
+ type RetainOptions = Parameters<HindsightLikeClient["retain"]>[2];
19
+
20
+ function sdkHeaders(config: ResolvedConfig): Record<string, string> {
21
+ const headers: Record<string, string> = {
22
+ "User-Agent": PI_HINDSIGHT_USER_AGENT,
23
+ };
24
+ if (config.hindsight.apiKey) headers.Authorization = `Bearer ${config.hindsight.apiKey}`;
25
+ return headers;
26
+ }
27
+
28
+ function createLowLevelClient(config: ResolvedConfig): Client {
29
+ return createClient(
30
+ createConfig({
31
+ baseUrl: config.hindsight.baseUrl.replace(/\/$/, ""),
32
+ headers: sdkHeaders(config),
33
+ }),
34
+ );
35
+ }
36
+
37
+ function unwrapSdkResponse<T>(
38
+ response: { data?: T; error?: unknown; response?: Response },
39
+ operation: string,
40
+ ): T {
41
+ if (response.data !== undefined) return response.data;
42
+ const error = response.error as { detail?: unknown; message?: string } | undefined;
43
+ const statusCode = response.response?.status;
44
+ const details = error?.detail ?? error?.message ?? error;
45
+ throw new HindsightError(`${operation} failed: ${JSON.stringify(details)}`, statusCode, details);
46
+ }
47
+
48
+ function reflectInclude(options: ReflectOptions): ReflectRequest["include"] | undefined {
49
+ if (options?.includeFacts === undefined && options?.includeToolCalls === undefined) {
50
+ return undefined;
51
+ }
52
+ const include: NonNullable<ReflectRequest["include"]> = {};
53
+ if (options?.includeFacts !== undefined) {
54
+ include.facts = options.includeFacts ? {} : null;
55
+ }
56
+ if (options?.includeToolCalls !== undefined) {
57
+ include.tool_calls = options.includeToolCalls
58
+ ? options.includeToolCallOutput === false
59
+ ? { output: false }
60
+ : {}
61
+ : null;
62
+ }
63
+ return include;
64
+ }
65
+
66
+ function reflectRequestBody(query: string, options: ReflectOptions | undefined): ReflectRequest {
67
+ const body: ReflectRequest = {
68
+ query,
69
+ budget: options?.budget ?? "low",
70
+ };
71
+ if (options?.context) body.context = options.context;
72
+ if (options?.maxTokens !== undefined) body.max_tokens = options.maxTokens;
73
+ if (options?.responseSchema) body.response_schema = options.responseSchema;
74
+ if (options?.factTypes) body.fact_types = options.factTypes;
75
+ if (options?.excludeMentalModels !== undefined) {
76
+ body.exclude_mental_models = options.excludeMentalModels;
77
+ }
78
+ if (options?.excludeMentalModelIds) {
79
+ body.exclude_mental_model_ids = options.excludeMentalModelIds;
80
+ }
81
+ if (options?.tagGroups) body.tag_groups = options.tagGroups;
82
+ else if (options?.tags) body.tags = options.tags;
83
+ if (!options?.tagGroups && options?.tagsMatch) body.tags_match = options.tagsMatch;
84
+ const include = reflectInclude(options);
85
+ if (include) body.include = include;
86
+ return body;
87
+ }
88
+
89
+ function retainBatchItem(content: string, options: RetainOptions) {
90
+ return {
91
+ content,
92
+ ...(options?.timestamp ? { timestamp: options.timestamp } : {}),
93
+ ...(options?.context ? { context: options.context } : {}),
94
+ ...(options?.metadata ? { metadata: options.metadata } : {}),
95
+ ...(options?.documentId ? { document_id: options.documentId } : {}),
96
+ ...(options?.entities?.length ? { entities: options.entities } : {}),
97
+ ...(options?.tags ? { tags: options.tags } : {}),
98
+ ...(options?.observationScopes?.length
99
+ ? { observation_scopes: options.observationScopes }
100
+ : {}),
101
+ ...(options?.updateMode ? { update_mode: options.updateMode } : {}),
102
+ ...(options?.strategy ? { strategy: options.strategy } : {}),
103
+ };
104
+ }
105
+
106
+ function retainSingle(
107
+ raw: HindsightClient,
108
+ bankId: string,
109
+ content: string,
110
+ options: RetainOptions,
111
+ signal: AbortSignal,
112
+ ) {
113
+ if (options?.documentTags?.length) {
114
+ return raw.retainBatch(bankId, [retainBatchItem(content, options)], {
115
+ ...(options.async !== undefined ? { async: options.async } : {}),
116
+ ...(options.async === true && options.operationId != null
117
+ ? { operationId: options.operationId }
118
+ : {}),
119
+ documentTags: options.documentTags,
120
+ signal,
121
+ });
122
+ }
123
+
124
+ const { documentTags: _documentTags, signal: _signal, ...retainOptions } = options ?? {};
125
+ return raw.retain(bankId, content, { ...retainOptions, signal });
126
+ }
127
+
128
+ async function reflect(
129
+ args: {
130
+ raw: HindsightClient;
131
+ lowLevel: Client;
132
+ bankId: string;
133
+ query: string;
134
+ options: ReflectOptions;
135
+ },
136
+ signal: AbortSignal,
137
+ ): Promise<unknown> {
138
+ if (args.options?.maxTokens !== undefined) {
139
+ const response = await sdk.reflect({
140
+ client: args.lowLevel,
141
+ path: { bank_id: args.bankId },
142
+ body: reflectRequestBody(args.query, args.options),
143
+ signal,
144
+ });
145
+ return unwrapSdkResponse(response, "reflect");
146
+ }
147
+
148
+ return args.raw.reflect(args.bankId, args.query, { ...args.options, signal });
149
+ }
150
+
151
+ async function importBankTemplate(
152
+ lowLevel: Client,
153
+ bankId: string,
154
+ manifest: BankTemplateManifest,
155
+ options: { dryRun?: boolean } | undefined,
156
+ signal: AbortSignal,
157
+ ): Promise<unknown> {
158
+ const response = await sdk.importBankTemplate({
159
+ client: lowLevel,
160
+ path: { bank_id: bankId },
161
+ ...(options?.dryRun ? { query: { dry_run: true } } : {}),
162
+ // The generated SDK types `body` as `never` for this endpoint: the OpenAPI schema
163
+ // declares a raw JSON object rather than a typed model. The request body is still the
164
+ // documented BankTemplateManifest shape (Hindsight's Bank Templates API docs).
165
+ body: manifest as never,
166
+ signal,
167
+ });
168
+ return unwrapSdkResponse(response, "importBankTemplate");
169
+ }
170
+
171
+ export function createHindsightClient(config: ResolvedConfig): HindsightLikeClient {
172
+ installFetchRequestCompat();
173
+
174
+ const raw = new HindsightClient({
175
+ baseUrl: config.hindsight.baseUrl,
176
+ ...(config.hindsight.apiKey ? { apiKey: config.hindsight.apiKey } : {}),
177
+ userAgent: PI_HINDSIGHT_USER_AGENT,
178
+ });
179
+ const lowLevel = createLowLevelClient(config);
180
+ const timeoutMs = config.hindsight.timeoutMs;
181
+
182
+ return {
183
+ retain: (bankId, content, options) =>
184
+ withTimeout(
185
+ "hindsight retain",
186
+ timeoutMs,
187
+ (signal) => retainSingle(raw, bankId, content, options, signal),
188
+ options?.signal,
189
+ ),
190
+ retainBatch: (...args) =>
191
+ withTimeout(
192
+ "hindsight retainBatch",
193
+ timeoutMs,
194
+ (signal) => {
195
+ const [bankId, items, options] = args;
196
+ return raw.retainBatch(bankId, items, { ...options, signal });
197
+ },
198
+ args[2]?.signal,
199
+ ),
200
+ recall: (...args) => {
201
+ const [bankId, query, options] = args;
202
+ return withTimeout(
203
+ "hindsight recall",
204
+ timeoutMs,
205
+ (signal) => raw.recall(bankId, query, { ...options, signal }),
206
+ options?.signal,
207
+ );
208
+ },
209
+ reflect: (bankId, query, options) =>
210
+ withTimeout(
211
+ "hindsight reflect",
212
+ timeoutMs,
213
+ (signal) => reflect({ raw, lowLevel, bankId, query, options }, signal),
214
+ options?.signal,
215
+ ),
216
+ createBank: (...args) =>
217
+ withTimeout("hindsight createBank", timeoutMs, (signal) => {
218
+ const [bankId, options] = args;
219
+ return raw.createBank(bankId, { ...options, signal });
220
+ }),
221
+ getBankProfile: (...args) =>
222
+ withTimeout("hindsight getBankProfile", timeoutMs, (signal) =>
223
+ withRetry("getBankProfile", () => raw.getBankProfile(args[0], { signal })),
224
+ ),
225
+ getBankStats: (bankId) =>
226
+ withTimeout("hindsight getBankStats", timeoutMs, (signal) =>
227
+ withRetry("getBankStats", async () => {
228
+ const response = await sdk.getAgentStats({
229
+ client: lowLevel,
230
+ path: { bank_id: bankId },
231
+ signal,
232
+ });
233
+ return unwrapSdkResponse(response, "getBankStats");
234
+ }),
235
+ ),
236
+ getBankConfig: (bankId) =>
237
+ withTimeout("hindsight getBankConfig", timeoutMs, (signal) =>
238
+ withRetry("getBankConfig", () => raw.getBankConfig(bankId, { signal })),
239
+ ),
240
+ importBankTemplate: (bankId, manifest, options) =>
241
+ withTimeout(
242
+ "hindsight importBankTemplate",
243
+ timeoutMs,
244
+ (signal) => importBankTemplate(lowLevel, bankId, manifest, options, signal),
245
+ options?.signal,
246
+ ),
247
+ listMentalModels: (bankId, options) =>
248
+ withTimeout(
249
+ "hindsight listMentalModels",
250
+ timeoutMs,
251
+ (signal) => raw.listMentalModels(bankId, { ...options, signal }),
252
+ options?.signal,
253
+ ),
254
+ getMentalModel: (bankId, mentalModelId, options) =>
255
+ withTimeout(
256
+ "hindsight getMentalModel",
257
+ timeoutMs,
258
+ (signal) => raw.getMentalModel(bankId, mentalModelId, { ...options, signal }),
259
+ options?.signal,
260
+ ),
261
+ createMentalModel: (bankId, name, sourceQuery, options) =>
262
+ withTimeout(
263
+ "hindsight createMentalModel",
264
+ timeoutMs,
265
+ (signal) => raw.createMentalModel(bankId, name, sourceQuery, { ...options, signal }),
266
+ options?.signal,
267
+ ),
268
+ updateMentalModel: (bankId, mentalModelId, options) =>
269
+ withTimeout(
270
+ "hindsight updateMentalModel",
271
+ timeoutMs,
272
+ (signal) => raw.updateMentalModel(bankId, mentalModelId, { ...options, signal }),
273
+ options?.signal,
274
+ ),
275
+ refreshMentalModel: (bankId, mentalModelId, options) =>
276
+ withTimeout(
277
+ "hindsight refreshMentalModel",
278
+ timeoutMs,
279
+ (signal) => raw.refreshMentalModel(bankId, mentalModelId, { ...options, signal }),
280
+ options?.signal,
281
+ ),
282
+ deleteMentalModel: (bankId, mentalModelId, options) =>
283
+ withTimeout(
284
+ "hindsight deleteMentalModel",
285
+ timeoutMs,
286
+ async (signal) => {
287
+ if (options?.dryRun) {
288
+ return { dryRun: true, bankId, mentalModelId, wouldDelete: true };
289
+ }
290
+ await raw.deleteMentalModel(bankId, mentalModelId, { signal });
291
+ return { deleted: true, bankId, mentalModelId };
292
+ },
293
+ options?.signal,
294
+ ),
295
+ getKnowledgeBaseTree: (bankId, options) =>
296
+ withTimeout(
297
+ "hindsight getKnowledgeBaseTree",
298
+ timeoutMs,
299
+ (signal) => raw.getKnowledgeBaseTree(bankId, { ...options, signal }),
300
+ options?.signal,
301
+ ),
302
+ createKnowledgeFolder: (bankId, name, options) =>
303
+ withTimeout(
304
+ "hindsight createKnowledgeFolder",
305
+ timeoutMs,
306
+ (signal) => raw.createKnowledgeFolder(bankId, name, { ...options, signal }),
307
+ options?.signal,
308
+ ),
309
+ createKnowledgePage: (bankId, name, sourceQuery, options) =>
310
+ withTimeout(
311
+ "hindsight createKnowledgePage",
312
+ timeoutMs,
313
+ (signal) => raw.createKnowledgePage(bankId, name, sourceQuery, { ...options, signal }),
314
+ options?.signal,
315
+ ),
316
+ getKnowledgePage: (bankId, pageId, options) =>
317
+ withTimeout(
318
+ "hindsight getKnowledgePage",
319
+ timeoutMs,
320
+ (signal) => raw.getKnowledgePage(bankId, pageId, { ...options, signal }),
321
+ options?.signal,
322
+ ),
323
+ searchKnowledgeBase: (bankId, query, options) =>
324
+ withTimeout(
325
+ "hindsight searchKnowledgeBase",
326
+ timeoutMs,
327
+ (signal) => raw.searchKnowledgeBase(bankId, query, { ...options, signal }),
328
+ options?.signal,
329
+ ),
330
+ updateKnowledgeNode: (bankId, nodeId, options) =>
331
+ withTimeout(
332
+ "hindsight updateKnowledgeNode",
333
+ timeoutMs,
334
+ (signal) => raw.updateKnowledgeNode(bankId, nodeId, { ...options, signal }),
335
+ options?.signal,
336
+ ),
337
+ deleteKnowledgeNode: (bankId, nodeId, options) =>
338
+ withTimeout(
339
+ "hindsight deleteKnowledgeNode",
340
+ timeoutMs,
341
+ (signal) => raw.deleteKnowledgeNode(bankId, nodeId, { ...options, signal }),
342
+ options?.signal,
343
+ ),
344
+ exportKnowledgeBase: (bankId, options) =>
345
+ withTimeout(
346
+ "hindsight exportKnowledgeBase",
347
+ timeoutMs,
348
+ (signal) => raw.exportKnowledgeBase(bankId, { ...options, signal }),
349
+ options?.signal,
350
+ ),
351
+ updateBankConfig: (bankId, options) =>
352
+ withTimeout(
353
+ "hindsight updateBankConfig",
354
+ timeoutMs,
355
+ (signal) => raw.updateBankConfig(bankId, { ...options, signal }),
356
+ options?.signal,
357
+ ),
358
+ health: () =>
359
+ withTimeout("hindsight health", timeoutMs, (signal) =>
360
+ withRetry("health", async () => {
361
+ const response = await sdk.healthEndpointHealthGet({ client: lowLevel, signal });
362
+ return unwrapSdkResponse(response, "health");
363
+ }),
364
+ ),
365
+ getVersion: () =>
366
+ withTimeout("hindsight getVersion", timeoutMs, (signal) =>
367
+ withRetry("getVersion", () => raw.getVersion({ signal })),
368
+ ),
369
+ };
370
+ }
371
+
372
+ export async function checkHindsight(
373
+ client: HindsightLikeClient,
374
+ bankId: string,
375
+ ): Promise<{ ok: boolean; error?: string }> {
376
+ try {
377
+ if (client.health) await client.health();
378
+ else if (client.getBankProfile) await client.getBankProfile(bankId);
379
+ else await client.recall(bankId, "health check", { maxTokens: 1, budget: "low" });
380
+ return { ok: true };
381
+ } catch (error) {
382
+ return { ok: false, error: redactError(error) };
383
+ }
384
+ }
385
+
386
+ export const HINDSIGHT_CLIENT_VERSION = CLIENT_VERSION;
@@ -0,0 +1,39 @@
1
+ const PATCH_MARKER: unique symbol = Symbol("@luxusai/pi-hindsight.fetch-request-compat");
2
+
3
+ type FetchWithMarker = typeof fetch & { [PATCH_MARKER]?: true };
4
+ type RequestInitWithDuplex = RequestInit & { duplex?: "half" };
5
+
6
+ function isRequest(value: unknown): value is Request {
7
+ return typeof Request !== "undefined" && value instanceof Request;
8
+ }
9
+
10
+ function requestToInit(request: Request): RequestInitWithDuplex {
11
+ return {
12
+ method: request.method,
13
+ headers: request.headers,
14
+ body: request.body,
15
+ signal: request.signal,
16
+ redirect: request.redirect,
17
+ credentials: request.credentials,
18
+ cache: request.cache,
19
+ mode: request.mode,
20
+ referrer: request.referrer,
21
+ referrerPolicy: request.referrerPolicy,
22
+ integrity: request.integrity,
23
+ keepalive: request.keepalive,
24
+ ...(request.body ? { duplex: "half" as const } : {}),
25
+ };
26
+ }
27
+
28
+ export function installFetchRequestCompat(): void {
29
+ const currentFetch = globalThis.fetch as FetchWithMarker | undefined;
30
+ if (!currentFetch || currentFetch[PATCH_MARKER]) return;
31
+
32
+ const originalFetch = currentFetch.bind(globalThis) as typeof fetch;
33
+ const patchedFetch: FetchWithMarker = ((input: RequestInfo | URL, init?: RequestInit) => {
34
+ if (!isRequest(input)) return originalFetch(input, init);
35
+ return originalFetch(input.url, { ...requestToInit(input), ...init });
36
+ }) as FetchWithMarker;
37
+ patchedFetch[PATCH_MARKER] = true;
38
+ globalThis.fetch = patchedFetch;
39
+ }
@@ -0,0 +1,34 @@
1
+ export async function withTimeout<T>(
2
+ operation: string,
3
+ timeoutMs: number,
4
+ fn: (signal: AbortSignal) => Promise<T>,
5
+ parentSignal?: AbortSignal,
6
+ ): Promise<T> {
7
+ if (parentSignal?.aborted) throw new Error(`${operation} aborted`);
8
+
9
+ const controller = new AbortController();
10
+ let timer: NodeJS.Timeout | undefined;
11
+ let removeAbortListener: (() => void) | undefined;
12
+ try {
13
+ const timeout = new Promise<never>((_resolve, reject) => {
14
+ timer = setTimeout(() => {
15
+ controller.abort();
16
+ reject(new Error(`${operation} timed out after ${timeoutMs}ms`));
17
+ }, timeoutMs);
18
+ });
19
+ const abort = parentSignal
20
+ ? new Promise<never>((_resolve, reject) => {
21
+ const onAbort = () => {
22
+ controller.abort(parentSignal.reason);
23
+ reject(new Error(`${operation} aborted`));
24
+ };
25
+ parentSignal.addEventListener("abort", onAbort, { once: true });
26
+ removeAbortListener = () => parentSignal.removeEventListener("abort", onAbort);
27
+ })
28
+ : undefined;
29
+ return await Promise.race([fn(controller.signal), timeout, ...(abort ? [abort] : [])]);
30
+ } finally {
31
+ if (timer) clearTimeout(timer);
32
+ removeAbortListener?.();
33
+ }
34
+ }
@@ -0,0 +1,140 @@
1
+ import type { ResolvedConfig } from "../types.js";
2
+
3
+ export const DEFAULT_CONFIG: ResolvedConfig = {
4
+ enabled: true,
5
+ setupComplete: false,
6
+ scope: {
7
+ mode: "domain-tagged",
8
+ projectIdStrategy: "remote",
9
+ includeSharedObservations: false,
10
+ },
11
+ hindsight: { baseUrl: "http://localhost:8888", timeoutMs: 30_000 },
12
+ agentUse: "coding",
13
+ mentalModels: {
14
+ inject: true,
15
+ maxChars: 12_000,
16
+ cacheTtlMs: 300_000,
17
+ },
18
+ banks: {
19
+ project: { enabled: true, derive: "repo" },
20
+ global: { enabled: false },
21
+ user: { enabled: false },
22
+ },
23
+ recall: {
24
+ enabled: true,
25
+ budget: "mid",
26
+ maxTokens: 800,
27
+ userMaxTokens: 400,
28
+ types: ["observation"],
29
+ includeSourceFacts: false,
30
+ maxSourceFactsTokens: 4096,
31
+ contextTurns: 2,
32
+ roles: ["user", "assistant"],
33
+ maxQueryChars: 800,
34
+ queryPreamble: "Pi coding task memory lookup.",
35
+ projectQueryPreamble:
36
+ "Project memory lookup for current repo architecture, tasks, bugs, decisions, and constraints.",
37
+ globalQueryPreamble:
38
+ "User memory lookup for durable user preferences, recurring workflows, coding habits, and cross-project context.",
39
+ userQueryPreamble:
40
+ "User memory lookup for durable user preferences, recurring workflows, coding habits, and cross-project context.",
41
+ includeDateInQuery: false,
42
+ includeRepoHintsInQuery: true,
43
+ storeLastRecall: false,
44
+ storeLastRecallFailures: false,
45
+ lastRecallPath: ".pi/hindsight/last-recall.json",
46
+ topK: 8,
47
+ timeoutMs: 40_000,
48
+ cacheTtlMs: 60_000,
49
+ injectionMode: "context",
50
+ injectionPosition: "append",
51
+ includeFactsInDebug: false,
52
+ preferObservations: true,
53
+ },
54
+ observations: {
55
+ enabled: true,
56
+ // Project-stable scope; {repoKey} remains a supported placeholder for legacy configs.
57
+ scopes: [["harness:pi"], ["project:{projectId}"]],
58
+ },
59
+ globalRetain: {
60
+ mode: "explicit-only",
61
+ },
62
+ userRetain: {
63
+ mode: "explicit-only",
64
+ },
65
+ retain: {
66
+ enabled: true,
67
+ async: true,
68
+ delivery: "immediate",
69
+ updateMode: "append",
70
+ content: {
71
+ user: ["text"],
72
+ assistant: ["text", "toolCall"],
73
+ toolResult: ["error"],
74
+ },
75
+ toolFilter: {
76
+ toolCall: {
77
+ exclude: [
78
+ "hindsight_retain",
79
+ "hindsight_retain_global",
80
+ "hindsight_delete_document",
81
+ "hindsight_retain_receipts",
82
+ "hindsight_route_memory",
83
+ "hindsight_recall",
84
+ "hindsight_reflect",
85
+ ],
86
+ },
87
+ toolResult: {
88
+ exclude: [
89
+ "hindsight_retain",
90
+ "hindsight_retain_global",
91
+ "hindsight_delete_document",
92
+ "hindsight_retain_receipts",
93
+ "hindsight_route_memory",
94
+ "hindsight_recall",
95
+ "hindsight_reflect",
96
+ "read",
97
+ "grep",
98
+ "find",
99
+ "find_files",
100
+ "ls",
101
+ ],
102
+ },
103
+ },
104
+ compactToolCalls: true,
105
+ strip: { message: ["usage", "cost", "responseId"], topLevel: ["id", "parentId"] },
106
+ redactSecrets: true,
107
+ entities: [],
108
+ queuePath: ".pi/hindsight/retain-queue.jsonl",
109
+ flushIntervalMs: 0,
110
+ periodicFlushMaxJobs: 1,
111
+ periodicFlushTimeoutMs: 2_000,
112
+ shutdownFlushMaxJobs: 10,
113
+ shutdownFlushTimeoutMs: 2_000,
114
+ postRetainReflect: false,
115
+ },
116
+ import: {
117
+ mode: "curated",
118
+ qualityProfile: "compatible",
119
+ turnsPerDocument: 12,
120
+ maxDocumentBytes: 80_000,
121
+ includeBranches: "current-only",
122
+ toolResults: "errors-only",
123
+ toolResultSummaryMaxChars: 500,
124
+ replaceExistingImportedDocs: true,
125
+ manifestPath: ".pi/hindsight/import-manifest.json",
126
+ checkpointPath: ".pi/hindsight/import-checkpoint.json",
127
+ resume: true,
128
+ },
129
+ status: {
130
+ style: "text",
131
+ detail: "activity",
132
+ maxLength: 24,
133
+ showActivity: true,
134
+ },
135
+ notifications: {
136
+ startup: true,
137
+ recall: false,
138
+ retain: false,
139
+ },
140
+ };
@@ -0,0 +1,61 @@
1
+ import type { ResolvedConfig } from "../types.js";
2
+ import { readGlobalConfig, readProjectConfig } from "./config-writer.js";
3
+ import type {
4
+ ConfigEditingField,
5
+ ConfigEditingTab,
6
+ ConfigLayers,
7
+ TabId,
8
+ } from "./config-editing-registry.js";
9
+ import {
10
+ buildConfigEditingFieldsFromRegistry,
11
+ buildStatusFacts,
12
+ } from "./config-editing-registry.js";
13
+
14
+ export type {
15
+ ConfigEditingField,
16
+ ConfigEditingKind,
17
+ ConfigEditingTab,
18
+ ConfigLayers,
19
+ FieldId,
20
+ TabId,
21
+ } from "./config-editing-registry.js";
22
+ export {
23
+ enabledDisabled,
24
+ inputDefaultForConfigEditingField,
25
+ parseConfigEditingFieldInput,
26
+ patchForConfigEditingField,
27
+ } from "./config-editing-registry.js";
28
+
29
+ export function readConfigLayers(cwd: string): ConfigLayers {
30
+ return { project: readProjectConfig(cwd), global: readGlobalConfig(), env: process.env };
31
+ }
32
+
33
+ export function buildConfigEditingFields(
34
+ config: ResolvedConfig,
35
+ projectBankId: string,
36
+ layers: ConfigLayers,
37
+ ): ConfigEditingField[] {
38
+ return buildConfigEditingFieldsFromRegistry(config, projectBankId, layers);
39
+ }
40
+
41
+ export function buildConfigEditingTabs(
42
+ config: ResolvedConfig,
43
+ projectBankId: string,
44
+ layers: ConfigLayers,
45
+ statusFacts: Array<[string, string]> = [],
46
+ options: { showAdvanced?: boolean } = {},
47
+ ): ConfigEditingTab[] {
48
+ const fields = buildConfigEditingFields(config, projectBankId, layers);
49
+ // Default hub is Status only; advanced unlocks full settings tabs.
50
+ const ids: TabId[] = options.showAdvanced
51
+ ? ["Status", "Connection", "Banks", "Recall", "Retain", "Import", "UI"]
52
+ : ["Status"];
53
+ return ids.map((id) => ({
54
+ id,
55
+ fields:
56
+ id === "Status"
57
+ ? []
58
+ : fields.filter((field) => field.tab === id && (options.showAdvanced || !field.advanced)),
59
+ ...(id === "Status" ? { facts: buildStatusFacts(config, projectBankId, statusFacts) } : {}),
60
+ }));
61
+ }