@cortexkit/pi-anthropic-auth 1.23.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/convert.js CHANGED
@@ -1,5 +1,5 @@
1
- import { applyClaudeCodeMetadata, applyMidConversationOutputConfig, applyThinkingBindingControls, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING, CLAUDE_OPUS_5_5_ADAPTIVE_THINKING, CLAUDE_OPUS_5_ADAPTIVE_THINKING, CLAUDE_SONNET_5_ADAPTIVE_THINKING, ClaudeCodeFirstUserTextTracker, isClaudeFableOrMythos5Model, isClaudeOpus5Model, isClaudeOpus55Model, isClaudeSonnet5Model, isFastModeSupportedModel, isOpenAIReasoningSignature, orderClaudeCodeBody, signRequestBody, } from '@cortexkit/anthropic-auth-core';
2
- import { collapseSystemMessages, getCurrentSystemPrompt, getCurrentTools, normalizeContext, } from '@earendil-works/pi-ai';
1
+ import { applyClaudeCodeMetadata, applyMidConversationOutputConfig, applyThinkingBindingControls, assertNoMeaningfulTrailingAssistant, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING, CLAUDE_HAIKU_5_5_ADAPTIVE_THINKING, CLAUDE_OPUS_5_5_ADAPTIVE_THINKING, CLAUDE_OPUS_5_ADAPTIVE_THINKING, CLAUDE_SONNET_5_5_ADAPTIVE_THINKING, CLAUDE_SONNET_5_ADAPTIVE_THINKING, ClaudeCodeFirstUserTextTracker, isClaudeFableOrMythos5Model, isClaudeHaiku55Model, isClaudeOpus5Model, isClaudeOpus55Model, isClaudeSonnet5Model, isClaudeSonnet55Model, isFastModeSupportedModel, isOpenAIReasoningSignature, orderClaudeCodeBody, signRequestBody, stripEmptyTrailingAssistantMessages, } from '@cortexkit/anthropic-auth-core';
2
+ import { collapseSystemMessages, getCurrentSystemPrompt, getCurrentTools, normalizeContext, } from './transcript.js';
3
3
  // Anchor identifying Pi's documentation paragraph — the only part of the prompt
4
4
  // that Anthropic currently rejects in system[]. Unknown prompt shapes take the
5
5
  // service-preserving fallback below instead of returning the full prompt there.
@@ -433,9 +433,10 @@ function applyCacheMode(body, enabled, mode) {
433
433
  });
434
434
  }
