@cortexkit/pi-anthropic-auth 2.1.1 → 2.2.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.d.ts CHANGED
@@ -33,6 +33,9 @@ export declare function buildAnthropicRequest(modelId: string, context: Context,
33
33
  }, fastModeEnabled?: boolean, identity?: ClaudeCodeIdentity, controls?: {
34
34
  effortTransitions?: readonly MidConversationEffortTransition[];
35
35
  thinkingPrefixMismatchBehavior?: ThinkingPrefixMismatchBehavior;
36
+ turnEndNote?: {
37
+ tools: string[];
38
+ };
36
39
  }): Promise<{
37
40
  body: AnthropicRequestBody;
38
41
  bodyText: string;
package/dist/convert.js CHANGED
@@ -1,4 +1,4 @@
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';
1
+ import { applyClaudeCodeMetadata, applyMidConversationOutputConfig, applyThinkingBindingControls, applyTurnEndNotes, 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
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
@@ -111,7 +111,7 @@ function convertTextAndImages(content) {
111
111
  }
112
112
  return blocks;
113
113
  }
114
- function convertMessages(messages, targetModelId) {
114
+ function convertMessages(messages, targetModelId, visibleReplies) {
115
115
  const result = [];
116
116
  for (let index = 0; index < messages.length; index++) {
117
117
  const message = messages[index];
@@ -142,9 +142,12 @@ function convertMessages(messages, targetModelId) {
142
142
  continue;
143
143
  }
144
144
  const blocks = [];
145
+ let visibleText = '';
145
146
  for (const block of message.content) {
146
147
  if (block.type === 'text' && block.text.trim()) {
147
- blocks.push({ type: 'text', text: sanitize(block.text) });
148
+ const text = sanitize(block.text);
149
+ blocks.push({ type: 'text', text });
150
+ visibleText += text;
148
151
  }
149
152
  else if (block.type === 'thinking') {
150
153
  const thinking = block;
@@ -213,8 +216,15 @@ function convertMessages(messages, targetModelId) {
213
216
  });
214
217
  }
215
218
  }
216
- if (blocks.length)
217
- result.push({ role: 'assistant', content: blocks });
219
+ if (blocks.length) {
220
+ const converted = { role: 'assistant', content: blocks };
221
+ result.push(converted);
222
+ // Converted thinking can become text for replay, but it was never a
223
+ // visible reply. Count only actual text blocks, using the exact outgoing
224
+ // strings collected above, so the note decision for this message stays
225
+ // the same on every later request.
226
+ visibleReplies.set(converted, visibleText);
227
+ }
218
228
  continue;
219
229
  }
220
230
  if (message.role === 'toolResult') {
@@ -453,7 +463,8 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
453
463
  // first so those losses cannot hide a meaningful final assistant turn. Check
454
464
  // the Anthropic messages again after conversion removes empty user messages.
455
465
  assertNoMeaningfulTrailingAssistant(context.messages);
456
- const messages = convertMessages(context.messages, modelId);
466
+ const visibleReplies = new WeakMap();
467
+ const messages = convertMessages(context.messages, modelId, visibleReplies);
457
468
  stripEmptyTrailingAssistantMessages(messages);
458
469
  const system = [
459
470
  {
@@ -566,6 +577,13 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
566
577
  }
567
578
  }
568
579
  applyMidConversationOutputConfig(body, controls.effortTransitions ?? []);
580
+ if (identity) {
581
+ applyTurnEndNotes(body, {
582
+ tools: controls.turnEndNote?.tools,
583
+ mapToolName: toClaudeCodeToolName,
584
+ visibleText: (message) => visibleReplies.get(message) ?? '',
585
+ });
586
+ }
569
587
  addEphemeralCacheControl(body);
570
588
  applyCacheMode(body, cache.enabled, cache.mode);
571
589
  if (identity) {
package/dist/native.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type NativeAccountRuntime, type NativeAccountSnapshot, type NativeCustodyClient, type NativeCustodyReceipt, type NativeCustodyReporterSource, type NativeRefreshResult } from '@cortexkit/anthropic-auth-core';
1
+ import { type NativeAccountRuntime, type NativeAccountRuntimeOptions, type NativeAccountSnapshot, type NativeCustodyClient, type NativeCustodyReceipt, type NativeCustodyReporterSource, type NativeRefreshResult } from '@cortexkit/anthropic-auth-core';
2
2
  export type PiNativeAttempt = {
3
3
  source: 'local';
4
4
  accountIdentity: string;
@@ -38,8 +38,14 @@ export declare class PiNativeRuntime {
38
38
  publish(write: () => Promise<unknown>): void;
39
39
  drain(): Promise<void>;
40
40
  };
41
+ private readonly routingPublications;
42
+ ownRoutingPublications(router: {
43
+ drain(): Promise<void>;
44
+ }): void;
45
+ drainRoutingPublications(): Promise<void>;
41
46
  constructor(storagePath: string, options?: {
42
47
  connect?: () => Promise<NativeCustodyClient>;
48
+ local?: NativeAccountRuntimeOptions['local'];
43
49
  vaultPoll?: PiVaultPollOptions;
44
50
  });
45
51
  service(): Promise<NativeAccountRuntime>;
@@ -68,6 +74,7 @@ export declare class PiNativeRuntime {
68
74
  }
69
75
  export declare function getPiNativeRuntime(storagePath?: string, options?: {
70
76
  connect?: () => Promise<NativeCustodyClient>;
77
+ local?: NativeAccountRuntimeOptions['local'];
71
78
  vaultPoll?: PiVaultPollOptions;
72
79
  }): PiNativeRuntime;
73
80
  export declare function closePiNativeRuntime(storagePath?: string): Promise<void>;
package/dist/native.js CHANGED
@@ -66,6 +66,16 @@ export class PiNativeRuntime {
66
66
  closed = false;
67
67
  closing;
68
68
  usagePublications = createNativeUsagePublisher('native-auth');
69
+ routingPublications = new Set();
70
+ ownRoutingPublications(router) {
71
+ this.routingPublications.add(router);
72
+ }
73
+ async drainRoutingPublications() {
74
+ await Promise.all([
75
+ ...[...this.routingPublications].map((router) => router.drain()),
76
+ this.initialization.then((runtime) => runtime.drainRoutingPublications(), () => { }),
77
+ ]);
78
+ }
69
79
  constructor(storagePath, options = {}) {
70
80
  const env = { ...process.env };
71
81
  this.env = env;
@@ -76,6 +86,7 @@ export class PiNativeRuntime {
76
86
  const runtime = acquireNativeAccountRuntime({
77
87
  paths,
78
88
  host: 'pi',
89
+ local: options.local,
79
90
  vault: {
80
91
  // Core checks Pi enrollment and reads a fresh scoped receipt on every send.
81
92
  // Keep the injected connector's identity rather than wrapping it per instance.
@@ -269,8 +280,11 @@ export class PiNativeRuntime {
269
280
  this.vaultWatch?.close();
270
281
  this.closing ??= (async () => {
271
282
  await this.usagePublications.drain();
283
+ await this.drainRoutingPublications();
284
+ this.routingPublications.clear();
272
285
  await this.initialization.then(async (runtime) => {
273
286
  await runtime.drainQuotaPublications();
287
+ await runtime.drainRoutingPublications();
274
288
  runtime.close();
275
289
  }, () => { });
276
290
  })();
@@ -282,11 +296,15 @@ export function getPiNativeRuntime(storagePath = getPiAccountStoragePath(), opti
282
296
  const key = JSON.stringify([
283
297
  contextKey,
284
298
  nativeRuntimeOptionIdentity(options.connect),
299
+ nativeRuntimeOptionIdentity(options.local?.onLockEvent),
300
+ nativeRuntimeOptionIdentity(options.local?.refreshToken),
301
+ nativeRuntimeOptionIdentity(options.local?.resolveIdentity),
302
+ nativeRuntimeOptionIdentity(options.local?.now),
285
303
  ]);
286
304
  const existing = runtimes.get(key);
287
305
  if (existing)
288
306
  return existing.runtime;
289
- if (!options.connect) {
307
+ if (!options.connect && !options.local) {
290
308
  // A supplied connector installs the context's test transport. Ordinary host
291
309
  // lookups reuse it, but an explicitly different connector owns another key.
292
310
  const configured = [...runtimes.values()].find((entry) => entry.contextKey === contextKey);
package/dist/stream.js CHANGED
@@ -109,10 +109,16 @@ async function getPiStickyRouter(storagePath) {
109
109
  const { path, vaultSeed } = await getPiNativeRuntime(storagePath).routingStorage();
110
110
  let router = stickyRouters.get(path);
111
111
  if (!router) {
112
- router = new StickySessionRouter({ path, vaultSeed });
112
+ const runtime = await getPiNativeRuntime(storagePath).service();
113
+ router = new StickySessionRouter({
114
+ path,
115
+ vaultSeed,
116
+ onLockEvent: runtime.onLockEvent,
117
+ });
113
118
  setBoundedService(stickyRouters, path, router);
114
119
  }
115
120
  router.setVaultSeed(vaultSeed);
121
+ getPiNativeRuntime(storagePath).ownRoutingPublications(router);
116
122
  return router;
117
123
  }
118
124
  export async function clearPiStickyRoutingSession(storagePath, sessionId) {
@@ -357,6 +363,7 @@ async function sendAnthropicRequest(options) {
357
363
  mode: getCache1hPersistentMode(storage),
358
364
  }, isFastModePersistentlyEnabled(storage), identity, {
359
365
  effortTransitions: options.apiAccount ? [] : options.effortTransitions,
366
+ turnEndNote: storage.turnEndNote,
360
367
  thinkingPrefixMismatchBehavior: options.apiAccount
361
368
  ? 'account-default'
362
369
  : getThinkingPrefixMismatchBehavior(storage),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/pi-anthropic-auth",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  "prepublishOnly": "bun run build"
37
37
  },
38
38
  "dependencies": {
39
- "@cortexkit/anthropic-auth-core": "2.1.1"
39
+ "@cortexkit/anthropic-auth-core": "2.2.0"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@earendil-works/pi-ai": ">=0.86.1 <2",