@herbertgao/pi-extensions 2026.9.7 → 2026.9.9

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 (158) hide show
  1. package/README.md +8 -4
  2. package/THIRD_PARTY_NOTICES.md +52 -0
  3. package/node_modules/@herbertgao/pi-bark/package.json +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  5. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/package.json +4 -4
  7. package/node_modules/@herbertgao/resume-from/package.json +2 -2
  8. package/node_modules/@narumitw/pi-btw/README.md +6 -1
  9. package/node_modules/@narumitw/pi-btw/dist/index.ts +209 -23
  10. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  11. package/node_modules/@narumitw/pi-btw/package.json +1 -1
  12. package/node_modules/@narumitw/pi-btw/src/btw.ts +1 -12
  13. package/node_modules/@narumitw/pi-btw/src/menu.ts +244 -15
  14. package/node_modules/@narumitw/pi-btw/src/settings.ts +5 -0
  15. package/node_modules/grok-mermaid/CHANGELOG.md +46 -0
  16. package/node_modules/grok-mermaid/LICENSE +205 -0
  17. package/node_modules/grok-mermaid/README.md +191 -0
  18. package/node_modules/grok-mermaid/dist/ansi.d.ts +16 -0
  19. package/node_modules/grok-mermaid/dist/ansi.d.ts.map +1 -0
  20. package/node_modules/grok-mermaid/dist/ansi.js +23 -0
  21. package/node_modules/grok-mermaid/dist/ansi.js.map +1 -0
  22. package/node_modules/grok-mermaid/dist/canvas.d.ts +87 -0
  23. package/node_modules/grok-mermaid/dist/canvas.d.ts.map +1 -0
  24. package/node_modules/grok-mermaid/dist/canvas.js +366 -0
  25. package/node_modules/grok-mermaid/dist/canvas.js.map +1 -0
  26. package/node_modules/grok-mermaid/dist/graph.d.ts +74 -0
  27. package/node_modules/grok-mermaid/dist/graph.d.ts.map +1 -0
  28. package/node_modules/grok-mermaid/dist/graph.js +91 -0
  29. package/node_modules/grok-mermaid/dist/graph.js.map +1 -0
  30. package/node_modules/grok-mermaid/dist/index.d.ts +32 -0
  31. package/node_modules/grok-mermaid/dist/index.d.ts.map +1 -0
  32. package/node_modules/grok-mermaid/dist/index.js +100 -0
  33. package/node_modules/grok-mermaid/dist/index.js.map +1 -0
  34. package/node_modules/grok-mermaid/dist/labels.d.ts +62 -0
  35. package/node_modules/grok-mermaid/dist/labels.d.ts.map +1 -0
  36. package/node_modules/grok-mermaid/dist/labels.js +324 -0
  37. package/node_modules/grok-mermaid/dist/labels.js.map +1 -0
  38. package/node_modules/grok-mermaid/dist/layout-seq.d.ts +12 -0
  39. package/node_modules/grok-mermaid/dist/layout-seq.d.ts.map +1 -0
  40. package/node_modules/grok-mermaid/dist/layout-seq.js +194 -0
  41. package/node_modules/grok-mermaid/dist/layout-seq.js.map +1 -0
  42. package/node_modules/grok-mermaid/dist/layout.d.ts +87 -0
  43. package/node_modules/grok-mermaid/dist/layout.d.ts.map +1 -0
  44. package/node_modules/grok-mermaid/dist/layout.js +881 -0
  45. package/node_modules/grok-mermaid/dist/layout.js.map +1 -0
  46. package/node_modules/grok-mermaid/dist/parse.d.ts +83 -0
  47. package/node_modules/grok-mermaid/dist/parse.d.ts.map +1 -0
  48. package/node_modules/grok-mermaid/dist/parse.js +1151 -0
  49. package/node_modules/grok-mermaid/dist/parse.js.map +1 -0
  50. package/node_modules/grok-mermaid/dist/source-box.d.ts +18 -0
  51. package/node_modules/grok-mermaid/dist/source-box.d.ts.map +1 -0
  52. package/node_modules/grok-mermaid/dist/source-box.js +78 -0
  53. package/node_modules/grok-mermaid/dist/source-box.js.map +1 -0
  54. package/node_modules/grok-mermaid/dist/types.d.ts +42 -0
  55. package/node_modules/grok-mermaid/dist/types.d.ts.map +1 -0
  56. package/node_modules/grok-mermaid/dist/types.js +1 -0
  57. package/node_modules/grok-mermaid/dist/types.js.map +1 -0
  58. package/node_modules/grok-mermaid/dist/width-data.d.ts +2 -0
  59. package/node_modules/grok-mermaid/dist/width-data.d.ts.map +1 -0
  60. package/node_modules/grok-mermaid/dist/width-data.js +994 -0
  61. package/node_modules/grok-mermaid/dist/width-data.js.map +1 -0
  62. package/node_modules/grok-mermaid/dist/width.d.ts +18 -0
  63. package/node_modules/grok-mermaid/dist/width.d.ts.map +1 -0
  64. package/node_modules/grok-mermaid/dist/width.js +76 -0
  65. package/node_modules/grok-mermaid/dist/width.js.map +1 -0
  66. package/node_modules/grok-mermaid/package.json +49 -0
  67. package/node_modules/grok-mermaid/src/ansi.ts +34 -0
  68. package/node_modules/grok-mermaid/src/canvas.ts +373 -0
  69. package/node_modules/grok-mermaid/src/graph.ts +142 -0
  70. package/node_modules/grok-mermaid/src/index.ts +104 -0
  71. package/node_modules/grok-mermaid/src/labels.ts +326 -0
  72. package/node_modules/grok-mermaid/src/layout-seq.ts +203 -0
  73. package/node_modules/grok-mermaid/src/layout.ts +1015 -0
  74. package/node_modules/grok-mermaid/src/parse.ts +1189 -0
  75. package/node_modules/grok-mermaid/src/source-box.ts +89 -0
  76. package/node_modules/grok-mermaid/src/types.ts +43 -0
  77. package/node_modules/grok-mermaid/src/width-data.ts +993 -0
  78. package/node_modules/grok-mermaid/src/width.ts +74 -0
  79. package/node_modules/pi-jev-auto-mode/CHANGELOG.md +110 -0
  80. package/node_modules/pi-jev-auto-mode/LICENSE +21 -0
  81. package/node_modules/pi-jev-auto-mode/README.md +285 -0
  82. package/node_modules/pi-jev-auto-mode/SECURITY.md +32 -0
  83. package/node_modules/pi-jev-auto-mode/docs/calibration.md +154 -0
  84. package/node_modules/pi-jev-auto-mode/docs/design.md +164 -0
  85. package/node_modules/pi-jev-auto-mode/docs/security.md +124 -0
  86. package/node_modules/pi-jev-auto-mode/index.ts +1 -0
  87. package/node_modules/pi-jev-auto-mode/package.json +72 -0
  88. package/node_modules/pi-jev-auto-mode/src/call.ts +180 -0
  89. package/node_modules/pi-jev-auto-mode/src/decide.ts +86 -0
  90. package/node_modules/pi-jev-auto-mode/src/extension.ts +862 -0
  91. package/node_modules/pi-jev-auto-mode/src/intent.ts +71 -0
  92. package/node_modules/pi-jev-auto-mode/src/jev/availability.ts +53 -0
  93. package/node_modules/pi-jev-auto-mode/src/jev/criteria.ts +19 -0
  94. package/node_modules/pi-jev-auto-mode/src/jev/decide.ts +187 -0
  95. package/node_modules/pi-jev-auto-mode/src/jev/engine.ts +165 -0
  96. package/node_modules/pi-jev-auto-mode/src/jev/index.ts +20 -0
  97. package/node_modules/pi-jev-auto-mode/src/jev/questions.ts +273 -0
  98. package/node_modules/pi-jev-auto-mode/src/jev/response.ts +64 -0
  99. package/node_modules/pi-jev-auto-mode/src/jev/state.ts +20 -0
  100. package/node_modules/pi-jev-auto-mode/src/jev/transport.ts +117 -0
  101. package/node_modules/pi-jev-auto-mode/src/jev/types.ts +46 -0
  102. package/node_modules/pi-jev-auto-mode/src/policy.ts +558 -0
  103. package/node_modules/pi-jev-auto-mode/src/records.ts +118 -0
  104. package/node_modules/pi-jev-auto-mode/src/settings.ts +327 -0
  105. package/node_modules/pi-jev-auto-mode/src/ui.ts +233 -0
  106. package/node_modules/pi-lens/CHANGELOG.md +45 -0
  107. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +21 -9
  108. package/node_modules/pi-lens/dist/clients/cascade-format.js +24 -0
  109. package/node_modules/pi-lens/dist/clients/extension-log.js +48 -1
  110. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +11 -12
  111. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +3 -0
  112. package/node_modules/pi-lens/dist/clients/lsp/index.js +41 -7
  113. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +9 -2
  114. package/node_modules/pi-lens/dist/clients/path-utils.js +51 -9
  115. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +271 -0
  116. package/node_modules/pi-lens/dist/clients/pipeline.js +15 -1
  117. package/node_modules/pi-lens/dist/clients/runtime-turn.js +124 -7
  118. package/node_modules/pi-lens/dist/clients/skills-resolver.js +38 -2
  119. package/node_modules/pi-lens/dist/clients/widget-state.js +152 -6
  120. package/node_modules/pi-lens/dist/index.js +729 -344
  121. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +14 -2
  122. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +84 -17
  123. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +32 -5
  124. package/node_modules/pi-lens/package.json +1 -1
  125. package/node_modules/pi-typesafe/LICENSE +21 -0
  126. package/node_modules/pi-typesafe/README.md +153 -0
  127. package/node_modules/pi-typesafe/dist/ask.d.ts +32 -0
  128. package/node_modules/pi-typesafe/dist/ask.js +25 -0
  129. package/node_modules/pi-typesafe/dist/auth.d.ts +56 -0
  130. package/node_modules/pi-typesafe/dist/auth.js +114 -0
  131. package/node_modules/pi-typesafe/dist/batch.d.ts +74 -0
  132. package/node_modules/pi-typesafe/dist/batch.js +116 -0
  133. package/node_modules/pi-typesafe/dist/calibrate.d.ts +106 -0
  134. package/node_modules/pi-typesafe/dist/calibrate.js +157 -0
  135. package/node_modules/pi-typesafe/dist/client.d.ts +69 -0
  136. package/node_modules/pi-typesafe/dist/client.js +188 -0
  137. package/node_modules/pi-typesafe/dist/credentials.d.ts +46 -0
  138. package/node_modules/pi-typesafe/dist/credentials.js +112 -0
  139. package/node_modules/pi-typesafe/dist/errors.d.ts +9 -0
  140. package/node_modules/pi-typesafe/dist/errors.js +31 -0
  141. package/node_modules/pi-typesafe/dist/extension.d.ts +3 -0
  142. package/node_modules/pi-typesafe/dist/extension.js +222 -0
  143. package/node_modules/pi-typesafe/dist/index.d.ts +18 -0
  144. package/node_modules/pi-typesafe/dist/index.js +9 -0
  145. package/node_modules/pi-typesafe/dist/key-prompt.d.ts +3 -0
  146. package/node_modules/pi-typesafe/dist/key-prompt.js +43 -0
  147. package/node_modules/pi-typesafe/dist/login.d.ts +27 -0
  148. package/node_modules/pi-typesafe/dist/login.js +38 -0
  149. package/node_modules/pi-typesafe/dist/schema.d.ts +43 -0
  150. package/node_modules/pi-typesafe/dist/schema.js +152 -0
  151. package/node_modules/pi-typesafe/dist/ui.d.ts +3 -0
  152. package/node_modules/pi-typesafe/dist/ui.js +3 -0
  153. package/node_modules/pi-typesafe/dist/usage.d.ts +73 -0
  154. package/node_modules/pi-typesafe/dist/usage.js +182 -0
  155. package/node_modules/pi-typesafe/examples/decision-extension.ts +41 -0
  156. package/node_modules/pi-typesafe/extensions/index.js +2 -0
  157. package/node_modules/pi-typesafe/package.json +89 -0
  158. package/package.json +16 -8
