@cortexkit/pi-anthropic-auth 1.23.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ import type { Context, Message, Tool } from '@earendil-works/pi-ai';
2
+ /**
3
+ * A transcript whose prompt and tool set are carried by its leading system
4
+ * message, which is the shape Pi 0.86 hands to providers.
5
+ */
6
+ export type Transcript = {
7
+ messages: Message[];
8
+ };
9
+ /** Fold `Context.systemPrompt` and `Context.tools` into a leading system message. */
10
+ export declare function normalizeContext(context: Context): Transcript;
11
+ /** Resolve the tools available after applying every transcript delta in order. */
12
+ export declare function getCurrentTools(messages: readonly Message[]): Tool[];
13
+ /** Render the current system prompt text after replaying every system message. */
14
+ export declare function getCurrentSystemPrompt(messages: readonly Message[]): string;
15
+ /**
16
+ * Rebuild the transcript for APIs without mid-conversation system messages: the
17
+ * replayed system message leads, and every later system message is dropped.
18
+ */
19
+ export declare function collapseSystemMessages(transcript: Transcript): Transcript;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Replay a transcript's system messages into one leading prompt and tool set.
3
+ *
4
+ * Ported from pi-ai's `utils/transcript.ts`. Pi 0.86 exports these helpers from
5
+ * its package root; Oh My Pi serves every legacy pi-scope import — including
6
+ * this package's `@earendil-works/pi-ai` — from its own bundled
7
+ * `@oh-my-pi/pi-ai` fork, which has no system-message replay at all. Importing
8
+ * them there threw `Export named 'collapseSystemMessages' not found in module
9
+ * 'omp-legacy-pi-bundled:@oh-my-pi/pi-ai'` while the extension loaded, so no
10
+ * request was ever built. Host types are used for build-time shape only: nothing
11
+ * here imports the host at runtime (see scripts/check-pi-dist-imports.ts).
12
+ *
13
+ * Only `system` messages are read, so a host whose transcript has none — an
14
+ * OMP transcript puts the prompt in `Context.systemPrompt` and has no system
15
+ * role — still resolves to the message built from that prompt and
16
+ * `Context.tools`, which is what that host's own providers send.
17
+ */
18
+ function contentText(content) {
19
+ if (typeof content === 'string')
20
+ return content;
21
+ return content
22
+ .filter((block) => block.type === 'text')
23
+ .map((block) => block.text)
24
+ .join('\n');
25
+ }
26
+ /** Render a system message as a complete prompt: its content followed by its sections. */
27
+ function systemMessageText(message) {
28
+ const parts = [contentText(message.content)];
29
+ for (const text of Object.values(message.sections ?? {})) {
30
+ if (text !== null)
31
+ parts.push(text);
32
+ }
33
+ return parts.filter((part) => part.length > 0).join('\n\n');
34
+ }
35
+ function isSystemMessage(message) {
36
+ return message.role === 'system';
37
+ }
38
+ /**
39
+ * Build the leading system message for a prompt and tool set. Returns undefined when
40
+ * both are empty, so an empty transcript stays empty.
41
+ */
42
+ function createInitialSystemMessage(systemPrompt, tools) {
43
+ const hasSystemPrompt = systemPrompt !== undefined && systemPrompt.length > 0;
44
+ const hasTools = tools !== undefined && tools.length > 0;
45
+ if (!hasSystemPrompt && !hasTools)
46
+ return undefined;
47
+ return {
48
+ role: 'system',
49
+ content: systemPrompt ?? '',
50
+ ...(hasTools ? { toolsAdded: tools } : {}),
51
+ timestamp: 0,
52
+ };
53
+ }
54
+ /** Fold `Context.systemPrompt` and `Context.tools` into a leading system message. */
55
+ export function normalizeContext(context) {
56
+ const initialMessage = createInitialSystemMessage(context.systemPrompt, context.tools);
57
+ const messages = initialMessage
58
+ ? [initialMessage, ...context.messages]
59
+ : context.messages;
60
+ return { messages };
61
+ }
62
+ /** Resolve the tools available after applying every transcript delta in order. */
63
+ export function getCurrentTools(messages) {
64
+ const tools = new Map();
65
+ for (const message of messages) {
66
+ if (!isSystemMessage(message))
67
+ continue;
68
+ for (const tool of message.toolsRemoved ?? [])
69
+ tools.delete(tool.name);
70
+ for (const tool of message.toolsAdded ?? [])
71
+ tools.set(tool.name, tool);
72
+ }
73
+ return [...tools.values()];
74
+ }
75
+ /**
76
+ * Replay every system message into one leading system message holding the current
77
+ * prompt and tools. Later `content` is appended to the base prompt, `sections` are
78
+ * patched by name, and tools are resolved with {@link getCurrentTools}.
79
+ */
80
+ function getCurrentSystemMessage(messages) {
81
+ const content = [];
82
+ const sections = new Map();
83
+ let timestamp;
84
+ for (const message of messages) {
85
+ if (!isSystemMessage(message))
86
+ continue;
87
+ timestamp ??= message.timestamp;
88
+ const text = contentText(message.content);
89
+ if (text.length > 0)
90
+ content.push(text);
91
+ for (const [name, value] of Object.entries(message.sections ?? {})) {
92
+ if (value === null)
93
+ sections.delete(name);
94
+ else
95
+ sections.set(name, value);
96
+ }
97
+ }
98
+ const tools = getCurrentTools(messages);
99
+ if (timestamp === undefined && tools.length === 0)
100
+ return undefined;
101
+ return {
102
+ role: 'system',
103
+ content: content.join('\n\n'),
104
+ ...(sections.size > 0 ? { sections: Object.fromEntries(sections) } : {}),
105
+ ...(tools.length > 0 ? { toolsAdded: tools } : {}),
106
+ timestamp: timestamp ?? 0,
107
+ };
108
+ }
109
+ /** Render the current system prompt text after replaying every system message. */
110
+ export function getCurrentSystemPrompt(messages) {
111
+ const message = getCurrentSystemMessage(messages);
112
+ return message ? systemMessageText(message) : '';
113
+ }
114
+ /**
115
+ * Rebuild the transcript for APIs without mid-conversation system messages: the
116
+ * replayed system message leads, and every later system message is dropped.
117
+ */
118
+ export function collapseSystemMessages(transcript) {
119
+ const head = getCurrentSystemMessage(transcript.messages);
120
+ const messages = transcript.messages.filter((message) => message.role !== 'system');
121
+ return { messages: head ? [head, ...messages] : messages };
122
+ }
@@ -0,0 +1,5 @@
1
+ /** Model base URLs and remembered cache-warm URLs must send OAuth tokens only to Anthropic’s HTTPS server. */
2
+ export declare function requirePiOAuthOrigin(value: string): URL;
3
+ export declare function buildPiOAuthMessagesUrl(baseUrl: string): URL;
4
+ /** Reject redirects rather than letting an approved OAuth destination forward the request. */
5
+ export declare const fetchPiOAuth: typeof fetch;
@@ -0,0 +1,30 @@
1
+ const ANTHROPIC_ORIGIN = 'https://api.anthropic.com';
2
+ /** Model base URLs and remembered cache-warm URLs must send OAuth tokens only to Anthropic’s HTTPS server. */
3
+ export function requirePiOAuthOrigin(value) {
4
+ let url;
5
+ try {
6
+ url = new URL(value);
7
+ }
8
+ catch {
9
+ throw new Error('Native OAuth requires the official Anthropic HTTPS origin');
10
+ }
11
+ if (url.protocol !== 'https:' ||
12
+ url.origin !== ANTHROPIC_ORIGIN ||
13
+ url.username ||
14
+ url.password)
15
+ throw new Error('Native OAuth requires the official Anthropic HTTPS origin without URL userinfo');
16
+ return url;
17
+ }
18
+ export function buildPiOAuthMessagesUrl(baseUrl) {
19
+ return new URL('/v1/messages?beta=true', requirePiOAuthOrigin(baseUrl));
20
+ }
21
+ /** Reject redirects rather than letting an approved OAuth destination forward the request. */
22
+ export const fetchPiOAuth = Object.assign(async (input, init) => {
23
+ requirePiOAuthOrigin(input instanceof Request ? input.url : String(input));
24
+ return fetch(input, { ...init, redirect: 'error' });
25
+ }, {
26
+ preconnect: (...args) => {
27
+ requirePiOAuthOrigin(String(args[0]));
28
+ return fetch.preconnect(...args);
29
+ },
30
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/pi-anthropic-auth",
3
- "version": "1.23.0",
3
+ "version": "2.0.1",
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": "1.23.0"
39
+ "@cortexkit/anthropic-auth-core": "2.0.1"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@earendil-works/pi-ai": ">=0.86.1",