@pencil-agent/nano-pencil 2.0.0-beta.2 → 2.0.0-beta.3
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/build-meta.json +3 -3
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-continuations.d.ts +17 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-continuations.js +60 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-stream-events.d.ts +19 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-stream-events.js +55 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-results.d.ts +10 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-results.js +137 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-summaries.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-summaries.js +64 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop.d.ts +26 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop.js +913 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-run-result.d.ts +9 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-run-result.js +32 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent.d.ts +215 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent.js +522 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/errors.d.ts +62 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/errors.js +146 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/index.d.ts +14 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/index.js +19 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/proxy.d.ts +91 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/proxy.js +279 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-agent-loop.d.ts +15 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-agent-loop.js +625 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-streaming-tool-executor.d.ts +33 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-streaming-tool-executor.js +189 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-tool-orchestration.d.ts +35 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-tool-orchestration.js +319 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/types.d.ts +417 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/types.js +13 -0
- package/dist/node_modules/@pencil-agent/agent-core/package.json +28 -0
- package/dist/node_modules/@pencil-agent/ai/dist/api-registry.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/ai/dist/api-registry.js +152 -0
- package/dist/node_modules/@pencil-agent/ai/dist/cli.d.ts +2 -0
- package/dist/node_modules/@pencil-agent/ai/dist/cli.js +121 -0
- package/dist/node_modules/@pencil-agent/ai/dist/config-path.d.ts +1 -0
- package/dist/node_modules/@pencil-agent/ai/dist/config-path.js +17 -0
- package/dist/node_modules/@pencil-agent/ai/dist/debug-logger.d.ts +94 -0
- package/dist/node_modules/@pencil-agent/ai/dist/debug-logger.js +218 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env-api-keys.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env-api-keys.js +107 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env.d.ts +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/events.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/events.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/index.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/ai/dist/index.js +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/json.d.ts +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/json.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.generated.d.ts +15159 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +14928 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.js +60 -0
- package/dist/node_modules/@pencil-agent/ai/dist/overflow.d.ts +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/overflow.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/amazon-bedrock.d.ts +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/amazon-bedrock.js +606 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/anthropic.d.ts +38 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/anthropic.js +737 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/azure-openai-responses.d.ts +21 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/azure-openai-responses.js +193 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/github-copilot-headers.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/github-copilot-headers.js +34 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-gemini-cli.d.ts +79 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-gemini-cli.js +753 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-shared.d.ts +70 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-shared.js +311 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-vertex.d.ts +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-vertex.js +380 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google.d.ts +18 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google.js +360 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-codex-responses.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-codex-responses.js +704 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-completions.d.ts +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-completions.js +870 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses-shared.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses-shared.js +432 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses.d.ts +19 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses.js +207 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/register-builtins.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/register-builtins.js +86 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/simple-options.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/simple-options.js +40 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/transform-messages.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/transform-messages.js +175 -0
- package/dist/node_modules/@pencil-agent/ai/dist/registry.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/registry.js +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/schema.d.ts +10 -0
- package/dist/node_modules/@pencil-agent/ai/dist/schema.js +9 -0
- package/dist/node_modules/@pencil-agent/ai/dist/stream.d.ts +25 -0
- package/dist/node_modules/@pencil-agent/ai/dist/stream.js +324 -0
- package/dist/node_modules/@pencil-agent/ai/dist/types.d.ts +306 -0
- package/dist/node_modules/@pencil-agent/ai/dist/types.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream-types.d.ts +12 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream-types.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream.js +98 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/http-proxy.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/http-proxy.js +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/json-parse.d.ts +14 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/json-parse.js +34 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/anthropic.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/anthropic.js +109 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/decode-credential.d.ts +12 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/decode-credential.js +25 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/github-copilot.d.ts +35 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/github-copilot.js +286 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-antigravity.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-antigravity.js +378 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-gemini-cli.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-gemini-cli.js +483 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/index.d.ts +60 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/index.js +131 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/openai-codex.d.ts +39 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/openai-codex.js +385 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/pkce.d.ts +18 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/pkce.js +36 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/types.d.ts +52 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/types.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/overflow.d.ts +57 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/overflow.js +120 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/sanitize-unicode.d.ts +16 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/sanitize-unicode.js +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/typebox-helpers.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/typebox-helpers.js +26 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/validation.d.ts +23 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/validation.js +78 -0
- package/dist/node_modules/@pencil-agent/ai/package.json +94 -0
- package/dist/node_modules/@pencil-agent/tui/dist/autocomplete.d.ts +62 -0
- package/dist/node_modules/@pencil-agent/tui/dist/autocomplete.js +624 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/box.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/box.js +109 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/cancellable-loader.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/cancellable-loader.js +40 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/editor.d.ts +218 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/editor.js +1697 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/image.d.ts +33 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/image.js +74 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/input.d.ts +42 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/input.js +438 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/loader.d.ts +26 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/loader.js +54 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/markdown.d.ts +100 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/markdown.js +634 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/select-list.d.ts +37 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/select-list.js +157 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/settings-list.d.ts +55 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/settings-list.js +190 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/spacer.d.ts +17 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/spacer.js +28 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/text.d.ts +24 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/text.js +94 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/truncated-text.d.ts +18 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/truncated-text.js +56 -0
- package/dist/node_modules/@pencil-agent/tui/dist/editor-component.d.ts +51 -0
- package/dist/node_modules/@pencil-agent/tui/dist/editor-component.js +7 -0
- package/dist/node_modules/@pencil-agent/tui/dist/fuzzy.d.ts +32 -0
- package/dist/node_modules/@pencil-agent/tui/dist/fuzzy.js +152 -0
- package/dist/node_modules/@pencil-agent/tui/dist/index.d.ts +28 -0
- package/dist/node_modules/@pencil-agent/tui/dist/index.js +37 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keybindings.d.ts +44 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keybindings.js +119 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keys.d.ts +149 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keys.js +948 -0
- package/dist/node_modules/@pencil-agent/tui/dist/kill-ring.d.ts +33 -0
- package/dist/node_modules/@pencil-agent/tui/dist/kill-ring.js +49 -0
- package/dist/node_modules/@pencil-agent/tui/dist/stdin-buffer.d.ts +38 -0
- package/dist/node_modules/@pencil-agent/tui/dist/stdin-buffer.js +307 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal-image.d.ts +73 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal-image.js +287 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal.d.ts +86 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal.js +266 -0
- package/dist/node_modules/@pencil-agent/tui/dist/tui.d.ts +219 -0
- package/dist/node_modules/@pencil-agent/tui/dist/tui.js +1001 -0
- package/dist/node_modules/@pencil-agent/tui/dist/undo-stack.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/tui/dist/undo-stack.js +30 -0
- package/dist/node_modules/@pencil-agent/tui/dist/utils.d.ts +83 -0
- package/dist/node_modules/@pencil-agent/tui/dist/utils.js +811 -0
- package/dist/node_modules/@pencil-agent/tui/package.json +37 -0
- package/package.json +3 -2
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: AgentLoopFramework, AgentLoopTransition, AgentRunResult with transition history, AgentModelErrorRecoveryResult, AgentLoopConfig, CustomAgentMessages, AgentState, AgentToolResult, AgentTool, AgentToolConcurrencySafety, AgentToolInterruptBehavior
|
|
3
|
+
* [FROM]: No external dependencies
|
|
4
|
+
* [TO]: Consumed by core/lib/agent-core/src/index.ts
|
|
5
|
+
* [HERE]: core/lib/agent-core/src/types.ts -
|
|
6
|
+
*/
|
|
7
|
+
import type { AssistantMessageEvent, ImageContent, Message, Model, SimpleStreamOptions, TextContent, Tool, ToolResultMessage, Usage } from "@pencil-agent/ai/types";
|
|
8
|
+
import type { streamSimple } from "@pencil-agent/ai/stream";
|
|
9
|
+
import type { Static, TSchema } from "@pencil-agent/ai/schema";
|
|
10
|
+
/** Stream function - can return sync or Promise for async config lookup */
|
|
11
|
+
export type StreamFn = (...args: Parameters<typeof streamSimple>) => ReturnType<typeof streamSimple> | Promise<ReturnType<typeof streamSimple>>;
|
|
12
|
+
export type AgentLoopFramework = "standard" | "weak-model-compatible";
|
|
13
|
+
export type AgentLoopFrameworkInput = AgentLoopFramework | "high-intelligence" | "low-intelligence" | "structured-adaptive";
|
|
14
|
+
export type AgentLoopTransition = {
|
|
15
|
+
reason: "start";
|
|
16
|
+
} | {
|
|
17
|
+
reason: "tool_result";
|
|
18
|
+
toolCallCount: number;
|
|
19
|
+
} | {
|
|
20
|
+
reason: "follow_up";
|
|
21
|
+
} | {
|
|
22
|
+
reason: "max_turns_reached";
|
|
23
|
+
maxTurns: number;
|
|
24
|
+
turnCount: number;
|
|
25
|
+
} | {
|
|
26
|
+
reason: "tool_call_limit_reached";
|
|
27
|
+
maxToolCalls: number;
|
|
28
|
+
requestedToolCalls: number;
|
|
29
|
+
toolCallCount: number;
|
|
30
|
+
} | {
|
|
31
|
+
reason: "stop_hook_limit_reached";
|
|
32
|
+
maxContinuations: number;
|
|
33
|
+
continuationCount: number;
|
|
34
|
+
} | {
|
|
35
|
+
reason: "max_output_tokens_recovery";
|
|
36
|
+
attempt: number;
|
|
37
|
+
} | {
|
|
38
|
+
reason: "stop_hook_blocking";
|
|
39
|
+
continuationCount: number;
|
|
40
|
+
} | {
|
|
41
|
+
reason: "model_error_recovery";
|
|
42
|
+
subtype: string;
|
|
43
|
+
attempt: number;
|
|
44
|
+
} | {
|
|
45
|
+
reason: "token_budget_continuation";
|
|
46
|
+
continuationCount: number;
|
|
47
|
+
outputTokens: number;
|
|
48
|
+
targetTokens: number;
|
|
49
|
+
};
|
|
50
|
+
export interface AgentRunResult {
|
|
51
|
+
stopReason: string;
|
|
52
|
+
loopFramework?: AgentLoopFramework;
|
|
53
|
+
loopPolicy?: AgentRunPolicy;
|
|
54
|
+
turnCount: number;
|
|
55
|
+
toolCallCount: number;
|
|
56
|
+
durationMs: number;
|
|
57
|
+
usage?: Usage;
|
|
58
|
+
permissionDenialCount?: number;
|
|
59
|
+
permissionDenials?: AgentToolPermissionDenial[];
|
|
60
|
+
transitions?: AgentLoopTransition[];
|
|
61
|
+
lastTransition?: AgentLoopTransition;
|
|
62
|
+
errorMessage?: string;
|
|
63
|
+
errorSubtype?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface AgentRunPolicy {
|
|
66
|
+
maxModelErrorRecoveryAttempts?: number;
|
|
67
|
+
maxOutputTokenRecoveryAttempts?: number;
|
|
68
|
+
outputTokenBudget?: {
|
|
69
|
+
targetTokens: number;
|
|
70
|
+
thresholdPct?: number;
|
|
71
|
+
maxContinuations?: number;
|
|
72
|
+
};
|
|
73
|
+
maxStopHookContinuations?: number;
|
|
74
|
+
maxToolConcurrency?: number;
|
|
75
|
+
maxToolResultBatchSizeChars?: number;
|
|
76
|
+
maxTurnsPerPrompt?: number;
|
|
77
|
+
maxToolCallsPerPrompt?: number;
|
|
78
|
+
}
|
|
79
|
+
export declare function normalizeAgentLoopFramework(value: AgentLoopFrameworkInput | undefined): AgentLoopFramework | undefined;
|
|
80
|
+
export type AgentToolPermissionDecision = {
|
|
81
|
+
decision: "allow";
|
|
82
|
+
} | {
|
|
83
|
+
decision: "deny";
|
|
84
|
+
reason?: string;
|
|
85
|
+
};
|
|
86
|
+
export interface AgentToolPermissionDenial {
|
|
87
|
+
toolCallId: string;
|
|
88
|
+
toolName: string;
|
|
89
|
+
reason?: string;
|
|
90
|
+
}
|
|
91
|
+
export type AgentModelErrorRecoveryResult = {
|
|
92
|
+
action: "stop";
|
|
93
|
+
} | {
|
|
94
|
+
action: "retry";
|
|
95
|
+
messages: AgentMessage[];
|
|
96
|
+
transition?: AgentLoopTransition;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Configuration for the agent loop.
|
|
100
|
+
*/
|
|
101
|
+
export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
102
|
+
model: Model<any>;
|
|
103
|
+
loopFramework?: AgentLoopFrameworkInput;
|
|
104
|
+
/**
|
|
105
|
+
* Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.
|
|
106
|
+
*
|
|
107
|
+
* Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage
|
|
108
|
+
* that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,
|
|
109
|
+
* status messages) should be filtered out.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* convertToLlm: (messages) => messages.flatMap(m => {
|
|
114
|
+
* if (m.role === "custom") {
|
|
115
|
+
* // Convert custom message to user message
|
|
116
|
+
* return [{ role: "user", content: m.content, timestamp: m.timestamp }];
|
|
117
|
+
* }
|
|
118
|
+
* if (m.role === "notification") {
|
|
119
|
+
* // Filter out UI-only messages
|
|
120
|
+
* return [];
|
|
121
|
+
* }
|
|
122
|
+
* // Pass through standard LLM messages
|
|
123
|
+
* return [m];
|
|
124
|
+
* })
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Optional transform applied to the context before `convertToLlm`.
|
|
130
|
+
*
|
|
131
|
+
* Use this for operations that work at the AgentMessage level:
|
|
132
|
+
* - Context window management (pruning old messages)
|
|
133
|
+
* - Injecting context from external sources
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* transformContext: async (messages) => {
|
|
138
|
+
* if (estimateTokens(messages) > MAX_TOKENS) {
|
|
139
|
+
* return pruneOldMessages(messages);
|
|
140
|
+
* }
|
|
141
|
+
* return messages;
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;
|
|
146
|
+
/**
|
|
147
|
+
* Resolves an API key dynamically for each LLM call.
|
|
148
|
+
*
|
|
149
|
+
* Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire
|
|
150
|
+
* during long-running tool execution phases.
|
|
151
|
+
*/
|
|
152
|
+
getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
|
|
153
|
+
/**
|
|
154
|
+
* Returns steering messages to inject into the conversation mid-run.
|
|
155
|
+
*
|
|
156
|
+
* Called after each tool execution to check for user interruptions.
|
|
157
|
+
* If messages are returned, remaining tool calls are skipped and
|
|
158
|
+
* these messages are added to the context before the next LLM call.
|
|
159
|
+
*
|
|
160
|
+
* Use this for "steering" the agent while it's working.
|
|
161
|
+
*/
|
|
162
|
+
getSteeringMessages?: () => Promise<AgentMessage[]>;
|
|
163
|
+
/**
|
|
164
|
+
* Returns follow-up messages to process after the agent would otherwise stop.
|
|
165
|
+
*
|
|
166
|
+
* Called when the agent has no more tool calls and no steering messages.
|
|
167
|
+
* If messages are returned, they're added to the context and the agent
|
|
168
|
+
* continues with another turn.
|
|
169
|
+
*
|
|
170
|
+
* Use this for follow-up messages that should wait until the agent finishes.
|
|
171
|
+
*/
|
|
172
|
+
getFollowUpMessages?: () => Promise<AgentMessage[]>;
|
|
173
|
+
/**
|
|
174
|
+
* Optional tool permission gate for agent loop tool execution.
|
|
175
|
+
*
|
|
176
|
+
* Called after schema/custom validation and before the tool executes.
|
|
177
|
+
* Return "deny" to feed a permission-denied tool_result back to the model
|
|
178
|
+
* without crashing the loop.
|
|
179
|
+
*/
|
|
180
|
+
canUseTool?: (event: {
|
|
181
|
+
toolCallId: string;
|
|
182
|
+
toolName: string;
|
|
183
|
+
requestedToolName: string;
|
|
184
|
+
input: unknown;
|
|
185
|
+
rawInput: unknown;
|
|
186
|
+
tool: AgentTool<any>;
|
|
187
|
+
}) => Promise<AgentToolPermissionDecision> | AgentToolPermissionDecision;
|
|
188
|
+
/**
|
|
189
|
+
* Optional in-loop model error recovery hook.
|
|
190
|
+
*
|
|
191
|
+
* Hosts can use this to recover from context overflow or transient provider
|
|
192
|
+
* errors before the loop reaches agent_end. Return retry with a replacement
|
|
193
|
+
* AgentMessage context to make the next model call use recovered state.
|
|
194
|
+
*/
|
|
195
|
+
recoverModelError?: (event: {
|
|
196
|
+
message: AgentMessage;
|
|
197
|
+
messages: AgentMessage[];
|
|
198
|
+
errorSubtype: string;
|
|
199
|
+
attempt: number;
|
|
200
|
+
}) => Promise<AgentModelErrorRecoveryResult> | AgentModelErrorRecoveryResult;
|
|
201
|
+
/**
|
|
202
|
+
* Optional non-blocking summary for completed tool batches.
|
|
203
|
+
*
|
|
204
|
+
* The loop starts this after a tool batch finishes. If the summary is
|
|
205
|
+
* already settled at the start of a later turn, it is emitted as a normal
|
|
206
|
+
* AgentMessage and included in that model request. If it is still pending,
|
|
207
|
+
* the model request proceeds without waiting.
|
|
208
|
+
*/
|
|
209
|
+
createToolUseSummary?: (event: {
|
|
210
|
+
assistantMessage: AgentMessage;
|
|
211
|
+
toolResults: ToolResultMessage[];
|
|
212
|
+
contextMessages: AgentMessage[];
|
|
213
|
+
messages: AgentMessage[];
|
|
214
|
+
}) => AgentMessage | undefined | Promise<AgentMessage | undefined>;
|
|
215
|
+
/**
|
|
216
|
+
* Maximum model-error recovery retries for one prompt. Defaults to 1.
|
|
217
|
+
*/
|
|
218
|
+
maxModelErrorRecoveryAttempts?: number;
|
|
219
|
+
/**
|
|
220
|
+
* Maximum assistant turns allowed for one prompt/continue loop.
|
|
221
|
+
*
|
|
222
|
+
* Prevents runaway model/tool/follow-up cycles from consuming unbounded
|
|
223
|
+
* tokens when the model keeps asking for tools or queued messages keep the
|
|
224
|
+
* loop alive.
|
|
225
|
+
*/
|
|
226
|
+
maxTurnsPerPrompt?: number;
|
|
227
|
+
/**
|
|
228
|
+
* Maximum tool calls allowed for one prompt/continue loop.
|
|
229
|
+
*
|
|
230
|
+
* The loop stops before executing a batch that would exceed this limit and
|
|
231
|
+
* emits a controlled assistant error message.
|
|
232
|
+
*/
|
|
233
|
+
maxToolCallsPerPrompt?: number;
|
|
234
|
+
/**
|
|
235
|
+
* Maximum concurrency for one batch of concurrency-safe tool calls in the
|
|
236
|
+
* weak-model-compatible loop. Defaults to 10.
|
|
237
|
+
*/
|
|
238
|
+
maxToolConcurrency?: number;
|
|
239
|
+
/**
|
|
240
|
+
* Maximum combined text characters from one assistant tool-use batch that
|
|
241
|
+
* may be fed into the next model request.
|
|
242
|
+
*
|
|
243
|
+
* This aggregate guard complements per-tool maxResultSizeChars: it prevents
|
|
244
|
+
* several individually valid read/search results from flooding the next
|
|
245
|
+
* context turn. Loops preserve tool_result order and trim the largest
|
|
246
|
+
* successful results first.
|
|
247
|
+
*/
|
|
248
|
+
maxToolResultBatchSizeChars?: number;
|
|
249
|
+
/**
|
|
250
|
+
* Maximum automatic continuations after a model stops because it hit its
|
|
251
|
+
* output-token limit. Defaults to 1.
|
|
252
|
+
*/
|
|
253
|
+
maxOutputTokenRecoveryAttempts?: number;
|
|
254
|
+
/**
|
|
255
|
+
* Optional output-token budget target for long-form agent turns.
|
|
256
|
+
*
|
|
257
|
+
* If the assistant stops naturally before cumulative output reaches
|
|
258
|
+
* thresholdPct * targetTokens, the loop injects a meta continuation prompt.
|
|
259
|
+
* This is distinct from max-output-token recovery: it handles
|
|
260
|
+
* under-complete answers, not hard length stops.
|
|
261
|
+
*/
|
|
262
|
+
outputTokenBudget?: {
|
|
263
|
+
targetTokens: number;
|
|
264
|
+
thresholdPct?: number;
|
|
265
|
+
maxContinuations?: number;
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Optional stop hook. Called when the assistant would stop without tool
|
|
269
|
+
* calls. Return action "continue" with messages to force a
|
|
270
|
+
* correction/validation turn; return "stop" to allow completion.
|
|
271
|
+
*/
|
|
272
|
+
runStopHooks?: (event: {
|
|
273
|
+
message: AgentMessage;
|
|
274
|
+
messages: AgentMessage[];
|
|
275
|
+
}) => Promise<StructuredAdaptiveStopHookResult> | StructuredAdaptiveStopHookResult;
|
|
276
|
+
/**
|
|
277
|
+
* Maximum stop-hook continuation turns for one prompt. Defaults to 3.
|
|
278
|
+
*/
|
|
279
|
+
maxStopHookContinuations?: number;
|
|
280
|
+
}
|
|
281
|
+
export type StructuredAdaptiveStopHookResult = {
|
|
282
|
+
action: "stop";
|
|
283
|
+
} | {
|
|
284
|
+
action: "continue";
|
|
285
|
+
messages: AgentMessage[];
|
|
286
|
+
reason?: string;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Thinking/reasoning level for models that support it.
|
|
290
|
+
* Note: "xhigh" is only supported by OpenAI gpt-5.1-codex-max, gpt-5.2, gpt-5.2-codex, gpt-5.3, and gpt-5.3-codex models.
|
|
291
|
+
*/
|
|
292
|
+
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
293
|
+
/**
|
|
294
|
+
* Extensible interface for custom app messages.
|
|
295
|
+
* Apps can extend via declaration merging:
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```typescript
|
|
299
|
+
* declare module "@pencil-agent/agent-core" {
|
|
300
|
+
* interface CustomAgentMessages {
|
|
301
|
+
* artifact: ArtifactMessage;
|
|
302
|
+
* notification: NotificationMessage;
|
|
303
|
+
* }
|
|
304
|
+
* }
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
export interface CustomAgentMessages {
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* AgentMessage: Union of LLM messages + custom messages.
|
|
311
|
+
* This abstraction allows apps to add custom message types while maintaining
|
|
312
|
+
* type safety and compatibility with the base LLM messages.
|
|
313
|
+
*/
|
|
314
|
+
export type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];
|
|
315
|
+
/**
|
|
316
|
+
* Agent state containing all configuration and conversation data.
|
|
317
|
+
*/
|
|
318
|
+
export interface AgentState {
|
|
319
|
+
systemPrompt: string;
|
|
320
|
+
model: Model<any>;
|
|
321
|
+
thinkingLevel: ThinkingLevel;
|
|
322
|
+
tools: AgentTool<any>[];
|
|
323
|
+
messages: AgentMessage[];
|
|
324
|
+
isStreaming: boolean;
|
|
325
|
+
streamMessage: AgentMessage | null;
|
|
326
|
+
pendingToolCalls: Set<string>;
|
|
327
|
+
lastResult?: AgentRunResult;
|
|
328
|
+
error?: string;
|
|
329
|
+
}
|
|
330
|
+
export interface AgentToolResult<T> {
|
|
331
|
+
content: (TextContent | ImageContent)[];
|
|
332
|
+
details: T;
|
|
333
|
+
/**
|
|
334
|
+
* Optional messages appended after the corresponding tool_result messages.
|
|
335
|
+
* Use this for tool-generated attachments or context updates that should not
|
|
336
|
+
* interrupt assistant tool_use -> tool_result pairing.
|
|
337
|
+
*/
|
|
338
|
+
contextMessages?: AgentMessage[];
|
|
339
|
+
}
|
|
340
|
+
export type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;
|
|
341
|
+
export type AgentToolConcurrencySafety<TParameters extends TSchema = TSchema> = boolean | ((params: Static<TParameters>) => boolean);
|
|
342
|
+
export type AgentToolInterruptBehavior<TParameters extends TSchema = TSchema> = "cancel" | "block" | ((params: Static<TParameters>) => "cancel" | "block");
|
|
343
|
+
export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {
|
|
344
|
+
label: string;
|
|
345
|
+
/** Alternative model-facing names accepted for transcript/tool compatibility. */
|
|
346
|
+
aliases?: string[];
|
|
347
|
+
/**
|
|
348
|
+
* Whether the tool can safely run alongside other concurrency-safe tools.
|
|
349
|
+
* The weak-model-compatible loop uses this to batch read-only work while keeping
|
|
350
|
+
* stateful tools such as edit/write/bash serialized.
|
|
351
|
+
*/
|
|
352
|
+
isConcurrencySafe?: AgentToolConcurrencySafety<TParameters>;
|
|
353
|
+
interruptBehavior?: AgentToolInterruptBehavior<TParameters>;
|
|
354
|
+
/** Optional semantic validation after schema validation and before execute. */
|
|
355
|
+
validateInput?: (params: Static<TParameters>) => void | string | Promise<void | string>;
|
|
356
|
+
/** Optional maximum text result size enforced by agent loop tool orchestration. */
|
|
357
|
+
maxResultSizeChars?: number;
|
|
358
|
+
execute: (toolCallId: string, params: Static<TParameters>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<TDetails>) => Promise<AgentToolResult<TDetails>>;
|
|
359
|
+
}
|
|
360
|
+
export interface AgentContext {
|
|
361
|
+
systemPrompt: string;
|
|
362
|
+
messages: AgentMessage[];
|
|
363
|
+
tools?: AgentTool<any>[];
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Events emitted by the Agent for UI updates.
|
|
367
|
+
* These events provide fine-grained lifecycle information for messages, turns, and tool executions.
|
|
368
|
+
*/
|
|
369
|
+
export type AgentEvent = {
|
|
370
|
+
type: "agent_start";
|
|
371
|
+
} | {
|
|
372
|
+
type: "agent_end";
|
|
373
|
+
messages: AgentMessage[];
|
|
374
|
+
} | ({
|
|
375
|
+
type: "agent_result";
|
|
376
|
+
} & AgentRunResult) | {
|
|
377
|
+
type: "stream_request_start";
|
|
378
|
+
model: string;
|
|
379
|
+
provider: string;
|
|
380
|
+
api: string;
|
|
381
|
+
messageCount: number;
|
|
382
|
+
maxTokens?: number;
|
|
383
|
+
} | {
|
|
384
|
+
type: "turn_start";
|
|
385
|
+
} | {
|
|
386
|
+
type: "turn_end";
|
|
387
|
+
message: AgentMessage;
|
|
388
|
+
toolResults: ToolResultMessage[];
|
|
389
|
+
} | {
|
|
390
|
+
type: "message_start";
|
|
391
|
+
message: AgentMessage;
|
|
392
|
+
} | {
|
|
393
|
+
type: "message_update";
|
|
394
|
+
message: AgentMessage;
|
|
395
|
+
assistantMessageEvent: AssistantMessageEvent;
|
|
396
|
+
} | {
|
|
397
|
+
type: "message_end";
|
|
398
|
+
message: AgentMessage;
|
|
399
|
+
} | {
|
|
400
|
+
type: "tool_execution_start";
|
|
401
|
+
toolCallId: string;
|
|
402
|
+
toolName: string;
|
|
403
|
+
args: any;
|
|
404
|
+
} | {
|
|
405
|
+
type: "tool_execution_update";
|
|
406
|
+
toolCallId: string;
|
|
407
|
+
toolName: string;
|
|
408
|
+
args: any;
|
|
409
|
+
partialResult: any;
|
|
410
|
+
} | {
|
|
411
|
+
type: "tool_execution_end";
|
|
412
|
+
toolCallId: string;
|
|
413
|
+
toolName: string;
|
|
414
|
+
result: any;
|
|
415
|
+
isError: boolean;
|
|
416
|
+
durationMs?: number;
|
|
417
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: AgentLoopFramework, AgentLoopTransition, AgentRunResult with transition history, AgentModelErrorRecoveryResult, AgentLoopConfig, CustomAgentMessages, AgentState, AgentToolResult, AgentTool, AgentToolConcurrencySafety, AgentToolInterruptBehavior
|
|
3
|
+
* [FROM]: No external dependencies
|
|
4
|
+
* [TO]: Consumed by core/lib/agent-core/src/index.ts
|
|
5
|
+
* [HERE]: core/lib/agent-core/src/types.ts -
|
|
6
|
+
*/
|
|
7
|
+
export function normalizeAgentLoopFramework(value) {
|
|
8
|
+
if (value === "high-intelligence")
|
|
9
|
+
return "standard";
|
|
10
|
+
if (value === "low-intelligence" || value === "structured-adaptive")
|
|
11
|
+
return "weak-model-compatible";
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pencil-agent/agent-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@pencil-agent/ai": "0.0.1"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ai",
|
|
13
|
+
"agent",
|
|
14
|
+
"llm",
|
|
15
|
+
"transport",
|
|
16
|
+
"state-management",
|
|
17
|
+
"pencil-agent"
|
|
18
|
+
],
|
|
19
|
+
"author": "nanoPencil Team",
|
|
20
|
+
"license": "GPL-3.0",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/O-Pencil/nanoPencil.git"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: ApiProvider, registerApiProvider, registerApiProviderLoader, getApiProvider, ensureApiProvider, reset helpers
|
|
3
|
+
* [FROM]: Depends on core AI stream/model types for provider contracts
|
|
4
|
+
* [TO]: Consumed by core/lib/ai/src/stream.ts and providers/register-builtins.ts for provider dispatch
|
|
5
|
+
* [HERE]: core/lib/ai/src/api-registry.ts - provider registry plus runtime lazy-loader registry
|
|
6
|
+
*/
|
|
7
|
+
import type { Api, AssistantMessageEventStreamContract, Context, Model, SimpleStreamOptions, StreamFunction, StreamOptions } from "./types.js";
|
|
8
|
+
export type ApiStreamFunction = (model: Model<Api>, context: Context, options?: StreamOptions) => AssistantMessageEventStreamContract;
|
|
9
|
+
export type ApiStreamSimpleFunction = (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStreamContract;
|
|
10
|
+
export interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {
|
|
11
|
+
api: TApi;
|
|
12
|
+
stream: StreamFunction<TApi, TOptions>;
|
|
13
|
+
streamSimple: StreamFunction<TApi, SimpleStreamOptions>;
|
|
14
|
+
}
|
|
15
|
+
export interface ApiProviderInternal {
|
|
16
|
+
api: Api;
|
|
17
|
+
stream: ApiStreamFunction;
|
|
18
|
+
streamSimple: ApiStreamSimpleFunction;
|
|
19
|
+
}
|
|
20
|
+
export type ApiProviderLoader<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> = () => Promise<ApiProvider<TApi, TOptions>>;
|
|
21
|
+
export declare function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(provider: ApiProvider<TApi, TOptions>, sourceId?: string): void;
|
|
22
|
+
export declare function registerApiProviderLoader<TApi extends Api, TOptions extends StreamOptions>(api: TApi, loader: ApiProviderLoader<TApi, TOptions>, sourceId?: string): void;
|
|
23
|
+
export declare function getApiProvider(api: Api): ApiProviderInternal | undefined;
|
|
24
|
+
export declare function ensureApiProvider(api: Api): Promise<ApiProviderInternal | undefined>;
|
|
25
|
+
export declare function getApiProviders(): ApiProviderInternal[];
|
|
26
|
+
export declare function unregisterApiProviders(sourceId: string): void;
|
|
27
|
+
export declare function clearApiProviders(): void;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: ApiProvider, registerApiProvider, registerApiProviderLoader, getApiProvider, ensureApiProvider, reset helpers
|
|
3
|
+
* [FROM]: Depends on core AI stream/model types for provider contracts
|
|
4
|
+
* [TO]: Consumed by core/lib/ai/src/stream.ts and providers/register-builtins.ts for provider dispatch
|
|
5
|
+
* [HERE]: core/lib/ai/src/api-registry.ts - provider registry plus runtime lazy-loader registry
|
|
6
|
+
*/
|
|
7
|
+
import { AssistantMessageEventStream } from "./utils/event-stream.js";
|
|
8
|
+
const apiProviderRegistry = new Map();
|
|
9
|
+
const apiProviderLoaders = new Map();
|
|
10
|
+
function wrapStream(api, stream) {
|
|
11
|
+
return (model, context, options) => {
|
|
12
|
+
if (model.api !== api) {
|
|
13
|
+
throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
14
|
+
}
|
|
15
|
+
return stream(model, context, options);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function wrapStreamSimple(api, streamSimple) {
|
|
19
|
+
return (model, context, options) => {
|
|
20
|
+
if (model.api !== api) {
|
|
21
|
+
throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
22
|
+
}
|
|
23
|
+
return streamSimple(model, context, options);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function emptyUsage() {
|
|
27
|
+
return {
|
|
28
|
+
input: 0,
|
|
29
|
+
output: 0,
|
|
30
|
+
cacheRead: 0,
|
|
31
|
+
cacheWrite: 0,
|
|
32
|
+
totalTokens: 0,
|
|
33
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function getErrorMessage(error) {
|
|
37
|
+
if (error instanceof Error)
|
|
38
|
+
return error.message;
|
|
39
|
+
if (typeof error === "string")
|
|
40
|
+
return error;
|
|
41
|
+
return JSON.stringify(error) ?? String(error);
|
|
42
|
+
}
|
|
43
|
+
function createProviderLoadErrorMessage(model, error) {
|
|
44
|
+
return {
|
|
45
|
+
role: "assistant",
|
|
46
|
+
content: [],
|
|
47
|
+
api: model.api,
|
|
48
|
+
provider: model.provider,
|
|
49
|
+
model: model.id,
|
|
50
|
+
stopReason: "error",
|
|
51
|
+
errorMessage: getErrorMessage(error),
|
|
52
|
+
usage: emptyUsage(),
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function createMissingProviderResultMessage(model) {
|
|
57
|
+
return createProviderLoadErrorMessage(model, new Error("Provider stream ended without a final assistant message"));
|
|
58
|
+
}
|
|
59
|
+
function createLazyProvider(api) {
|
|
60
|
+
return {
|
|
61
|
+
api,
|
|
62
|
+
stream: (model, context, options) => {
|
|
63
|
+
const stream = new AssistantMessageEventStream();
|
|
64
|
+
void pipeLazyProviderStream(api, model, context, options, stream, "stream");
|
|
65
|
+
return stream;
|
|
66
|
+
},
|
|
67
|
+
streamSimple: (model, context, options) => {
|
|
68
|
+
const stream = new AssistantMessageEventStream();
|
|
69
|
+
void pipeLazyProviderStream(api, model, context, options, stream, "streamSimple");
|
|
70
|
+
return stream;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function pipeLazyProviderStream(api, model, context, options, output, method) {
|
|
75
|
+
try {
|
|
76
|
+
const provider = await ensureApiProvider(api);
|
|
77
|
+
if (!provider) {
|
|
78
|
+
throw new Error(`No API provider registered for api: ${api}`);
|
|
79
|
+
}
|
|
80
|
+
const source = method === "stream"
|
|
81
|
+
? provider.stream(model, context, options)
|
|
82
|
+
: provider.streamSimple(model, context, options);
|
|
83
|
+
for await (const event of source) {
|
|
84
|
+
output.push(event);
|
|
85
|
+
}
|
|
86
|
+
if (!source.resultIfResolved()) {
|
|
87
|
+
output.push({ type: "error", reason: "error", error: createMissingProviderResultMessage(model) });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
output.push({ type: "error", reason: "error", error: createProviderLoadErrorMessage(model, error) });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function registerApiProvider(provider, sourceId) {
|
|
95
|
+
apiProviderRegistry.set(provider.api, {
|
|
96
|
+
provider: {
|
|
97
|
+
api: provider.api,
|
|
98
|
+
stream: wrapStream(provider.api, provider.stream),
|
|
99
|
+
streamSimple: wrapStreamSimple(provider.api, provider.streamSimple),
|
|
100
|
+
},
|
|
101
|
+
sourceId,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
export function registerApiProviderLoader(api, loader, sourceId) {
|
|
105
|
+
apiProviderLoaders.set(api, { loader, sourceId });
|
|
106
|
+
apiProviderRegistry.set(api, {
|
|
107
|
+
provider: createLazyProvider(api),
|
|
108
|
+
sourceId,
|
|
109
|
+
lazy: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export function getApiProvider(api) {
|
|
113
|
+
return apiProviderRegistry.get(api)?.provider;
|
|
114
|
+
}
|
|
115
|
+
export async function ensureApiProvider(api) {
|
|
116
|
+
const entry = apiProviderRegistry.get(api);
|
|
117
|
+
if (entry && !entry.lazy)
|
|
118
|
+
return entry.provider;
|
|
119
|
+
const loader = apiProviderLoaders.get(api);
|
|
120
|
+
if (!loader)
|
|
121
|
+
return entry?.provider;
|
|
122
|
+
loader.pending ??= loader
|
|
123
|
+
.loader()
|
|
124
|
+
.then((provider) => {
|
|
125
|
+
registerApiProvider(provider, loader.sourceId);
|
|
126
|
+
return getApiProvider(api);
|
|
127
|
+
})
|
|
128
|
+
.finally(() => {
|
|
129
|
+
loader.pending = undefined;
|
|
130
|
+
});
|
|
131
|
+
await loader.pending;
|
|
132
|
+
return getApiProvider(api);
|
|
133
|
+
}
|
|
134
|
+
export function getApiProviders() {
|
|
135
|
+
return Array.from(apiProviderRegistry.values(), (entry) => entry.provider);
|
|
136
|
+
}
|
|
137
|
+
export function unregisterApiProviders(sourceId) {
|
|
138
|
+
for (const [api, entry] of apiProviderRegistry.entries()) {
|
|
139
|
+
if (entry.sourceId === sourceId) {
|
|
140
|
+
apiProviderRegistry.delete(api);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const [api, entry] of apiProviderLoaders.entries()) {
|
|
144
|
+
if (entry.sourceId === sourceId) {
|
|
145
|
+
apiProviderLoaders.delete(api);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function clearApiProviders() {
|
|
150
|
+
apiProviderRegistry.clear();
|
|
151
|
+
apiProviderLoaders.clear();
|
|
152
|
+
}
|