@@ -0,0 +1,188 @@
1
+ import { TypeSafeClient } from "@typesafe-ai/sdk";
2
+ import { recordAuthFailure, recordAuthVerified } from "./auth.js";
3
+ import { evaluateAll, evaluateMany } from "./batch.js";
4
+ import { keySituation } from "./credentials.js";
5
+ import { TypeSafeIntegrationError, safeError } from "./errors.js";
6
+ import { DEFAULT_MAX_INPUT_BYTES, assertWithinByteLimit, prepareEvaluationRequest } from "./schema.js";
7
+ import { DEFAULT_USD_PER_MTOK, capsFromEnvironment, estimateUsd, mergeCaps, openUsageLedger } from "./usage.js";
8
+ /** Default attempts per client instance; the extension quotes the same number in its consent copy. */
9
+ export const DEFAULT_MAX_REQUESTS = 20;
10
+ function positiveInteger(value, label) {
11
+ if (!Number.isSafeInteger(value) || value <= 0) {
12
+ throw new TypeSafeIntegrationError("configuration", `${label} must be a positive safe integer.`);
13
+ }
14
+ return value;
15
+ }
16
+ function positiveNumber(value, label) {
17
+ if (!Number.isFinite(value) || value <= 0) {
18
+ throw new TypeSafeIntegrationError("configuration", `${label} must be a positive number.`);
19
+ }
20
+ return value;
21
+ }
22
+ function validResult(result, questions) {
23
+ const probability = (value) => Number.isFinite(value) && value >= 0 && value <= 1;
24
+ if (!result || typeof result.model !== "string" || !result.model || !result.usage || !result.answers)
25
+ return false;
26
+ for (const count of [result.usage.input_tokens, result.usage.output_tokens]) {
27
+ if (!Number.isSafeInteger(count) || count < 0)
28
+ return false;
29
+ }
30
+ if (Object.keys(result.answers).length !== Object.keys(questions).length)
31
+ return false;
32
+ for (const [id, question] of Object.entries(questions)) {
33
+ const answer = result.answers[id];
34
+ if (!answer || answer.type !== question.type)
35
+ return false;
36
+ if (answer.type === "noul") {
37
+ if (!probability(answer.noul))
38
+ return false;
39
+ continue;
40
+ }
41
+ if (!probability(answer.confidence) || !answer.probabilities)
42
+ return false;
43
+ const keys = question.type === "choice" ? Object.keys(question.criteria)
44
+ : question.type === "score" ? question.criteria.map((_, index) => String(index)) : [];
45
+ const probabilities = new Map(Object.entries(answer.probabilities));
46
+ if (probabilities.size !== keys.length || keys.some(key => !probability(probabilities.get(key) ?? NaN)))
47
+ return false;
48
+ if (answer.type === "choice" && !keys.includes(answer.choice))
49
+ return false;
50
+ if (answer.type === "score" && (!Number.isFinite(answer.score) || answer.score < 0 || answer.score > keys.length - 1 || !answer.legend))
51
+ return false;
52
+ }
53
+ return true;
54
+ }
55
+ const CAP_LABELS = {
56
+ requestsPerDay: "daily request cap",
57
+ inputTokensPerDay: "daily input-token cap",
58
+ usdPerDay: "daily spend cap",
59
+ };
60
+ function capsDescription(caps) {
61
+ const parts = [
62
+ caps.maxRequests === undefined ? undefined : `${caps.maxRequests} per session`,
63
+ caps.maxRequestsPerDay === undefined ? undefined : `${caps.maxRequestsPerDay} requests per day`,
64
+ caps.maxInputTokensPerDay === undefined ? undefined : `${caps.maxInputTokensPerDay} input tokens per day`,
65
+ caps.maxUsdPerDay === undefined ? undefined : `$${caps.maxUsdPerDay} per day`,
66
+ ].filter((part) => part !== undefined);
67
+ return parts.length ? `Caps: ${parts.join(", ")}.` : "No request or spend caps are set.";
68
+ }
69
+ /** A bounded, server-side TypeSafe client independent of Pi's runtime. */
70
+ export function createTypeSafe(options = {}) {
71
+ let apiKey = options.apiKey?.trim();
72
+ if (!apiKey) {
73
+ const situation = keySituation();
74
+ if (situation.kind === "unusable")
75
+ throw new TypeSafeIntegrationError("configuration", situation.reason);
76
+ if (situation.kind === "environment" || situation.kind === "stored")
77
+ apiKey = situation.key;
78
+ }
79
+ if (!apiKey)
80
+ throw new TypeSafeIntegrationError("configuration", "No TypeSafe API key. Run /typesafe login in Pi, or set TYPESAFE_API_KEY in the environment.");
81
+ const timeout = positiveInteger(options.timeoutMs ?? 15_000, "timeoutMs");
82
+ const maxInputBytes = positiveInteger(options.maxInputBytes ?? DEFAULT_MAX_INPUT_BYTES, "maxInputBytes");
83
+ const maxRequests = positiveInteger(options.maxRequests ?? DEFAULT_MAX_REQUESTS, "maxRequests");
84
+ const usdPerMTok = positiveNumber(options.usdPerMTok ?? DEFAULT_USD_PER_MTOK, "usdPerMTok");
85
+ const caps = mergeCaps({
86
+ maxRequests,
87
+ ...(options.maxRequestsPerDay === undefined ? {} : { maxRequestsPerDay: positiveInteger(options.maxRequestsPerDay, "maxRequestsPerDay") }),
88
+ ...(options.maxInputTokensPerDay === undefined ? {} : { maxInputTokensPerDay: positiveInteger(options.maxInputTokensPerDay, "maxInputTokensPerDay") }),
89
+ ...(options.maxUsdPerDay === undefined ? {} : { maxUsdPerDay: positiveNumber(options.maxUsdPerDay, "maxUsdPerDay") }),
90
+ }, capsFromEnvironment());
91
+ const model = options.model ?? "jev-latest";
92
+ if (typeof model !== "string" || !model.trim() || model.length > 100)
93
+ throw new TypeSafeIntegrationError("configuration", "model must be a nonempty string of at most 100 characters.");
94
+ // Do not inherit SDK debug logging or alternate destinations from the environment.
95
+ const client = new TypeSafeClient({
96
+ apiKey,
97
+ defaultModel: model,
98
+ baseURL: "https://api.typesafe.ai",
99
+ timeout,
100
+ retry: { maxRetries: 0 },
101
+ logLevel: "off",
102
+ ...(options.fetch ? { fetch: options.fetch } : {}),
103
+ });
104
+ const ledger = options.ledger ?? openUsageLedger({ usdPerMTok });
105
+ const usage = { requestsStarted: 0, requestsSucceeded: 0, requestsFailed: 0, inputTokens: 0, outputTokens: 0 };
106
+ // Auth state writes are deduplicated: one verification record per process, one record per distinct failure.
107
+ let verificationRecorded = false;
108
+ let lastFailureRecorded;
109
+ const snapshot = () => ({ ...usage, estimatedUsd: estimateUsd(usage.inputTokens, usdPerMTok) });
110
+ const blocked = () => (usage.requestsStarted >= maxRequests ? undefined : ledger.blocked(caps));
111
+ const typesafe = {
112
+ getUsage: snapshot,
113
+ getSpend: () => {
114
+ const reached = blocked();
115
+ return {
116
+ session: snapshot(),
117
+ today: ledger.today(),
118
+ caps,
119
+ usdPerMTok,
120
+ ...(reached === undefined ? {} : { blocked: reached }),
121
+ };
122
+ },
123
+ async listModels(callOptions = {}) {
124
+ try {
125
+ const models = await client.models.list(callOptions);
126
+ if (!Array.isArray(models))
127
+ throw new TypeSafeIntegrationError("response", "TypeSafe returned an unexpected model list.");
128
+ if (!verificationRecorded) {
129
+ verificationRecorded = true;
130
+ recordAuthVerified();
131
+ }
132
+ return models.map(card => card?.name).filter((name) => typeof name === "string" && name.length > 0 && name.length <= 100);
133
+ }
134
+ catch (error) {
135
+ throw safeError(error);
136
+ }
137
+ },
138
+ async evaluate(input, callOptions = {}) {
139
+ const validated = prepareEvaluationRequest(input, { maxInputBytes });
140
+ const body = JSON.stringify({ ...validated, model: validated.model ?? model });
141
+ assertWithinByteLimit(body, maxInputBytes);
142
+ if (callOptions.signal?.aborted)
143
+ throw new TypeSafeIntegrationError("aborted", "TypeSafe request cancelled before submission.");
144
+ if (usage.requestsStarted >= maxRequests) {
145
+ throw new TypeSafeIntegrationError("budget", `TypeSafe request limit reached (${maxRequests} attempts per client instance). ${capsDescription(caps)}`);
146
+ }
147
+ const reached = ledger.blocked(caps);
148
+ if (reached) {
149
+ // Name the cap, the used amount, and the day, so a long run stops loudly instead of burning tokens unnoticed.
150
+ throw new TypeSafeIntegrationError("budget", `TypeSafe ${CAP_LABELS[reached.cap]} reached (${reached.used} of ${reached.limit} on ${reached.day}); no request was submitted. Requests resume after the local day rolls over, or raise the cap deliberately.`);
151
+ }
152
+ // Snapshot before awaiting so later mutations cannot change the request or validation.
153
+ const request = JSON.parse(body);
154
+ usage.requestsStarted += 1;
155
+ ledger.recordStart();
156
+ const start = performance.now();
157
+ try {
158
+ const result = await client.systemOne(request, callOptions);
159
+ if (!validResult(result, request.questions))
160
+ throw new TypeSafeIntegrationError("response", "TypeSafe returned an unexpected answer or usage format.");
161
+ usage.requestsSucceeded += 1;
162
+ usage.inputTokens += result.usage.input_tokens;
163
+ usage.outputTokens += result.usage.output_tokens;
164
+ ledger.recordSuccess(result.usage.input_tokens, result.usage.output_tokens);
165
+ if (!verificationRecorded) {
166
+ verificationRecorded = true;
167
+ recordAuthVerified();
168
+ }
169
+ return { ...result, elapsedMs: Math.round(performance.now() - start) };
170
+ }
171
+ catch (error) {
172
+ const safe = safeError(error);
173
+ // The request was submitted, so it counts even when it fails; the reason stays visible in `authState()`.
174
+ usage.requestsFailed += 1;
175
+ ledger.recordFailure();
176
+ const fingerprint = `${safe.code}:${safe.status ?? ""}:${safe.message}`;
177
+ if (lastFailureRecorded !== fingerprint) {
178
+ lastFailureRecorded = fingerprint;
179
+ recordAuthFailure(safe);
180
+ }
181
+ throw safe;
182
+ }
183
+ },
184
+ evaluateMany: (requests, batchOptions = {}) => evaluateMany(typesafe, requests, batchOptions),
185
+ evaluateAll: (request, batchOptions = {}) => evaluateAll(typesafe, request, batchOptions),
186
+ };
187
+ return typesafe;
188
+ }
@@ -0,0 +1,46 @@
1
+ export type KeySource = "environment" | "stored";
2
+ /** The complete, never-throwing answer to "which key is in effect". */
3
+ export type KeySituation = {
4
+ readonly kind: "environment";
5
+ readonly key: string;
6
+ } | {
7
+ readonly kind: "stored";
8
+ readonly key: string;
9
+ readonly path: string;
10
+ } | {
11
+ readonly kind: "missing";
12
+ } | {
13
+ readonly kind: "unusable";
14
+ readonly path: string;
15
+ readonly reason: string;
16
+ };
17
+ /** Mirrors Pi's agent directory rule; every pi-typesafe file lives in this one directory. */
18
+ export declare function piTypesafeDir(): string;
19
+ /** Where the key lives, next to Pi's own auth.json. */
20
+ export declare function credentialsPath(): string;
21
+ /** Accepts the key only when it is a plausible token; never logs or echoes the value. */
22
+ export declare function normalizeApiKey(value: unknown): string;
23
+ /**
24
+ * The stored key, or `undefined` when the file is missing, unreadable, or holds no usable value. Throws
25
+ * `configuration` when the file is readable by other users; keySituation() reports that case as `unusable` instead.
26
+ */
27
+ export declare function readStoredApiKey(): string | undefined;
28
+ /**
29
+ * What the environment, the login store, and file permissions add up to right now. Never throws; the "unusable" kind
30
+ * carries the user-facing reason (a stored key that other local users can read).
31
+ */
32
+ export declare function keySituation(): KeySituation;
33
+ /** Short phrase naming the source; full sentences stay with the caller. */
34
+ export declare function keySourceLabel(situation: KeySituation): string;
35
+ /**
36
+ * The pre-0.4.0 key interface, frozen for existing callers: environment first so CI and scripts stay explicit, the
37
+ * stored key as the interactive default, `undefined` when no key is configured, and a `configuration` error when a
38
+ * store must not be read. New code should call keySituation() instead: same precedence, never throws, and the
39
+ * "must not be read" case arrives as `unusable` with the reason.
40
+ */
41
+ export declare function resolveApiKey(): {
42
+ key: string;
43
+ source: KeySource;
44
+ } | undefined;
45
+ export declare function storeApiKey(value: unknown): string;
46
+ export declare function clearStoredApiKey(): boolean;
@@ -0,0 +1,112 @@
1
+ import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { TypeSafeIntegrationError } from "./errors.js";
5
+ /** Mirrors Pi's agent directory rule; every pi-typesafe file lives in this one directory. */
6
+ export function piTypesafeDir() {
7
+ const configured = process.env.PI_CODING_AGENT_DIR?.trim();
8
+ const agentDir = configured
9
+ ? (configured === "~" || configured.startsWith("~/") ? join(homedir(), configured.slice(1)) : configured)
10
+ : join(homedir(), ".pi", "agent");
11
+ return join(agentDir, "pi-typesafe");
12
+ }
13
+ /** Where the key lives, next to Pi's own auth.json. */
14
+ export function credentialsPath() {
15
+ return join(piTypesafeDir(), "auth.json");
16
+ }
17
+ /** Accepts the key only when it is a plausible token; never logs or echoes the value. */
18
+ export function normalizeApiKey(value) {
19
+ const key = typeof value === "string" ? value.trim() : "";
20
+ if (key.length < 16 || key.length > 512 || /\s/.test(key) || /[^\x21-\x7e]/.test(key)) {
21
+ throw new TypeSafeIntegrationError("validation", "That does not look like a TypeSafe API key. Copy the complete key from console.typesafe.ai and try again; nothing was saved.");
22
+ }
23
+ return key;
24
+ }
25
+ /**
26
+ * The stored key, or `undefined` when the file is missing, unreadable, or holds no usable value. Throws
27
+ * `configuration` when the file is readable by other users; keySituation() reports that case as `unusable` instead.
28
+ */
29
+ export function readStoredApiKey() {
30
+ const path = credentialsPath();
31
+ try {
32
+ if (process.platform !== "win32" && (statSync(path).mode & 0o077) !== 0) {
33
+ // Refuse to use a key other local users can read; the user must fix permissions or log in again.
34
+ throw new TypeSafeIntegrationError("configuration", `Refusing to read ${path}: it is readable by other users. Run chmod 600 on it, or run /typesafe logout and /typesafe login.`);
35
+ }
36
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
37
+ const key = parsed && typeof parsed === "object" ? parsed.apiKey : undefined;
38
+ return typeof key === "string" && key.trim() ? key.trim() : undefined;
39
+ }
40
+ catch (error) {
41
+ if (error instanceof TypeSafeIntegrationError)
42
+ throw error;
43
+ return undefined;
44
+ }
45
+ }
46
+ /**
47
+ * What the environment, the login store, and file permissions add up to right now. Never throws; the "unusable" kind
48
+ * carries the user-facing reason (a stored key that other local users can read).
49
+ */
50
+ export function keySituation() {
51
+ const fromEnvironment = process.env.TYPESAFE_API_KEY?.trim();
52
+ if (fromEnvironment)
53
+ return { kind: "environment", key: fromEnvironment };
54
+ const path = credentialsPath();
55
+ try {
56
+ const key = readStoredApiKey();
57
+ return key ? { kind: "stored", key, path } : { kind: "missing" };
58
+ }
59
+ catch (error) {
60
+ // readStoredApiKey only throws for a store this process must not use; report it instead of failing the caller.
61
+ if (error instanceof TypeSafeIntegrationError && error.code === "configuration")
62
+ return { kind: "unusable", path, reason: error.message };
63
+ throw error;
64
+ }
65
+ }
66
+ /** Short phrase naming the source; full sentences stay with the caller. */
67
+ export function keySourceLabel(situation) {
68
+ switch (situation.kind) {
69
+ case "environment": return "TYPESAFE_API_KEY";
70
+ case "stored": return "/typesafe login";
71
+ case "missing": return "no key";
72
+ case "unusable": return "unusable key";
73
+ }
74
+ }
75
+ /**
76
+ * The pre-0.4.0 key interface, frozen for existing callers: environment first so CI and scripts stay explicit, the
77
+ * stored key as the interactive default, `undefined` when no key is configured, and a `configuration` error when a
78
+ * store must not be read. New code should call keySituation() instead: same precedence, never throws, and the
79
+ * "must not be read" case arrives as `unusable` with the reason.
80
+ */
81
+ export function resolveApiKey() {
82
+ const situation = keySituation();
83
+ if (situation.kind === "unusable")
84
+ throw new TypeSafeIntegrationError("configuration", situation.reason);
85
+ return situation.kind === "environment" || situation.kind === "stored" ? { key: situation.key, source: situation.kind } : undefined;
86
+ }
87
+ export function storeApiKey(value) {
88
+ const key = normalizeApiKey(value);
89
+ const path = credentialsPath();
90
+ try {
91
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
92
+ const temporary = `${path}.${process.pid}.tmp`;
93
+ writeFileSync(temporary, `${JSON.stringify({ apiKey: key }, null, 2)}\n`, { mode: 0o600, flag: "w" });
94
+ chmodSync(temporary, 0o600);
95
+ renameSync(temporary, path);
96
+ }
97
+ catch {
98
+ throw new TypeSafeIntegrationError("configuration", `Could not write ${path}. Check directory permissions, or set TYPESAFE_API_KEY in the environment instead.`);
99
+ }
100
+ return path;
101
+ }
102
+ export function clearStoredApiKey() {
103
+ const path = credentialsPath();
104
+ try {
105
+ statSync(path);
106
+ }
107
+ catch {
108
+ return false;
109
+ }
110
+ rmSync(path, { force: true });
111
+ return true;
112
+ }
@@ -0,0 +1,9 @@
1
+ export type IntegrationErrorCode = "configuration" | "validation" | "budget" | "aborted" | "timeout" | "http" | "connection" | "response";
2
+ /** Safe to display: never contains upstream bodies, headers, keys, or submitted state. */
3
+ export declare class TypeSafeIntegrationError extends Error {
4
+ readonly code: IntegrationErrorCode;
5
+ readonly status?: number | undefined;
6
+ readonly name = "TypeSafeIntegrationError";
7
+ constructor(code: IntegrationErrorCode, message: string, status?: number | undefined);
8
+ }
9
+ export declare function safeError(error: unknown): TypeSafeIntegrationError;
@@ -0,0 +1,31 @@
1
+ import { APIError, APIConnectionError, APITimeoutError, APIUserAbortError } from "@typesafe-ai/sdk";
2
+ /** Safe to display: never contains upstream bodies, headers, keys, or submitted state. */
3
+ export class TypeSafeIntegrationError extends Error {
4
+ code;
5
+ status;
6
+ name = "TypeSafeIntegrationError";
7
+ constructor(code, message, status) {
8
+ super(message);
9
+ this.code = code;
10
+ this.status = status;
11
+ }
12
+ }
13
+ export function safeError(error) {
14
+ if (error instanceof TypeSafeIntegrationError)
15
+ return error;
16
+ if (error instanceof APIUserAbortError)
17
+ return new TypeSafeIntegrationError("aborted", "TypeSafe request cancelled; an already submitted request may still be billed.");
18
+ if (error instanceof APITimeoutError)
19
+ return new TypeSafeIntegrationError("timeout", "TypeSafe request timed out; it was not retried and may still be billed.");
20
+ if (error instanceof APIError) {
21
+ const advice = error.status === 401 ? "Check TYPESAFE_API_KEY."
22
+ : error.status === 403 ? "Check your account access and model permissions."
23
+ : error.status === 429 ? "Check your account quota and try again later."
24
+ : error.status === 400 || error.status === 422 ? "Check the question format and model limits."
25
+ : "Try again later or check the service status.";
26
+ return new TypeSafeIntegrationError("http", `TypeSafe returned HTTP ${error.status}. ${advice} No automatic retry was made.`, error.status);
27
+ }
28
+ if (error instanceof APIConnectionError)
29
+ return new TypeSafeIntegrationError("connection", "Could not complete the TypeSafe connection. No automatic retry was made.");
30
+ return new TypeSafeIntegrationError("response", "TypeSafe returned an unreadable or unexpected response.");
31
+ }
@@ -0,0 +1,3 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ /** Native Pi registration; importing the root library does not load this module. */
3
+ export default function typesafeExtension(pi: ExtensionAPI): void;
@@ -0,0 +1,222 @@
1
+ import { Text } from "@earendil-works/pi-tui";
2
+ import { createTypeSafe, DEFAULT_MAX_REQUESTS } from "./client.js";
3
+ import { authState, clearAuthState, describeAuth } from "./auth.js";
4
+ import { clearStoredApiKey, credentialsPath, keySituation, keySourceLabel } from "./credentials.js";
5
+ import { TypeSafeIntegrationError, safeError } from "./errors.js";
6
+ import { loginWithPrompt } from "./login.js";
7
+ import { DEFAULT_MAX_INPUT_BYTES, evaluationSchema, normalizeEvaluationRequest, prepareEvaluationRequest } from "./schema.js";
8
+ const disclosure = "Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.";
9
+ const sample = {
10
+ state: { message: "I was charged twice for my subscription. Please help today." },
11
+ questions: {
12
+ category: { type: "choice", instructions: "Which team should handle this message?", criteria: { billing: "Charges and payments", technical: "Software failures", other: "None of these" } },
13
+ urgent: { type: "noul", instructions: "Does the sender request help today?" },
14
+ frustration: { type: "score", instructions: "How frustrated does the sender sound?", criteria: ["A neutral request without expressed frustration", "Expressed frustration while remaining civil", "Explicit anger or threats"] },
15
+ },
16
+ };
17
+ function format(result, expanded = false) {
18
+ const lines = [`TypeSafe · ${JSON.stringify(result.model)} · ${result.elapsedMs} ms`];
19
+ for (const [id, answer] of Object.entries(result.answers)) {
20
+ const label = JSON.stringify(id);
21
+ if (answer.type === "noul")
22
+ lines.push(`${label}: P(yes) = ${answer.noul.toFixed(3)}`);
23
+ else if (answer.type === "choice")
24
+ lines.push(`${label}: ${JSON.stringify(answer.choice)} · confidence ${answer.confidence.toFixed(3)}`);
25
+ else
26
+ lines.push(`${label}: ${answer.score.toFixed(3)} · confidence ${answer.confidence.toFixed(3)}`);
27
+ if (expanded && answer.type !== "noul")
28
+ lines.push(` ${JSON.stringify(answer.probabilities)}`);
29
+ }
30
+ lines.push(`${result.usage.input_tokens} input / ${result.usage.output_tokens} output tokens`);
31
+ lines.push("Confidence is distribution concentration, not proof of correctness.");
32
+ return lines.join("\n");
33
+ }
34
+ /** Native Pi registration; importing the root library does not load this module. */
35
+ export default function typesafeExtension(pi) {
36
+ let enabled = process.env.PI_TYPESAFE_ENABLED === "1";
37
+ let client;
38
+ // One callout per distinct degradation per session: a long run must not bury the reason in repeated notices.
39
+ let calledOut;
40
+ const getClient = () => client ??= createTypeSafe();
41
+ const callOut = (ctx, key, text) => {
42
+ if (calledOut === key)
43
+ return;
44
+ calledOut = key;
45
+ try {
46
+ if (ctx?.hasUI)
47
+ ctx.ui.notify(text, "warning");
48
+ else
49
+ pi.sendMessage({ customType: "typesafe-status", content: text, display: true });
50
+ }
51
+ catch {
52
+ // Reporting must never replace the failure it describes, and a headless run may have no message channel.
53
+ }
54
+ };
55
+ pi.on("session_start", async (_event, ctx) => {
56
+ enabled = process.env.PI_TYPESAFE_ENABLED === "1";
57
+ client = undefined;
58
+ calledOut = undefined;
59
+ // An enabled extension with no usable key used to look exactly like a working one. Say it at startup; an
60
+ // unverified-but-present key stays quiet, because the first request is what proves it.
61
+ const auth = describeAuth(authState());
62
+ if (enabled && auth.level === "error")
63
+ callOut(ctx, `start:${auth.level}`, `TypeSafe is enabled but judgments are skipped. ${auth.text}`);
64
+ });
65
+ pi.registerEntryRenderer("typesafe-result", (entry, { expanded }) => new Text(entry.data ? format(entry.data, expanded) : "TypeSafe · no result", 0, 0));
66
+ pi.registerTool({
67
+ name: "typesafe_evaluate",
68
+ label: "TypeSafe",
69
+ description: `Evaluate supplied state with independent Choice, Score, and Noul questions in one TypeSafe request. Each question judges the whole state, so when several items are involved, put each item in a named state field (e.g. \`reports.r1\`) and ask one question per item per dimension (e.g. \`r1_owner\`, \`r2_owner\`), naming the field in the instructions; never aggregate several items into one question. ${disclosure} Requires operator opt-in via /typesafe enable or PI_TYPESAFE_ENABLED=1. Limit: 32 questions, ${DEFAULT_MAX_INPUT_BYTES / 1024} KiB JSON, ${DEFAULT_MAX_REQUESTS} attempts per session; no retries.`,
70
+ promptSnippet: "Ask batched structured questions with TypeSafe (external service; operator opt-in required)",
71
+ promptGuidelines: [
72
+ "Use typesafe_evaluate only for requested semantic judgments, not calculations or exact lookups; send only the relevant permitted data.",
73
+ "Batch independent typesafe_evaluate questions over the same state; use code or explicit permission rules for actions, never confidence as authorization.",
74
+ "When typesafe_evaluate judges several items, give each item a named state field and ask one question per item per dimension, naming the field in the instructions; one question over many items returns an unusable blend.",
75
+ "Report typesafe_evaluate answers as the model's judgments with their probabilities; do not replace them with your own guesses, and say when an answer is uncertain.",
76
+ ],
77
+ parameters: evaluationSchema,
78
+ // Pi validates against `parameters` after this hook; the cast only names the schema's type.
79
+ prepareArguments: args => normalizeEvaluationRequest(args),
80
+ async execute(_id, params, signal, _onUpdate, ctx) {
81
+ if (!enabled)
82
+ throw new TypeSafeIntegrationError("configuration", "TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files.");
83
+ // The tool admits through the same rule as the library; evaluate() re-runs it idempotently.
84
+ const request = prepareEvaluationRequest(params);
85
+ try {
86
+ const result = await getClient().evaluate(request, signal ? { signal } : {});
87
+ return { content: [{ type: "text", text: JSON.stringify(result) }], details: result };
88
+ }
89
+ catch (error) {
90
+ const safe = safeError(error);
91
+ // Authentication degradation is louder than a single failed call: it means every later judgment is skipped.
92
+ const rejected = safe.code === "http" && (safe.status === 401 || safe.status === 403);
93
+ if (rejected || safe.code === "configuration") {
94
+ callOut(ctx, `run:${safe.code}:${safe.status ?? ""}`, `TypeSafe is not authenticated (${safe.message}) Judgments will fail until the key is fixed.`);
95
+ }
96
+ throw safe;
97
+ }
98
+ },
99
+ renderCall(args) {
100
+ return new Text(`TypeSafe · ${Object.keys(args.questions ?? {}).length} questions · external request`, 0, 0);
101
+ },
102
+ renderResult(result, { expanded, isPartial }) {
103
+ if (isPartial)
104
+ return new Text("TypeSafe · waiting for response", 0, 0);
105
+ if (!result.details?.answers)
106
+ return new Text(result.content.filter(part => part.type === "text").map(part => part.text).join("\n"), 0, 0);
107
+ return new Text(format(result.details, expanded), 0, 0);
108
+ },
109
+ });
110
+ const actions = ["login", "logout", "setup", "status", "enable", "disable", "test", "playground"];
111
+ pi.registerCommand("typesafe", {
112
+ description: "TypeSafe login, consent, usage, sample test, and JSON playground",
113
+ getArgumentCompletions(prefix) {
114
+ const matches = actions.filter(action => action.startsWith(prefix)).map(action => ({ value: action, label: action }));
115
+ return matches.length ? matches : null;
116
+ },
117
+ async handler(args, ctx) {
118
+ const action = args.trim() || "status";
119
+ const report = (text, level = "info") => {
120
+ if (ctx.hasUI)
121
+ ctx.ui.notify(text, level);
122
+ else
123
+ pi.sendMessage({ customType: "typesafe-status", content: text, display: true });
124
+ };
125
+ try {
126
+ if (action === "status") {
127
+ const spend = client?.getSpend();
128
+ const auth = describeAuth(authState());
129
+ const session = spend
130
+ ? `Session ${spend.session.requestsStarted}/${DEFAULT_MAX_REQUESTS} attempts, ${spend.session.requestsSucceeded} successful, ${spend.session.requestsFailed} failed, ${spend.session.inputTokens} input tokens (~$${spend.session.estimatedUsd.toFixed(4)}).`
131
+ : `Session 0/${DEFAULT_MAX_REQUESTS} attempts; no client yet in this session.`;
132
+ const today = spend
133
+ ? `Today ${spend.today.requestsStarted} requests (${spend.today.requestsSucceeded} ok, ${spend.today.requestsFailed} failed), ${spend.today.inputTokens} input tokens, ~$${spend.today.estimatedUsd.toFixed(4)}.`
134
+ : "";
135
+ const blocked = spend?.blocked ? ` Cap reached: ${spend.blocked.cap} ${spend.blocked.used}/${spend.blocked.limit} on ${spend.blocked.day}; no request will be submitted until the local day rolls over.` : "";
136
+ report(`TypeSafe: ${enabled ? "enabled" : "disabled"}. ${auth.text} ${session} ${today}${blocked} Model: jev-latest. Session limits reset on session start/reload; daily counters persist and caps come from client options or PI_TYPESAFE_MAX_* environment variables. ${disclosure}`, auth.level === "error" && enabled ? "warning" : "info");
137
+ return;
138
+ }
139
+ if (action === "logout") {
140
+ const removed = clearStoredApiKey();
141
+ clearAuthState();
142
+ client = undefined;
143
+ enabled = false;
144
+ report(removed ? `Removed the stored key at ${credentialsPath()}. TypeSafe is disabled.` : "No stored key to remove." + (process.env.TYPESAFE_API_KEY?.trim() ? " TYPESAFE_API_KEY is still set in the environment." : ""));
145
+ return;
146
+ }
147
+ if (action === "disable") {
148
+ enabled = false;
149
+ report("TypeSafe disabled for future agent calls. In-flight requests are not cancelled.");
150
+ return;
151
+ }
152
+ if (!actions.includes(action)) {
153
+ report(`Usage: /typesafe ${actions.join(" | ")}`, "warning");
154
+ return;
155
+ }
156
+ if (!ctx.hasUI) {
157
+ report("This command needs interactive Pi. For headless tool use, explicitly set PI_TYPESAFE_ENABLED=1 and TYPESAFE_API_KEY before launching Pi.", "warning");
158
+ return;
159
+ }
160
+ const situation = keySituation();
161
+ if (action === "login" || (action === "setup" && situation.kind === "missing")) {
162
+ if (process.env.TYPESAFE_API_KEY?.trim()) {
163
+ report("TYPESAFE_API_KEY is set in the environment and takes precedence over a stored key. Unset it before using /typesafe login.", "warning");
164
+ return;
165
+ }
166
+ const login = await loginWithPrompt(ctx);
167
+ if (login === undefined) {
168
+ report("Login cancelled; nothing was saved.");
169
+ return;
170
+ }
171
+ client = undefined;
172
+ report(`Key verified (${login.models} model${login.models === 1 ? "" : "s"} available) and saved to ${login.path} with owner-only permissions. Run /typesafe enable to allow agent tool calls.`);
173
+ return;
174
+ }
175
+ if (action === "setup") {
176
+ const current = situation.kind === "environment" || situation.kind === "stored" ? `configured via ${keySourceLabel(situation)}`
177
+ : situation.kind === "unusable" ? `unusable — ${situation.reason}`
178
+ : "missing";
179
+ report(`Key ${current}. Run /typesafe test for one sample request or /typesafe enable to allow agent tool calls.`);
180
+ return;
181
+ }
182
+ if (action === "enable") {
183
+ if (situation.kind === "missing") {
184
+ report("Run /typesafe login first: no API key is configured.", "warning");
185
+ return;
186
+ }
187
+ if (situation.kind === "unusable") {
188
+ report(`The stored key cannot be used. ${situation.reason}`, "warning");
189
+ return;
190
+ }
191
+ if (await ctx.ui.confirm("Enable TypeSafe for this session?", disclosure)) {
192
+ enabled = true;
193
+ report(`TypeSafe enabled. Up to ${DEFAULT_MAX_REQUESTS} attempts in this session; /typesafe disable stops future agent calls.`);
194
+ }
195
+ return;
196
+ }
197
+ let request = sample;
198
+ if (action === "playground") {
199
+ const text = await ctx.ui.editor("TypeSafe request JSON · edit state and questions", JSON.stringify(sample, null, 2));
200
+ if (text === undefined)
201
+ return;
202
+ try {
203
+ request = JSON.parse(text);
204
+ }
205
+ catch {
206
+ report("Invalid JSON. Keep quoted strings on one line; nothing was sent.", "error");
207
+ return;
208
+ }
209
+ }
210
+ const validated = prepareEvaluationRequest(request);
211
+ if (!await ctx.ui.confirm("Send this TypeSafe request?", disclosure))
212
+ return;
213
+ const result = await getClient().evaluate(validated);
214
+ // Playground results stay out of LLM context; the agent tool returns its own results normally.
215
+ pi.appendEntry("typesafe-result", result);
216
+ }
217
+ catch (error) {
218
+ report(safeError(error).message, "error");
219
+ }
220
+ },
221
+ });
222
+ }
@@ -0,0 +1,18 @@
1
+ export { createTypeSafe, DEFAULT_MAX_REQUESTS } from "./client.js";
2
+ export type { TypeSafe, TypeSafeOptions, EvaluationOptions, Evaluation, UsageSnapshot, SpendReport, } from "./client.js";
3
+ export { ask, DEFAULT_ASK_TIMEOUT_MS } from "./ask.js";
4
+ export type { AskAnswer, AskOptions, Judge } from "./ask.js";
5
+ export { fanOut, DEFAULT_CONCURRENCY, evaluateMany, evaluateAll, chunkEvaluationRequest } from "./batch.js";
6
+ export type { BatchEvaluation, BatchOptions, FanOutOptions, Settled } from "./batch.js";
7
+ export { authState, authStatePath, clearAuthState, describeAuth, recordAuthFailure, recordAuthVerified } from "./auth.js";
8
+ export type { AuthFailure, AuthReport, AuthState } from "./auth.js";
9
+ export { clearStoredApiKey, credentialsPath, keySituation, keySourceLabel, normalizeApiKey, piTypesafeDir, resolveApiKey, storeApiKey, } from "./credentials.js";
10
+ export type { KeySituation, KeySource } from "./credentials.js";
11
+ export { capsFromEnvironment, DEFAULT_USD_PER_MTOK, emptyTotals, estimateUsd, localDay, mergeCaps, openUsageLedger, usagePath, } from "./usage.js";
12
+ export type { BlockedCap, SpendCaps, UsageLedger, UsageLedgerOptions, UsageReport, UsageTotals } from "./usage.js";
13
+ export { TypeSafeIntegrationError } from "./errors.js";
14
+ export type { IntegrationErrorCode } from "./errors.js";
15
+ export { DEFAULT_MAX_INPUT_BYTES, DEFAULT_MAX_QUESTIONS, evaluationSchema, normalizeEvaluationRequest, parseEvaluationRequest, prepareEvaluationRequest, } from "./schema.js";
16
+ export type { PrepareEvaluationOptions } from "./schema.js";
17
+ export { choice, noul, score } from "@typesafe-ai/sdk";
18
+ export type { Questions, Question, SystemOneRequest, SystemOneResult, EntryType, JsonValue, ChoiceQuestion, ChoiceResponse, NoulQuestion, NoulResponse, ScoreQuestion, ScoreResponse, Usage, } from "@typesafe-ai/sdk";
@@ -0,0 +1,9 @@
1
+ export { createTypeSafe, DEFAULT_MAX_REQUESTS } from "./client.js";
2
+ export { ask, DEFAULT_ASK_TIMEOUT_MS } from "./ask.js";
3
+ export { fanOut, DEFAULT_CONCURRENCY, evaluateMany, evaluateAll, chunkEvaluationRequest } from "./batch.js";
4
+ export { authState, authStatePath, clearAuthState, describeAuth, recordAuthFailure, recordAuthVerified } from "./auth.js";
5
+ export { clearStoredApiKey, credentialsPath, keySituation, keySourceLabel, normalizeApiKey, piTypesafeDir, resolveApiKey, storeApiKey, } from "./credentials.js";
6
+ export { capsFromEnvironment, DEFAULT_USD_PER_MTOK, emptyTotals, estimateUsd, localDay, mergeCaps, openUsageLedger, usagePath, } from "./usage.js";
7
+ export { TypeSafeIntegrationError } from "./errors.js";
8
+ export { DEFAULT_MAX_INPUT_BYTES, DEFAULT_MAX_QUESTIONS, evaluationSchema, normalizeEvaluationRequest, parseEvaluationRequest, prepareEvaluationRequest, } from "./schema.js";
9
+ export { choice, noul, score } from "@typesafe-ai/sdk";