435
435
  export async function buildAnthropicRequest(modelId, context, options, cache, fastModeEnabled = false, identity, controls = {}) {
436
- // Pi 0.86 passes a normalized transcript to providers. Resolve instructions,
437
- // named sections and tool changes through its own replay helpers; raw host
438
- // prompts (including ordered OMP blocks) still enter through normalization.
436
+ // Pi 0.86 passes a normalized transcript to providers, and later system
437
+ // messages can change the prompt, its named sections and the tool set. Resolve
438
+ // them through the local replay port; raw host prompts (including ordered OMP
439
+ // blocks) still enter through normalization.
439
440
  const transcript = collapseSystemMessages(normalizeContext({
440
441
  ...context,
441
442
  systemPrompt: systemPromptText(context.systemPrompt),
@@ -445,12 +446,12 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
445
446
  systemPrompt: getCurrentSystemPrompt(transcript.messages),
446
447
  tools: getCurrentTools(transcript.messages),
447
448
  };
449
+ // Conversion can discard opaque or incomplete blocks. Check context.messages
450
+ // first so those losses cannot hide a meaningful final assistant turn. Check
451
+ // the Anthropic messages again after conversion removes empty user messages.
452
+ assertNoMeaningfulTrailingAssistant(context.messages);
448
453
  const messages = convertMessages(context.messages, modelId);
449
- // Strip trailing assistant messages — Anthropic rejects prefill on some models
450
- while (messages.length &&
451
- messages[messages.length - 1]?.role === 'assistant') {
452
- messages.pop();
453
- }
454
+ stripEmptyTrailingAssistantMessages(messages);
454
455
  const system = [
455
456
  {
456
457
  type: 'text',
@@ -507,18 +508,23 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
507
508
  }
508
509
  const isFableOrMythos5 = isClaudeFableOrMythos5Model(modelId);
509
510
  const isSonnet5 = isClaudeSonnet5Model(modelId);
511
+ const isSonnet55 = isClaudeSonnet55Model(modelId);
512
+ const isHaiku55 = isClaudeHaiku55Model(modelId);
510
513
  const isOpus5 = isClaudeOpus5Model(modelId);
511
514
  const isOpus55 = isClaudeOpus55Model(modelId);
512
- // Sonnet 5, Opus 5, and Opus 5.5 share Fable/Mythos's adaptive-summarized contract: make
513
- // adaptive thinking visible (display defaults to "omitted") and map reasoning
514
- // to output_config effort. Pi's typed options cannot express
515
- // thinking-disabled, so there is no disable case here (see transform.ts for
516
- // the raw-body path). Each model refs its own per-family constant —
517
- // families can diverge, so the constant rather than a shared alias keeps
518
- // the call sites explicit.
515
+ // Request summaries so the host can display adaptive reasoning.
516
+ // Pi's typed options cannot express the disabled-thinking setting;
517
+ // OpenCode handles it by transforming the raw request body. Separate model
518
+ // constants let each model's thinking configuration change independently.
519
519
  if (isFableOrMythos5) {
520
520
  body.thinking = { ...CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING };
521
521
  }
522
+ else if (isHaiku55) {
523
+ body.thinking = { ...CLAUDE_HAIKU_5_5_ADAPTIVE_THINKING };
524
+ }
525
+ else if (isSonnet55) {
526
+ body.thinking = { ...CLAUDE_SONNET_5_5_ADAPTIVE_THINKING };
527
+ }
522
528
  else if (isSonnet5) {
523
529
  body.thinking = { ...CLAUDE_SONNET_5_ADAPTIVE_THINKING };
524
530
  }
@@ -529,7 +535,15 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
529
535
  body.thinking = { ...CLAUDE_OPUS_5_ADAPTIVE_THINKING };
530
536
  }
531
537
  if (options?.reasoning) {
532
- if (isFableOrMythos5 || isSonnet5 || isOpus5 || isOpus55) {
538
+ if ((isSonnet55 || isHaiku55) && options.reasoning === 'minimal') {
539
+ throw new Error(`${isHaiku55 ? 'Claude Haiku 5.5' : 'Claude Sonnet 5.5'} does not support minimal effort`);
540
+ }
541
+ if (isFableOrMythos5 ||
542
+ isSonnet5 ||
543
+ isSonnet55 ||
544
+ isHaiku55 ||
545
+ isOpus5 ||
546
+ isOpus55) {
533
547
  body.output_config = { effort: options.reasoning };
534
548
  }
535
549
  else {
@@ -557,5 +571,8 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
557
571
  }
558
572
  const unsigned = JSON.stringify(orderClaudeCodeBody(body));
559
573
  const bodyText = await signRequestBody(unsigned);
560
- return { body, bodyText };
574
+ // The stream decoder must use the exact tool set that produced this body.
575
+ // Pi 0.86's transcript has no top-level context.tools, and normalizing it
576
+ // again after dispatch would duplicate work and risk a different mapping.
577
+ return { body, bodyText, hostTools: context.tools ?? [] };
561
578
  }
package/dist/custody.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AccountCommandResult, type ClaustrumEnrollmentStatus, type ClaustrumMode, type ClaustrumScopedClient } from '@cortexkit/anthropic-auth-core';
1
+ import { type AccountCommandResult, type ClaustrumEnrollmentStatus, type ClaustrumMode } from '@cortexkit/anthropic-auth-core';
2
2
  export declare function requirePiEnrollment(): Promise<{
3
3
  state: 'approved';
4
4
  proposedName: string;
@@ -10,8 +10,4 @@ export interface PiCustodyCommands {
10
10
  status(): Promise<ClaustrumEnrollmentStatus>;
11
11
  reset(): Promise<AccountCommandResult>;
12
12
  }
13
- export declare function createPiCustodyCommands(options: {
14
- reconfigure: () => Promise<void>;
15
- storagePath?: string;
16
- connect?: () => Promise<ClaustrumScopedClient>;
17
- }): PiCustodyCommands;
13
+ export declare function createPiCustodyCommands(): PiCustodyCommands;
package/dist/custody.js CHANGED
@@ -1,5 +1,4 @@
1
- import { CLAUSTRUM_PI_ENROLLMENT_NAME, ClaustrumScopedCustody, connectClaustrumScopedClient, getHostClaustrumEnrollmentPaths, readClaustrumEnrollmentStatus, resetClaustrumEnrollmentState, setClaustrumModePersistent, } from '@cortexkit/anthropic-auth-core';
2
- import { getPiAccountStoragePath, getPiClaustrumConnectionOptions, hasPiLocalAnthropicOAuth, } from './paths.js';
1
+ import { CLAUSTRUM_PI_ENROLLMENT_NAME, getHostClaustrumEnrollmentPaths, readClaustrumEnrollmentStatus, resetClaustrumEnrollmentState, } from '@cortexkit/anthropic-auth-core';
3
2
  export async function requirePiEnrollment() {
4
3
  const status = await readClaustrumEnrollmentStatus(getHostClaustrumEnrollmentPaths('pi'), CLAUSTRUM_PI_ENROLLMENT_NAME);
5
4
  if (status.state !== 'approved' ||
@@ -8,8 +7,7 @@ export async function requirePiEnrollment() {
8
7
  }
9
8
  return status;
10
9
  }
11
- export function createPiCustodyCommands(options) {
12
- const storagePath = options.storagePath ?? getPiAccountStoragePath();
10
+ export function createPiCustodyCommands() {
13
11
  const paths = () => getHostClaustrumEnrollmentPaths('pi');
14
12
  return {
15
13
  status: () => readClaustrumEnrollmentStatus(paths(), CLAUSTRUM_PI_ENROLLMENT_NAME),
@@ -25,41 +23,10 @@ export function createPiCustodyCommands(options) {
25
23
  return { text: messages[result] };
26
24
  },
27
25
  async transition(mode) {
28
- if (mode === 'claustrum') {
29
- if (await hasPiLocalAnthropicOAuth()) {
30
- return {
31
- text: 'Refused: Pi still has a local Anthropic OAuth credential. Run `bunx @cortexkit/opencode-anthropic-auth setup` to approve its removal and enable Claustrum.',
32
- };
33
- }
34
- // A slash command never invokes the administrative CLI or approves itself.
35
- // Existing enrollment and grants must be verified before committing mode.
36
- await requirePiEnrollment();
37
- const client = await (options.connect?.() ??
38
- connectClaustrumScopedClient(getPiClaustrumConnectionOptions(storagePath)));
39
- const custody = new ClaustrumScopedCustody({
40
- client,
41
- tokenPath: paths().tokenPath,
42
- });
43
- try {
44
- const inventory = await custody.discover();
45
- const active = inventory.accounts.filter((account) => account.state === 'active');
46
- if (!active.length)
47
- return {
48
- text: 'Refused: Claustrum has no accessible active Anthropic accounts.',
49
- };
50
- for (const account of active)
51
- await custody.authorize(account);
52
- }
53
- finally {
54
- custody.close();
55
- }
56
- }
57
- await setClaustrumModePersistent(mode, storagePath);
58
- await options.reconfigure();
26
+ // Mode changes can remove host credentials and require offline consent.
27
+ // A menu never enrolls, grants custody or writes the authority journal.
59
28
  return {
60
- text: mode === 'claustrum'
61
- ? 'Pi now uses Claustrum for Anthropic authentication.'
62
- : 'Pi now uses local Anthropic authentication. Use /login anthropic if needed.',
29
+ text: `Refused: Changing Pi authentication to ${mode} requires offline setup. Run \`bunx @cortexkit/opencode-anthropic-auth setup\` and review the migration and host OAuth removal consent.`,
63
30
  };
64
31
  },
65
32
  };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { type ClaustrumScopedClient } from '@cortexkit/anthropic-auth-core';
1
+ import { type NativeCustodyClient } from '@cortexkit/anthropic-auth-core';
2
2
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
3
3
  export default function cortexKitPiAnthropicAuth(pi: ExtensionAPI, options?: {
4
- connectScoped?: () => Promise<ClaustrumScopedClient>;
5
- pollIntervalMs?: number;
4
+ connectScoped?: () => Promise<NativeCustodyClient>;
6
5
  }): Promise<void>;
package/dist/index.js CHANGED
@@ -1,54 +1,43 @@
1
- import { authorize, CLAUDE_FABLE_MYTHOS_5_1_PRICING, CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW, CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS, CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS, CLAUDE_FABLE_MYTHOS_5_PRICING, exchange, getClaustrumMode, isClaudeFableOrMythos51Model, loadAccounts, refreshClaudeOAuthToken, } from '@cortexkit/anthropic-auth-core';
1
+ import { CLAUDE_FABLE_MYTHOS_5_1_PRICING, CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW, CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS, CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS, CLAUDE_FABLE_MYTHOS_5_PRICING, CLAUDE_HAIKU_5_5_CONTEXT_WINDOW, CLAUDE_HAIKU_5_5_LONG_CONTEXT_PRICING, CLAUDE_HAIKU_5_5_LONG_CONTEXT_THRESHOLD, CLAUDE_HAIKU_5_5_MAX_OUTPUT_TOKENS, CLAUDE_HAIKU_5_5_MODEL_ID, CLAUDE_HAIKU_5_5_PRICING, CLAUDE_SONNET_5_5_CONTEXT_WINDOW, CLAUDE_SONNET_5_5_MAX_OUTPUT_TOKENS, CLAUDE_SONNET_5_5_MODEL_ID, CLAUDE_SONNET_5_5_PRICING, isClaudeFableOrMythos51Model, readNativeEnrollmentMode, } from '@cortexkit/anthropic-auth-core';
2
2
  import { registerCommands } from './commands.js';
3
- import { createPiCustodyCommands, requirePiEnrollment } from './custody.js';
4
3
  import { collectPiEffortHistory, deriveContextEntries, } from './effort-history.js';
5
- import { getPiAccountStoragePath } from './paths.js';
6
- import { closePiScopedRuntime, getPiScopedRuntime, streamCortexKitAnthropic, } from './stream.js';
7
- async function assertLocalAuthentication() {
8
- if (getClaustrumMode(await loadAccounts(getPiAccountStoragePath())) ===
9
- 'claustrum') {
10
- throw new Error('Local Anthropic login and refresh are disabled while Claustrum custody is active');
11
- }
12
- }
13
- async function loginAnthropic(callbacks) {
14
- await assertLocalAuthentication();
15
- const auth = await authorize('max');
16
- callbacks.onAuth({ url: auth.url });
17
- const callback = await callbacks.onPrompt({
18
- message: 'Paste the Claude OAuth callback URL or code:',
19
- });
20
- const result = await exchange(callback, auth.verifier, auth.redirectUri, auth.state);
21
- if (result.type !== 'success') {
22
- throw new Error('Anthropic OAuth exchange failed');
23
- }
24
- await assertLocalAuthentication();
25
- return {
26
- refresh: result.refresh,
27
- access: result.access,
28
- expires: result.expires,
29
- };
30
- }
4
+ import { closePiNativeRuntime, getPiNativeRuntime, isNativeMigrationRequired, } from './native.js';
5
+ import { createPiNativeCommands } from './native-commands.js';
6
+ import { getPiAccountStoragePath, requirePiNativeHostAuth } from './paths.js';
7
+ import { streamCortexKitAnthropic } from './stream.js';
31
8
  function textImageInput() {
32
9
  return ['text', 'image'];
33
10
  }
34
- async function refreshAnthropicToken(credentials) {
35
- await assertLocalAuthentication();
36
- const refreshed = await refreshClaudeOAuthToken({
37
- refreshToken: credentials.refresh,
11
+ /**
12
+ * Refuses every Anthropic prompt with Core's migration message. Registered
13
+ * only when startup found an install that offline setup has never migrated.
14
+ * Pi checks a prompt's credentials after its input handlers run. Handling the
15
+ * input here stops it before that check, which would otherwise answer with
16
+ * Pi's generic missing-key text for a 1.x install whose Pi login is still
17
+ * stored. Extension commands such as /claude run before input handlers, so
18
+ * they stay reachable.
19
+ */
20
+ function refuseUnmigratedPrompts(pi, storagePath) {
21
+ pi.on('input', async (_event, ctx) => {
22
+ // Prompts for other providers do not use this install's accounts.
23
+ if (ctx.model?.provider !== 'anthropic')
24
+ return { action: 'continue' };
25
+ const refusal = await getPiNativeRuntime(storagePath).migrationRefusal();
26
+ if (!refusal)
27
+ return { action: 'continue' };
28
+ if (ctx.hasUI)
29
+ ctx.ui.notify(refusal.message, 'error');
30
+ else
31
+ process.stderr.write(`${refusal.message}\n`);
32
+ return { action: 'handled' };
38
33
  });
39
- return {
40
- refresh: refreshed.refresh,
41
- access: refreshed.access,
42
- expires: refreshed.expires,
43
- };
44
34
  }
45
35
  export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
46
36
  const storagePath = getPiAccountStoragePath();
47
- registerCommands(pi, createPiCustodyCommands({
48
- storagePath,
49
- reconfigure: configureProvider,
50
- connect: options.connectScoped,
51
- }));
37
+ getPiNativeRuntime(storagePath, {
38
+ ...(options.connectScoped && { connect: options.connectScoped }),
39
+ });
40
+ registerCommands(pi, createPiNativeCommands(storagePath));
52
41
  const effortHistoryBySession = new Map();
53
42
  pi.on('turn_start', async (_event, ctx) => {
54
43
  const sessionId = ctx.sessionManager.getSessionId();
@@ -86,7 +75,7 @@ export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
86
75
  const sessionId = ctx.sessionManager.getSessionId();
87
76
  if (sessionId)
88
77
  effortHistoryBySession.delete(sessionId);
89
- closePiScopedRuntime(storagePath);
78
+ await closePiNativeRuntime(storagePath);
90
79
  });
91
80
  const configuration = {
92
81
  name: 'Anthropic (CortexKit OAuth)',
@@ -166,47 +155,120 @@ export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
166
155
  contextWindow: 1_000_000,
167
156
  maxTokens: 128_000,
168
157
  },
158
+ {
159
+ id: CLAUDE_HAIKU_5_5_MODEL_ID,
160
+ name: 'Claude Haiku 5.5',
161
+ reasoning: true,
162
+ thinkingLevelMap: {
163
+ off: null,
164
+ minimal: null,
165
+ xhigh: 'xhigh',
166
+ max: 'max',
167
+ },
168
+ input: textImageInput(),
169
+ cost: {
170
+ input: CLAUDE_HAIKU_5_5_PRICING.input,
171
+ output: CLAUDE_HAIKU_5_5_PRICING.output,
172
+ cacheRead: CLAUDE_HAIKU_5_5_PRICING.cacheRead,
173
+ cacheWrite: CLAUDE_HAIKU_5_5_PRICING.cacheWrite5m,
174
+ tiers: [
175
+ {
176
+ inputTokensAbove: CLAUDE_HAIKU_5_5_LONG_CONTEXT_THRESHOLD,
177
+ input: CLAUDE_HAIKU_5_5_LONG_CONTEXT_PRICING.input,
178
+ output: CLAUDE_HAIKU_5_5_LONG_CONTEXT_PRICING.output,
179
+ cacheRead: CLAUDE_HAIKU_5_5_LONG_CONTEXT_PRICING.cacheRead,
180
+ cacheWrite: CLAUDE_HAIKU_5_5_LONG_CONTEXT_PRICING.cacheWrite5m,
181
+ },
182
+ ],
183
+ },
184
+ contextWindow: CLAUDE_HAIKU_5_5_CONTEXT_WINDOW,
185
+ maxTokens: CLAUDE_HAIKU_5_5_MAX_OUTPUT_TOKENS,
186
+ },
187
+ {
188
+ id: CLAUDE_SONNET_5_5_MODEL_ID,
189
+ name: 'Claude Sonnet 5.5',
190
+ reasoning: true,
191
+ thinkingLevelMap: {
192
+ off: null,
193
+ minimal: null,
194
+ xhigh: 'xhigh',
195
+ max: 'max',
196
+ },
197
+ input: textImageInput(),
198
+ cost: {
199
+ input: CLAUDE_SONNET_5_5_PRICING.input,
200
+ output: CLAUDE_SONNET_5_5_PRICING.output,
201
+ cacheRead: CLAUDE_SONNET_5_5_PRICING.cacheRead,
202
+ cacheWrite: CLAUDE_SONNET_5_5_PRICING.cacheWrite5m,
203
+ },
204
+ contextWindow: CLAUDE_SONNET_5_5_CONTEXT_WINDOW,
205
+ maxTokens: CLAUDE_SONNET_5_5_MAX_OUTPUT_TOKENS,
206
+ },
169
207
  ],
170
- oauth: {
171
- name: 'Anthropic Claude Pro/Max (CortexKit)',
172
- login: loginAnthropic,
173
- refreshToken: refreshAnthropicToken,
174
- getApiKey: (credentials) => credentials.access,
175
- },
176
208
  streamSimple: (model, context, options) => streamCortexKitAnthropic(model, context, options, options?.sessionId
177
209
  ? effortHistoryBySession.get(options.sessionId)
178
210
  : undefined),
179
211
  };
180
212
  async function configureProvider() {
181
- if (getClaustrumMode(await loadAccounts(storagePath)) !== 'claustrum') {
182
- closePiScopedRuntime(storagePath);
183
- pi.registerProvider('anthropic', configuration);
184
- return;
185
- }
186
213
  const streamSimple = configuration.streamSimple;
187
214
  if (!streamSimple)
188
215
  throw new Error('Anthropic stream implementation is unavailable');
216
+ // Register vault-backed models even if no account can serve yet, so a
217
+ // repaired enrollment or re-login takes effect on the next request. Local
218
+ // credentials still require completed offline migration before serving.
219
+ const startupMode = await readNativeEnrollmentMode('pi').catch(() => undefined);
220
+ try {
221
+ if (startupMode === 'local')
222
+ await getPiNativeRuntime(storagePath).view();
223
+ }
224
+ catch (error) {
225
+ // An install that offline setup has never migrated, such as a 1.x
226
+ // install upgraded in place, must still load: a failed load drops the
227
+ // /claude menu and Pi exits at startup. Its prompts are refused instead.
228
+ // Any other failure stops the load as before.
229
+ const refusal = await getPiNativeRuntime(storagePath).migrationRefusal();
230
+ if (!refusal)
231
+ throw error;
232
+ refuseUnmigratedPrompts(pi, storagePath);
233
+ }
189
234
  const configured = async () => {
190
- if (getClaustrumMode(await loadAccounts(storagePath)) !== 'claustrum')
191
- return false;
192
- await requirePiEnrollment();
193
- return true;
235
+ const mode = await readNativeEnrollmentMode('pi').catch(() => undefined);
236
+ if (mode !== 'local')
237
+ return true;
238
+ await requirePiNativeHostAuth();
239
+ let snapshot;
240
+ try {
241
+ snapshot = await (await getPiNativeRuntime(storagePath).service()).read();
242
+ }
243
+ catch (error) {
244
+ // An unmigrated install still reports the provider as configured, so
245
+ // Pi keeps its Anthropic models selectable and its prompts reach the
246
+ // refusal above (or, from any other caller, the stream, which refuses
247
+ // with the same Core message). If this check threw instead, Pi would
248
+ // record the refusal as a startup availability error.
249
+ if (isNativeMigrationRequired(error))
250
+ return true;
251
+ throw error;
252
+ }
253
+ return snapshot.accounts.some((account) => account.type === 'oauth' &&
254
+ account.enabled &&
255
+ (account.source !== 'vault' || account.state === 'active'));
194
256
  };
195
257
  const provider = {
196
258
  id: 'anthropic',
197
- name: 'Anthropic (Claustrum)',
259
+ name: 'Anthropic (CortexKit Native)',
198
260
  baseUrl: 'https://api.anthropic.com',
199
261
  auth: {
200
262
  // Native ambient auth avoids fake keys and local OAuth refresh. Pi refuses
201
263
  // a leftover stored OAuth credential because this provider has no OAuth
202
264
  // handler; setup must obtain consent before removing that local entry.
203
265
  apiKey: {
204
- name: 'Claustrum',
266
+ name: 'CortexKit Native',
205
267
  check: async () => (await configured())
206
- ? { type: 'api_key', source: 'Claustrum' }
268
+ ? { type: 'api_key', source: 'CortexKit Native' }
207
269
  : undefined,
208
270
  resolve: async () => (await configured())
209
- ? { auth: {}, source: 'Claustrum' }
271
+ ? { auth: {}, source: 'CortexKit Native' }
210
272
  : undefined,
211
273
  },
212
274
  },
@@ -221,12 +283,6 @@ export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
221
283
  streamSimple,
222
284
  };
223
285
  pi.registerProvider(provider);
224
- getPiScopedRuntime(storagePath, {
225
- ...(options.connectScoped && { connect: options.connectScoped }),
226
- ...(options.pollIntervalMs !== undefined && {
227
- pollIntervalMs: options.pollIntervalMs,
228
- }),
229
- }).start();
230
286
  }
231
287
  await configureProvider();
232
288
  }
@@ -0,0 +1,2 @@
1
+ import type { PiNativeCommands } from './commands.ts';
2
+ export declare function createPiNativeCommands(storagePath?: string): PiNativeCommands;