@codeany/open-agent-sdk 0.1.0 → 0.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/README.md +144 -31
- package/examples/12-skills.ts +88 -0
- package/examples/13-hooks.ts +88 -0
- package/examples/14-openai-compat.ts +71 -0
- package/package.json +1 -1
- package/src/agent.ts +106 -15
- package/src/engine.ts +169 -59
- package/src/index.ts +51 -1
- package/src/providers/anthropic.ts +60 -0
- package/src/providers/index.ts +34 -0
- package/src/providers/openai.ts +315 -0
- package/src/providers/types.ts +85 -0
- package/src/session.ts +5 -5
- package/src/skills/bundled/commit.ts +38 -0
- package/src/skills/bundled/debug.ts +48 -0
- package/src/skills/bundled/index.ts +28 -0
- package/src/skills/bundled/review.ts +41 -0
- package/src/skills/bundled/simplify.ts +51 -0
- package/src/skills/bundled/test.ts +43 -0
- package/src/skills/index.ts +25 -0
- package/src/skills/registry.ts +133 -0
- package/src/skills/types.ts +99 -0
- package/src/tools/agent-tool.ts +13 -2
- package/src/tools/index.ts +8 -0
- package/src/tools/skill-tool.ts +133 -0
- package/src/tools/types.ts +7 -3
- package/src/types.ts +35 -8
- package/src/utils/compact.ts +18 -17
- package/src/utils/messages.ts +12 -13
- package/src/utils/tokens.ts +29 -6
package/src/types.ts
CHANGED
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
* Core type definitions for the Agent SDK
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Content block types (provider-agnostic, compatible with Anthropic format)
|
|
6
|
+
export type ContentBlockParam =
|
|
7
|
+
| { type: 'text'; text: string }
|
|
8
|
+
| { type: 'image'; source: any }
|
|
9
|
+
| { type: 'tool_use'; id: string; name: string; input: any }
|
|
10
|
+
| { type: 'tool_result'; tool_use_id: string; content: string | any[]; is_error?: boolean }
|
|
11
|
+
|
|
12
|
+
export type ContentBlock =
|
|
13
|
+
| { type: 'text'; text: string }
|
|
14
|
+
| { type: 'tool_use'; id: string; name: string; input: any }
|
|
15
|
+
| { type: 'thinking'; thinking: string }
|
|
6
16
|
|
|
7
17
|
// --------------------------------------------------------------------------
|
|
8
18
|
// Message Types
|
|
@@ -12,7 +22,7 @@ export type MessageRole = 'user' | 'assistant'
|
|
|
12
22
|
|
|
13
23
|
export interface ConversationMessage {
|
|
14
24
|
role: MessageRole
|
|
15
|
-
content: string |
|
|
25
|
+
content: string | ContentBlockParam[]
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
export interface UserMessage {
|
|
@@ -26,7 +36,7 @@ export interface AssistantMessage {
|
|
|
26
36
|
type: 'assistant'
|
|
27
37
|
message: {
|
|
28
38
|
role: 'assistant'
|
|
29
|
-
content:
|
|
39
|
+
content: ContentBlock[]
|
|
30
40
|
}
|
|
31
41
|
uuid: string
|
|
32
42
|
timestamp: string
|
|
@@ -57,7 +67,7 @@ export interface SDKAssistantMessage {
|
|
|
57
67
|
session_id?: string
|
|
58
68
|
message: {
|
|
59
69
|
role: 'assistant'
|
|
60
|
-
content:
|
|
70
|
+
content: ContentBlock[]
|
|
61
71
|
}
|
|
62
72
|
parent_tool_use_id?: string | null
|
|
63
73
|
}
|
|
@@ -181,12 +191,18 @@ export interface ToolInputSchema {
|
|
|
181
191
|
export interface ToolContext {
|
|
182
192
|
cwd: string
|
|
183
193
|
abortSignal?: AbortSignal
|
|
194
|
+
/** Parent agent's LLM provider (inherited by subagents) */
|
|
195
|
+
provider?: import('./providers/types.js').LLMProvider
|
|
196
|
+
/** Parent agent's model ID */
|
|
197
|
+
model?: string
|
|
198
|
+
/** Parent agent's API type */
|
|
199
|
+
apiType?: import('./providers/types.js').ApiType
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
export interface ToolResult {
|
|
187
203
|
type: 'tool_result'
|
|
188
204
|
tool_use_id: string
|
|
189
|
-
content: string |
|
|
205
|
+
content: string | any[]
|
|
190
206
|
is_error?: boolean
|
|
191
207
|
}
|
|
192
208
|
|
|
@@ -202,8 +218,10 @@ export type PermissionMode =
|
|
|
202
218
|
| 'dontAsk'
|
|
203
219
|
| 'auto'
|
|
204
220
|
|
|
221
|
+
export type PermissionBehavior = 'allow' | 'deny'
|
|
222
|
+
|
|
205
223
|
export type CanUseToolResult = {
|
|
206
|
-
behavior:
|
|
224
|
+
behavior: PermissionBehavior
|
|
207
225
|
updatedInput?: unknown
|
|
208
226
|
message?: string
|
|
209
227
|
}
|
|
@@ -326,6 +344,11 @@ export interface ModelInfo {
|
|
|
326
344
|
export interface AgentOptions {
|
|
327
345
|
/** LLM model ID */
|
|
328
346
|
model?: string
|
|
347
|
+
/**
|
|
348
|
+
* API type: 'anthropic-messages' or 'openai-completions'.
|
|
349
|
+
* Falls back to CODEANY_API_TYPE env var. Default: 'anthropic-messages'.
|
|
350
|
+
*/
|
|
351
|
+
apiType?: import('./providers/types.js').ApiType
|
|
329
352
|
/** API key. Falls back to CODEANY_API_KEY env var. */
|
|
330
353
|
apiKey?: string
|
|
331
354
|
/** API base URL override */
|
|
@@ -442,8 +465,8 @@ export interface QueryResult {
|
|
|
442
465
|
export interface QueryEngineConfig {
|
|
443
466
|
cwd: string
|
|
444
467
|
model: string
|
|
445
|
-
|
|
446
|
-
|
|
468
|
+
/** LLM provider instance (created from apiType) */
|
|
469
|
+
provider: import('./providers/types.js').LLMProvider
|
|
447
470
|
tools: ToolDefinition[]
|
|
448
471
|
systemPrompt?: string
|
|
449
472
|
appendSystemPrompt?: string
|
|
@@ -456,4 +479,8 @@ export interface QueryEngineConfig {
|
|
|
456
479
|
includePartialMessages: boolean
|
|
457
480
|
abortSignal?: AbortSignal
|
|
458
481
|
agents?: Record<string, AgentDefinition>
|
|
482
|
+
/** Hook registry for lifecycle events */
|
|
483
|
+
hookRegistry?: import('./hooks.js').HookRegistry
|
|
484
|
+
/** Session ID for hook context */
|
|
485
|
+
sessionId?: string
|
|
459
486
|
}
|
package/src/utils/compact.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* 3. Session memory compaction: consolidates across sessions
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
import type { LLMProvider } from '../providers/types.js'
|
|
12
|
+
import type { NormalizedMessageParam } from '../providers/types.js'
|
|
12
13
|
import {
|
|
13
14
|
estimateMessagesTokens,
|
|
14
15
|
getAutoCompactThreshold,
|
|
@@ -38,7 +39,7 @@ export function createAutoCompactState(): AutoCompactState {
|
|
|
38
39
|
* Check if auto-compaction should trigger.
|
|
39
40
|
*/
|
|
40
41
|
export function shouldAutoCompact(
|
|
41
|
-
messages:
|
|
42
|
+
messages: any[],
|
|
42
43
|
model: string,
|
|
43
44
|
state: AutoCompactState,
|
|
44
45
|
): boolean {
|
|
@@ -57,12 +58,12 @@ export function shouldAutoCompact(
|
|
|
57
58
|
* then replaces the history with a compact summary.
|
|
58
59
|
*/
|
|
59
60
|
export async function compactConversation(
|
|
60
|
-
|
|
61
|
+
provider: LLMProvider,
|
|
61
62
|
model: string,
|
|
62
|
-
messages:
|
|
63
|
+
messages: any[],
|
|
63
64
|
state: AutoCompactState,
|
|
64
65
|
): Promise<{
|
|
65
|
-
compactedMessages:
|
|
66
|
+
compactedMessages: NormalizedMessageParam[]
|
|
66
67
|
summary: string
|
|
67
68
|
state: AutoCompactState
|
|
68
69
|
}> {
|
|
@@ -73,9 +74,9 @@ export async function compactConversation(
|
|
|
73
74
|
// Build compaction prompt
|
|
74
75
|
const compactionPrompt = buildCompactionPrompt(strippedMessages)
|
|
75
76
|
|
|
76
|
-
const response = await
|
|
77
|
+
const response = await provider.createMessage({
|
|
77
78
|
model,
|
|
78
|
-
|
|
79
|
+
maxTokens: 8192,
|
|
79
80
|
system: 'You are a conversation summarizer. Create a detailed summary of the conversation that preserves all important context, decisions made, files modified, tool outputs, and current state. The summary should allow the conversation to continue seamlessly.',
|
|
80
81
|
messages: [
|
|
81
82
|
{
|
|
@@ -86,12 +87,12 @@ export async function compactConversation(
|
|
|
86
87
|
})
|
|
87
88
|
|
|
88
89
|
const summary = response.content
|
|
89
|
-
.filter((b)
|
|
90
|
-
.map((b) => b.text)
|
|
90
|
+
.filter((b) => b.type === 'text')
|
|
91
|
+
.map((b) => (b as { type: 'text'; text: string }).text)
|
|
91
92
|
.join('\n')
|
|
92
93
|
|
|
93
94
|
// Replace messages with summary
|
|
94
|
-
const compactedMessages:
|
|
95
|
+
const compactedMessages: NormalizedMessageParam[] = [
|
|
95
96
|
{
|
|
96
97
|
role: 'user',
|
|
97
98
|
content: `[Previous conversation summary]\n\n${summary}\n\n[End of summary - conversation continues below]`,
|
|
@@ -127,9 +128,9 @@ export async function compactConversation(
|
|
|
127
128
|
* Strip images from messages for compaction safety.
|
|
128
129
|
*/
|
|
129
130
|
function stripImagesFromMessages(
|
|
130
|
-
messages:
|
|
131
|
-
):
|
|
132
|
-
return messages.map((msg) => {
|
|
131
|
+
messages: any[],
|
|
132
|
+
): any[] {
|
|
133
|
+
return messages.map((msg: any) => {
|
|
133
134
|
if (typeof msg.content === 'string') return msg
|
|
134
135
|
|
|
135
136
|
const filtered = (msg.content as any[]).filter((block: any) => {
|
|
@@ -143,7 +144,7 @@ function stripImagesFromMessages(
|
|
|
143
144
|
/**
|
|
144
145
|
* Build compaction prompt from messages.
|
|
145
146
|
*/
|
|
146
|
-
function buildCompactionPrompt(messages:
|
|
147
|
+
function buildCompactionPrompt(messages: any[]): string {
|
|
147
148
|
const parts: string[] = ['Please summarize this conversation:\n']
|
|
148
149
|
|
|
149
150
|
for (const msg of messages) {
|
|
@@ -179,10 +180,10 @@ function buildCompactionPrompt(messages: Anthropic.MessageParam[]): string {
|
|
|
179
180
|
* to fit within token budgets.
|
|
180
181
|
*/
|
|
181
182
|
export function microCompactMessages(
|
|
182
|
-
messages:
|
|
183
|
+
messages: any[],
|
|
183
184
|
maxToolResultChars: number = 50000,
|
|
184
|
-
):
|
|
185
|
-
return messages.map((msg) => {
|
|
185
|
+
): any[] {
|
|
186
|
+
return messages.map((msg: any) => {
|
|
186
187
|
if (typeof msg.content === 'string') return msg
|
|
187
188
|
if (!Array.isArray(msg.content)) return msg
|
|
188
189
|
|
package/src/utils/messages.ts
CHANGED
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
* synthetic placeholders, and content processing.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type Anthropic from '@anthropic-ai/sdk'
|
|
9
8
|
import type { Message, UserMessage, AssistantMessage, TokenUsage } from '../types.js'
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Create a user message.
|
|
13
12
|
*/
|
|
14
13
|
export function createUserMessage(
|
|
15
|
-
content: string |
|
|
14
|
+
content: string | any[],
|
|
16
15
|
options?: {
|
|
17
16
|
uuid?: string
|
|
18
17
|
isMeta?: boolean
|
|
@@ -34,7 +33,7 @@ export function createUserMessage(
|
|
|
34
33
|
* Create an assistant message.
|
|
35
34
|
*/
|
|
36
35
|
export function createAssistantMessage(
|
|
37
|
-
content:
|
|
36
|
+
content: any[],
|
|
38
37
|
usage?: TokenUsage,
|
|
39
38
|
): AssistantMessage {
|
|
40
39
|
return {
|
|
@@ -55,9 +54,9 @@ export function createAssistantMessage(
|
|
|
55
54
|
* and fixes tool result pairing.
|
|
56
55
|
*/
|
|
57
56
|
export function normalizeMessagesForAPI(
|
|
58
|
-
messages:
|
|
59
|
-
):
|
|
60
|
-
const normalized:
|
|
57
|
+
messages: Array<{ role: string; content: any }>,
|
|
58
|
+
): Array<{ role: string; content: any }> {
|
|
59
|
+
const normalized: Array<{ role: string; content: any }> = []
|
|
61
60
|
|
|
62
61
|
for (let i = 0; i < messages.length; i++) {
|
|
63
62
|
const msg = messages[i]
|
|
@@ -96,9 +95,9 @@ export function normalizeMessagesForAPI(
|
|
|
96
95
|
* matching tool_use in the previous assistant message.
|
|
97
96
|
*/
|
|
98
97
|
function fixToolResultPairing(
|
|
99
|
-
messages:
|
|
100
|
-
):
|
|
101
|
-
const result:
|
|
98
|
+
messages: Array<{ role: string; content: any }>,
|
|
99
|
+
): Array<{ role: string; content: any }> {
|
|
100
|
+
const result: Array<{ role: string; content: any }> = []
|
|
102
101
|
|
|
103
102
|
for (let i = 0; i < messages.length; i++) {
|
|
104
103
|
const msg = messages[i]
|
|
@@ -145,8 +144,8 @@ function fixToolResultPairing(
|
|
|
145
144
|
* Strip images from messages (for compaction).
|
|
146
145
|
*/
|
|
147
146
|
export function stripImagesFromMessages(
|
|
148
|
-
messages:
|
|
149
|
-
):
|
|
147
|
+
messages: Array<{ role: string; content: any }>,
|
|
148
|
+
): Array<{ role: string; content: any }> {
|
|
150
149
|
return messages.map((msg) => {
|
|
151
150
|
if (typeof msg.content === 'string') return msg
|
|
152
151
|
if (!Array.isArray(msg.content)) return msg
|
|
@@ -166,7 +165,7 @@ export function stripImagesFromMessages(
|
|
|
166
165
|
* Extract text from message content blocks.
|
|
167
166
|
*/
|
|
168
167
|
export function extractTextFromContent(
|
|
169
|
-
content:
|
|
168
|
+
content: any[] | string,
|
|
170
169
|
): string {
|
|
171
170
|
if (typeof content === 'string') return content
|
|
172
171
|
|
|
@@ -179,7 +178,7 @@ export function extractTextFromContent(
|
|
|
179
178
|
/**
|
|
180
179
|
* Create a system message for compact boundary.
|
|
181
180
|
*/
|
|
182
|
-
export function createCompactBoundaryMessage():
|
|
181
|
+
export function createCompactBoundaryMessage(): { role: string; content: string } {
|
|
183
182
|
return {
|
|
184
183
|
role: 'user',
|
|
185
184
|
content: '[Previous context has been summarized above. Continuing conversation.]',
|
package/src/utils/tokens.ts
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* API-based exact counting when available.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import Anthropic from '@anthropic-ai/sdk'
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
9
|
* Rough token estimation: ~4 chars per token (conservative).
|
|
12
10
|
*/
|
|
@@ -18,7 +16,7 @@ export function estimateTokens(text: string): number {
|
|
|
18
16
|
* Estimate tokens for a message array.
|
|
19
17
|
*/
|
|
20
18
|
export function estimateMessagesTokens(
|
|
21
|
-
messages:
|
|
19
|
+
messages: Array<{ role: string; content: any }>,
|
|
22
20
|
): number {
|
|
23
21
|
let total = 0
|
|
24
22
|
for (const msg of messages) {
|
|
@@ -68,15 +66,26 @@ export function getTokenCountFromUsage(usage: {
|
|
|
68
66
|
* Get the context window size for a model.
|
|
69
67
|
*/
|
|
70
68
|
export function getContextWindowSize(model: string): number {
|
|
71
|
-
//
|
|
69
|
+
// Anthropic model context windows
|
|
72
70
|
if (model.includes('opus-4') && model.includes('1m')) return 1_000_000
|
|
73
71
|
if (model.includes('opus-4')) return 200_000
|
|
74
72
|
if (model.includes('sonnet-4')) return 200_000
|
|
75
73
|
if (model.includes('haiku-4')) return 200_000
|
|
76
|
-
|
|
77
|
-
|
|
78
74
|
if (model.includes('claude-3')) return 200_000
|
|
79
75
|
|
|
76
|
+
// OpenAI model context windows
|
|
77
|
+
if (model.includes('gpt-4o')) return 128_000
|
|
78
|
+
if (model.includes('gpt-4-turbo')) return 128_000
|
|
79
|
+
if (model.includes('gpt-4-1')) return 1_000_000
|
|
80
|
+
if (model.includes('gpt-4')) return 128_000
|
|
81
|
+
if (model.includes('gpt-3.5')) return 16_385
|
|
82
|
+
if (model.includes('o1')) return 200_000
|
|
83
|
+
if (model.includes('o3')) return 200_000
|
|
84
|
+
if (model.includes('o4')) return 200_000
|
|
85
|
+
|
|
86
|
+
// DeepSeek models
|
|
87
|
+
if (model.includes('deepseek')) return 128_000
|
|
88
|
+
|
|
80
89
|
// Default
|
|
81
90
|
return 200_000
|
|
82
91
|
}
|
|
@@ -97,6 +106,7 @@ export function getAutoCompactThreshold(model: string): number {
|
|
|
97
106
|
* Model pricing (USD per token).
|
|
98
107
|
*/
|
|
99
108
|
export const MODEL_PRICING: Record<string, { input: number; output: number }> = {
|
|
109
|
+
// Anthropic models
|
|
100
110
|
'claude-opus-4-6': { input: 15 / 1_000_000, output: 75 / 1_000_000 },
|
|
101
111
|
'claude-opus-4-5': { input: 15 / 1_000_000, output: 75 / 1_000_000 },
|
|
102
112
|
'claude-sonnet-4-6': { input: 3 / 1_000_000, output: 15 / 1_000_000 },
|
|
@@ -105,6 +115,19 @@ export const MODEL_PRICING: Record<string, { input: number; output: number }> =
|
|
|
105
115
|
'claude-3-5-sonnet': { input: 3 / 1_000_000, output: 15 / 1_000_000 },
|
|
106
116
|
'claude-3-5-haiku': { input: 0.8 / 1_000_000, output: 4 / 1_000_000 },
|
|
107
117
|
'claude-3-opus': { input: 15 / 1_000_000, output: 75 / 1_000_000 },
|
|
118
|
+
|
|
119
|
+
// OpenAI models
|
|
120
|
+
'gpt-4o': { input: 2.5 / 1_000_000, output: 10 / 1_000_000 },
|
|
121
|
+
'gpt-4o-mini': { input: 0.15 / 1_000_000, output: 0.6 / 1_000_000 },
|
|
122
|
+
'gpt-4-turbo': { input: 10 / 1_000_000, output: 30 / 1_000_000 },
|
|
123
|
+
'gpt-4-1': { input: 2 / 1_000_000, output: 8 / 1_000_000 },
|
|
124
|
+
'o1': { input: 15 / 1_000_000, output: 60 / 1_000_000 },
|
|
125
|
+
'o3': { input: 10 / 1_000_000, output: 40 / 1_000_000 },
|
|
126
|
+
'o4-mini': { input: 1.1 / 1_000_000, output: 4.4 / 1_000_000 },
|
|
127
|
+
|
|
128
|
+
// DeepSeek models
|
|
129
|
+
'deepseek-chat': { input: 0.27 / 1_000_000, output: 1.1 / 1_000_000 },
|
|
130
|
+
'deepseek-reasoner': { input: 0.55 / 1_000_000, output: 2.19 / 1_000_000 },
|
|
108
131
|
}
|
|
109
132
|
|
|
110
133
|
/**
|