@link-assistant/agent 0.26.6 → 0.26.7
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/package.json +67 -61
- package/src/agent/agent.ts +2 -2
- package/src/cli/input-queue.js +2 -1
- package/src/cli/model-config.js +4 -0
- package/src/provider/cache.ts +57 -16
- package/src/provider/echo.ts +37 -18
- package/src/provider/opencode-zen.ts +1 -2
- package/src/provider/provider.ts +13 -5
- package/src/session/compaction.ts +1 -1
- package/src/session/processor.ts +1 -1
- package/src/session/prompt.ts +7 -4
- package/src/session/summary.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/agent",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.7",
|
|
4
4
|
"description": "A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface. Bun-only runtime.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -50,76 +50,77 @@
|
|
|
50
50
|
"LICENSE"
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@agentclientprotocol/sdk": "^
|
|
54
|
-
"@ai-sdk/amazon-bedrock": "^
|
|
55
|
-
"@ai-sdk/anthropic": "^
|
|
56
|
-
"@ai-sdk/azure": "^
|
|
57
|
-
"@ai-sdk/google": "^
|
|
58
|
-
"@ai-sdk/google-vertex": "^
|
|
59
|
-
"@ai-sdk/groq": "^
|
|
60
|
-
"@ai-sdk/mcp": "^
|
|
61
|
-
"@ai-sdk/mistral": "^
|
|
62
|
-
"@ai-sdk/openai": "^
|
|
63
|
-
"@ai-sdk/openai-compatible": "^
|
|
64
|
-
"@ai-sdk/
|
|
65
|
-
"@
|
|
66
|
-
"@
|
|
67
|
-
"@hono/
|
|
68
|
-
"@
|
|
69
|
-
"@
|
|
53
|
+
"@agentclientprotocol/sdk": "^1.5.1",
|
|
54
|
+
"@ai-sdk/amazon-bedrock": "^5.0.101",
|
|
55
|
+
"@ai-sdk/anthropic": "^4.0.68",
|
|
56
|
+
"@ai-sdk/azure": "^4.0.85",
|
|
57
|
+
"@ai-sdk/google": "^4.0.85",
|
|
58
|
+
"@ai-sdk/google-vertex": "^5.0.98",
|
|
59
|
+
"@ai-sdk/groq": "^4.0.52",
|
|
60
|
+
"@ai-sdk/mcp": "^2.0.62",
|
|
61
|
+
"@ai-sdk/mistral": "^4.0.54",
|
|
62
|
+
"@ai-sdk/openai": "^4.0.81",
|
|
63
|
+
"@ai-sdk/openai-compatible": "^3.0.59",
|
|
64
|
+
"@ai-sdk/provider": "^4.0.19",
|
|
65
|
+
"@ai-sdk/xai": "^5.0.12",
|
|
66
|
+
"@clack/prompts": "^1.8.1",
|
|
67
|
+
"@hono/standard-validator": "^0.4.0",
|
|
68
|
+
"@hono/zod-validator": "^0.9.1",
|
|
69
|
+
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
70
|
+
"@octokit/graphql": "^9.0.5",
|
|
70
71
|
"@octokit/rest": "^22.0.1",
|
|
71
72
|
"@openauthjs/openauth": "^0.4.3",
|
|
72
|
-
"@opencode-ai/sdk": "^1.
|
|
73
|
-
"@openrouter/ai-sdk-provider": "^
|
|
74
|
-
"@opentui/core": "0.
|
|
75
|
-
"@opentui/solid": "0.
|
|
76
|
-
"@parcel/watcher": "^2.
|
|
77
|
-
"@solid-primitives/event-bus": "^1.1.
|
|
78
|
-
"@standard-community/standard-json": "^0.3.
|
|
79
|
-
"@standard-community/standard-openapi": "^0.2.
|
|
80
|
-
"@zip.js/zip.js": "^2.
|
|
81
|
-
"ai": "^
|
|
82
|
-
"chokidar": "^
|
|
83
|
-
"clipboardy": "^5.
|
|
84
|
-
"command-stream": "^
|
|
73
|
+
"@opencode-ai/sdk": "^1.18.33",
|
|
74
|
+
"@openrouter/ai-sdk-provider": "^3.1.0",
|
|
75
|
+
"@opentui/core": "0.5.12",
|
|
76
|
+
"@opentui/solid": "0.5.12",
|
|
77
|
+
"@parcel/watcher": "^2.6.0",
|
|
78
|
+
"@solid-primitives/event-bus": "^1.1.4",
|
|
79
|
+
"@standard-community/standard-json": "^0.3.6",
|
|
80
|
+
"@standard-community/standard-openapi": "^0.2.10",
|
|
81
|
+
"@zip.js/zip.js": "^2.18.2",
|
|
82
|
+
"ai": "^7.0.122",
|
|
83
|
+
"chokidar": "^5.0.0",
|
|
84
|
+
"clipboardy": "^5.3.2",
|
|
85
|
+
"command-stream": "^1.3.0",
|
|
85
86
|
"decimal.js": "^10.6.0",
|
|
86
|
-
"diff": "^
|
|
87
|
-
"fuzzysort": "^
|
|
88
|
-
"glob": "^
|
|
89
|
-
"gpt-tokenizer": "^
|
|
87
|
+
"diff": "^9.0.0",
|
|
88
|
+
"fuzzysort": "^4.0.2",
|
|
89
|
+
"glob": "^13.0.6",
|
|
90
|
+
"gpt-tokenizer": "^4.0.0",
|
|
90
91
|
"gray-matter": "^4.0.3",
|
|
91
|
-
"hono": "^4.
|
|
92
|
-
"hono-openapi": "^1.
|
|
93
|
-
"ignore": "^7.0.
|
|
92
|
+
"hono": "^4.13.11",
|
|
93
|
+
"hono-openapi": "^1.3.3",
|
|
94
|
+
"ignore": "^7.0.10",
|
|
94
95
|
"jsonc-parser": "^3.3.1",
|
|
95
|
-
"lino-arguments": "^0.
|
|
96
|
-
"lino-objects-codec": "^0.
|
|
96
|
+
"lino-arguments": "^0.3.0",
|
|
97
|
+
"lino-objects-codec": "^0.8.0",
|
|
97
98
|
"log-lazy": "^1.0.4",
|
|
98
|
-
"minimatch": "^10.
|
|
99
|
-
"remeda": "^2.
|
|
99
|
+
"minimatch": "^10.2.6",
|
|
100
|
+
"remeda": "^2.50.0",
|
|
100
101
|
"solid-js": "1.9.12",
|
|
101
|
-
"strip-ansi": "^7.
|
|
102
|
-
"test-anywhere": "^0.
|
|
103
|
-
"tree-sitter-bash": "^0.25.
|
|
104
|
-
"turndown": "^7.2.
|
|
105
|
-
"ulid": "^3.0.
|
|
106
|
-
"vscode-jsonrpc": "^
|
|
107
|
-
"vscode-languageserver-types": "^3.
|
|
108
|
-
"web-tree-sitter": "
|
|
102
|
+
"strip-ansi": "^7.2.0",
|
|
103
|
+
"test-anywhere": "^0.9.1",
|
|
104
|
+
"tree-sitter-bash": "^0.25.1",
|
|
105
|
+
"turndown": "^7.2.4",
|
|
106
|
+
"ulid": "^3.0.2",
|
|
107
|
+
"vscode-jsonrpc": "^9.0.3",
|
|
108
|
+
"vscode-languageserver-types": "^3.18.4",
|
|
109
|
+
"web-tree-sitter": "0.25.10",
|
|
109
110
|
"xdg-basedir": "^5.1.0",
|
|
110
|
-
"yargs": "^18.
|
|
111
|
-
"zod": "^4.
|
|
111
|
+
"yargs": "^18.2.0",
|
|
112
|
+
"zod": "^4.6.5"
|
|
112
113
|
},
|
|
113
114
|
"devDependencies": {
|
|
114
|
-
"@changesets/cli": "^
|
|
115
|
-
"@eslint/js": "^
|
|
116
|
-
"eslint": "^
|
|
115
|
+
"@changesets/cli": "^3.0.3",
|
|
116
|
+
"@eslint/js": "^10.0.1",
|
|
117
|
+
"eslint": "^10.11.0",
|
|
117
118
|
"eslint-config-prettier": "^10.1.8",
|
|
118
|
-
"eslint-plugin-prettier": "^5.5.
|
|
119
|
-
"eslint-plugin-promise": "^7.
|
|
119
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
120
|
+
"eslint-plugin-promise": "^7.3.0",
|
|
120
121
|
"husky": "^9.1.7",
|
|
121
|
-
"lint-staged": "^
|
|
122
|
-
"prettier": "^3.
|
|
122
|
+
"lint-staged": "^17.6.0",
|
|
123
|
+
"prettier": "^3.9.9"
|
|
123
124
|
},
|
|
124
125
|
"lint-staged": {
|
|
125
126
|
"*.{js,mjs,cjs}": [
|
|
@@ -134,10 +135,15 @@
|
|
|
134
135
|
},
|
|
135
136
|
"license": "Unlicense",
|
|
136
137
|
"allowScripts": {
|
|
137
|
-
"@parcel/watcher@2.
|
|
138
|
-
"tree-sitter-bash@0.25.1": true
|
|
138
|
+
"@parcel/watcher@2.6.0": true,
|
|
139
|
+
"tree-sitter-bash@0.25.1": true,
|
|
140
|
+
"node-pty@1.2.0-beta.15": true
|
|
139
141
|
},
|
|
140
142
|
"overrides": {
|
|
141
143
|
"undici": "^7.16.0"
|
|
144
|
+
},
|
|
145
|
+
"dependencyPins": {
|
|
146
|
+
"solid-js": "https://github.com/anomalyco/opentui/issues/689",
|
|
147
|
+
"web-tree-sitter": "https://github.com/link-assistant/agent/issues/322"
|
|
142
148
|
}
|
|
143
149
|
}
|
package/src/agent/agent.ts
CHANGED
|
@@ -138,10 +138,10 @@ export namespace Agent {
|
|
|
138
138
|
const existing = await list();
|
|
139
139
|
const result = await generateObject({
|
|
140
140
|
temperature: 0.3,
|
|
141
|
-
// The system prompt belongs in `
|
|
141
|
+
// The system prompt belongs in `instructions`, not in `prompt`: the AI SDK
|
|
142
142
|
// warns that a system message inside the prompt enables prompt
|
|
143
143
|
// injection, and providers are free to treat it as ordinary content.
|
|
144
|
-
|
|
144
|
+
instructions: system.map((content) => ({
|
|
145
145
|
role: 'system' as const,
|
|
146
146
|
content,
|
|
147
147
|
})),
|
package/src/cli/input-queue.js
CHANGED
package/src/cli/model-config.js
CHANGED
|
@@ -100,6 +100,10 @@ export async function parseModelConfig(
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
if (argv['dry-run'] && !cliModelArg && modelArg === defaultModel) {
|
|
104
|
+
modelArg = 'link-assistant/echo';
|
|
105
|
+
}
|
|
106
|
+
|
|
103
107
|
// Attestation inputs (#295): record what was requested, and where it came
|
|
104
108
|
// from, before resolution can rewrite the effective model. `argv.model` is
|
|
105
109
|
// only treated as a request when it differs from the default, because yargs
|
package/src/provider/cache.ts
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
* @see https://github.com/link-foundation/lino-objects-codec
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
LanguageModelV4,
|
|
20
|
+
LanguageModelV4CallOptions,
|
|
21
|
+
LanguageModelV4GenerateResult,
|
|
22
|
+
} from '@ai-sdk/provider';
|
|
19
23
|
import { Log } from '../util/log';
|
|
20
24
|
import { createEchoModel } from './echo';
|
|
21
25
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
@@ -33,7 +37,7 @@ const CACHE_ROOT = join(__dirname, '../../data/api-cache');
|
|
|
33
37
|
* Generate a cache key from the prompt
|
|
34
38
|
*/
|
|
35
39
|
function generateCacheKey(
|
|
36
|
-
prompt:
|
|
40
|
+
prompt: LanguageModelV4CallOptions['prompt']
|
|
37
41
|
): string {
|
|
38
42
|
// Simple hash of the prompt content
|
|
39
43
|
const content = JSON.stringify(prompt);
|
|
@@ -64,14 +68,43 @@ function generatePartId(): string {
|
|
|
64
68
|
/**
|
|
65
69
|
* Load cached response from file using Links Notation format
|
|
66
70
|
*/
|
|
67
|
-
function loadCachedResponse(
|
|
71
|
+
function loadCachedResponse(
|
|
72
|
+
filePath: string
|
|
73
|
+
): LanguageModelV4GenerateResult | null {
|
|
68
74
|
try {
|
|
69
75
|
if (!existsSync(filePath)) {
|
|
70
76
|
return null;
|
|
71
77
|
}
|
|
72
78
|
const content = readFileSync(filePath, 'utf8');
|
|
73
79
|
// Decode from Links Notation format
|
|
74
|
-
|
|
80
|
+
const cached = decode({ notation: content });
|
|
81
|
+
// Existing .lino files may contain AI SDK 6 responses. Normalize their
|
|
82
|
+
// finish reason and usage so they remain usable by AI SDK 7.
|
|
83
|
+
if (typeof cached?.finishReason === 'string') {
|
|
84
|
+
const promptTokens = cached.usage?.promptTokens ?? 0;
|
|
85
|
+
const completionTokens = cached.usage?.completionTokens ?? 0;
|
|
86
|
+
return {
|
|
87
|
+
...cached,
|
|
88
|
+
finishReason: {
|
|
89
|
+
unified: cached.finishReason,
|
|
90
|
+
raw: cached.finishReason,
|
|
91
|
+
},
|
|
92
|
+
usage: {
|
|
93
|
+
inputTokens: {
|
|
94
|
+
total: promptTokens,
|
|
95
|
+
noCache: promptTokens,
|
|
96
|
+
cacheRead: 0,
|
|
97
|
+
cacheWrite: 0,
|
|
98
|
+
},
|
|
99
|
+
outputTokens: {
|
|
100
|
+
total: completionTokens,
|
|
101
|
+
text: completionTokens,
|
|
102
|
+
reasoning: 0,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return cached;
|
|
75
108
|
} catch (error: any) {
|
|
76
109
|
log.warn('Failed to load cached response', {
|
|
77
110
|
filePath,
|
|
@@ -104,21 +137,21 @@ function saveCachedResponse(filePath: string, response: any): void {
|
|
|
104
137
|
|
|
105
138
|
/**
|
|
106
139
|
* Creates a cache language model that stores/retrieves responses
|
|
107
|
-
* Implements
|
|
140
|
+
* Implements the AI SDK 7 language model interface.
|
|
108
141
|
*/
|
|
109
142
|
export function createCacheModel(
|
|
110
143
|
providerId: string,
|
|
111
144
|
modelId: string
|
|
112
|
-
):
|
|
113
|
-
const model:
|
|
114
|
-
specificationVersion: '
|
|
145
|
+
): LanguageModelV4 {
|
|
146
|
+
const model: LanguageModelV4 = {
|
|
147
|
+
specificationVersion: 'v4',
|
|
115
148
|
provider: 'link-assistant',
|
|
116
149
|
modelId: `${providerId}/${modelId}`,
|
|
117
150
|
|
|
118
151
|
// No external URLs are supported by this synthetic provider
|
|
119
152
|
supportedUrls: {},
|
|
120
153
|
|
|
121
|
-
async doGenerate(options:
|
|
154
|
+
async doGenerate(options: LanguageModelV4CallOptions) {
|
|
122
155
|
const cacheKey = generateCacheKey(options.prompt);
|
|
123
156
|
const cachePath = getCachePath(providerId, modelId, cacheKey);
|
|
124
157
|
|
|
@@ -144,7 +177,7 @@ export function createCacheModel(
|
|
|
144
177
|
return response;
|
|
145
178
|
},
|
|
146
179
|
|
|
147
|
-
async doStream(options:
|
|
180
|
+
async doStream(options: LanguageModelV4CallOptions) {
|
|
148
181
|
const cacheKey = generateCacheKey(options.prompt);
|
|
149
182
|
const cachePath = getCachePath(providerId, modelId, cacheKey);
|
|
150
183
|
|
|
@@ -163,7 +196,7 @@ export function createCacheModel(
|
|
|
163
196
|
cached.content?.[0]?.text || cached.text || 'Cached response';
|
|
164
197
|
const textPartId = generatePartId();
|
|
165
198
|
|
|
166
|
-
// Create a ReadableStream with
|
|
199
|
+
// Create a ReadableStream with LanguageModelV4StreamPart format.
|
|
167
200
|
const stream = new ReadableStream({
|
|
168
201
|
async start(controller) {
|
|
169
202
|
// Emit text-start
|
|
@@ -195,10 +228,19 @@ export function createCacheModel(
|
|
|
195
228
|
// Emit finish event
|
|
196
229
|
controller.enqueue({
|
|
197
230
|
type: 'finish',
|
|
198
|
-
finishReason: 'stop',
|
|
231
|
+
finishReason: { unified: 'stop', raw: 'stop' },
|
|
199
232
|
usage: cached.usage || {
|
|
200
|
-
|
|
201
|
-
|
|
233
|
+
inputTokens: {
|
|
234
|
+
total: Math.ceil(echoText.length / 4),
|
|
235
|
+
noCache: Math.ceil(echoText.length / 4),
|
|
236
|
+
cacheRead: 0,
|
|
237
|
+
cacheWrite: 0,
|
|
238
|
+
},
|
|
239
|
+
outputTokens: {
|
|
240
|
+
total: Math.ceil(echoText.length / 4),
|
|
241
|
+
text: Math.ceil(echoText.length / 4),
|
|
242
|
+
reasoning: 0,
|
|
243
|
+
},
|
|
202
244
|
},
|
|
203
245
|
providerMetadata: undefined,
|
|
204
246
|
});
|
|
@@ -211,7 +253,6 @@ export function createCacheModel(
|
|
|
211
253
|
stream,
|
|
212
254
|
request: undefined,
|
|
213
255
|
response: undefined,
|
|
214
|
-
warnings: [],
|
|
215
256
|
};
|
|
216
257
|
}
|
|
217
258
|
|
|
@@ -237,7 +278,7 @@ export function createCacheModel(
|
|
|
237
278
|
*/
|
|
238
279
|
export function createCacheProvider(options?: { name?: string }) {
|
|
239
280
|
return {
|
|
240
|
-
languageModel(modelId: string):
|
|
281
|
+
languageModel(modelId: string): LanguageModelV4 {
|
|
241
282
|
// Parse provider/model from modelId like "opencode/grok-code"
|
|
242
283
|
const parts = modelId.split('/');
|
|
243
284
|
if (parts.length < 2) {
|
package/src/provider/echo.ts
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
* Input: "How are you?" -> Output: "How are you?"
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
LanguageModelV4,
|
|
19
|
+
LanguageModelV4CallOptions,
|
|
20
|
+
} from '@ai-sdk/provider';
|
|
18
21
|
import { Log } from '../util/log';
|
|
19
22
|
|
|
20
23
|
const log = Log.create({ service: 'provider.echo' });
|
|
@@ -23,7 +26,7 @@ const log = Log.create({ service: 'provider.echo' });
|
|
|
23
26
|
* Extract text content from the prompt messages
|
|
24
27
|
*/
|
|
25
28
|
function extractTextFromPrompt(
|
|
26
|
-
prompt:
|
|
29
|
+
prompt: LanguageModelV4CallOptions['prompt']
|
|
27
30
|
): string {
|
|
28
31
|
const textParts: string[] = [];
|
|
29
32
|
|
|
@@ -52,18 +55,18 @@ function generatePartId(): string {
|
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* Creates an echo language model that echoes back the user's input
|
|
55
|
-
* Implements
|
|
58
|
+
* Implements the AI SDK 7 language model interface.
|
|
56
59
|
*/
|
|
57
|
-
export function createEchoModel(modelId: string = 'echo'):
|
|
58
|
-
const model:
|
|
59
|
-
specificationVersion: '
|
|
60
|
+
export function createEchoModel(modelId: string = 'echo'): LanguageModelV4 {
|
|
61
|
+
const model: LanguageModelV4 = {
|
|
62
|
+
specificationVersion: 'v4',
|
|
60
63
|
provider: 'link-assistant',
|
|
61
64
|
modelId,
|
|
62
65
|
|
|
63
66
|
// No external URLs are supported by this synthetic provider
|
|
64
67
|
supportedUrls: {},
|
|
65
68
|
|
|
66
|
-
async doGenerate(options:
|
|
69
|
+
async doGenerate(options: LanguageModelV4CallOptions) {
|
|
67
70
|
const echoText = extractTextFromPrompt(options.prompt);
|
|
68
71
|
log.info('echo generate', { modelId, echoText });
|
|
69
72
|
|
|
@@ -78,10 +81,19 @@ export function createEchoModel(modelId: string = 'echo'): LanguageModelV2 {
|
|
|
78
81
|
text: echoText,
|
|
79
82
|
},
|
|
80
83
|
],
|
|
81
|
-
finishReason: 'stop' as const,
|
|
84
|
+
finishReason: { unified: 'stop' as const, raw: 'stop' },
|
|
82
85
|
usage: {
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
inputTokens: {
|
|
87
|
+
total: promptTokens,
|
|
88
|
+
noCache: promptTokens,
|
|
89
|
+
cacheRead: 0,
|
|
90
|
+
cacheWrite: 0,
|
|
91
|
+
},
|
|
92
|
+
outputTokens: {
|
|
93
|
+
total: completionTokens,
|
|
94
|
+
text: completionTokens,
|
|
95
|
+
reasoning: 0,
|
|
96
|
+
},
|
|
85
97
|
},
|
|
86
98
|
warnings: [],
|
|
87
99
|
providerMetadata: undefined,
|
|
@@ -90,7 +102,7 @@ export function createEchoModel(modelId: string = 'echo'): LanguageModelV2 {
|
|
|
90
102
|
};
|
|
91
103
|
},
|
|
92
104
|
|
|
93
|
-
async doStream(options:
|
|
105
|
+
async doStream(options: LanguageModelV4CallOptions) {
|
|
94
106
|
const echoText = extractTextFromPrompt(options.prompt);
|
|
95
107
|
log.info('echo stream', { modelId, echoText });
|
|
96
108
|
|
|
@@ -100,8 +112,7 @@ export function createEchoModel(modelId: string = 'echo'): LanguageModelV2 {
|
|
|
100
112
|
|
|
101
113
|
const textPartId = generatePartId();
|
|
102
114
|
|
|
103
|
-
//
|
|
104
|
-
// V2 format uses: text-start -> text-delta (with delta) -> text-end -> finish
|
|
115
|
+
// Stream text-start -> text-delta -> text-end -> finish.
|
|
105
116
|
const stream = new ReadableStream({
|
|
106
117
|
async start(controller) {
|
|
107
118
|
// Emit text-start
|
|
@@ -133,10 +144,19 @@ export function createEchoModel(modelId: string = 'echo'): LanguageModelV2 {
|
|
|
133
144
|
// Emit finish event with usage information
|
|
134
145
|
controller.enqueue({
|
|
135
146
|
type: 'finish',
|
|
136
|
-
finishReason: 'stop',
|
|
147
|
+
finishReason: { unified: 'stop', raw: 'stop' },
|
|
137
148
|
usage: {
|
|
138
|
-
|
|
139
|
-
|
|
149
|
+
inputTokens: {
|
|
150
|
+
total: promptTokens,
|
|
151
|
+
noCache: promptTokens,
|
|
152
|
+
cacheRead: 0,
|
|
153
|
+
cacheWrite: 0,
|
|
154
|
+
},
|
|
155
|
+
outputTokens: {
|
|
156
|
+
total: completionTokens,
|
|
157
|
+
text: completionTokens,
|
|
158
|
+
reasoning: 0,
|
|
159
|
+
},
|
|
140
160
|
},
|
|
141
161
|
providerMetadata: undefined,
|
|
142
162
|
});
|
|
@@ -149,7 +169,6 @@ export function createEchoModel(modelId: string = 'echo'): LanguageModelV2 {
|
|
|
149
169
|
stream,
|
|
150
170
|
request: undefined,
|
|
151
171
|
response: undefined,
|
|
152
|
-
warnings: [],
|
|
153
172
|
};
|
|
154
173
|
},
|
|
155
174
|
};
|
|
@@ -162,7 +181,7 @@ export function createEchoModel(modelId: string = 'echo'): LanguageModelV2 {
|
|
|
162
181
|
*/
|
|
163
182
|
export function createEchoProvider(options?: { name?: string }) {
|
|
164
183
|
return {
|
|
165
|
-
languageModel(modelId: string):
|
|
184
|
+
languageModel(modelId: string): LanguageModelV4 {
|
|
166
185
|
return createEchoModel(modelId);
|
|
167
186
|
},
|
|
168
187
|
textEmbeddingModel() {
|
|
@@ -90,8 +90,7 @@ export namespace OpenCodeZen {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
const body = (await response.json().catch(() => undefined)) as
|
|
93
|
-
|
|
94
|
-
| undefined;
|
|
93
|
+
{ data?: Array<{ id?: unknown }> } | undefined;
|
|
95
94
|
const rows = Array.isArray(body?.data) ? body.data : [];
|
|
96
95
|
return new Set(
|
|
97
96
|
rows
|
package/src/provider/provider.ts
CHANGED
|
@@ -730,9 +730,9 @@ export namespace Provider {
|
|
|
730
730
|
'link-assistant': async () => {
|
|
731
731
|
// Echo provider is always available - no external dependencies needed
|
|
732
732
|
return {
|
|
733
|
-
autoload:
|
|
733
|
+
autoload: true, // Also available through --model link-assistant/echo
|
|
734
734
|
async getModel(_sdk: any, modelID: string) {
|
|
735
|
-
// Return our custom
|
|
735
|
+
// Return our custom AI SDK language model.
|
|
736
736
|
return createEchoModel(modelID);
|
|
737
737
|
},
|
|
738
738
|
options: {},
|
|
@@ -762,7 +762,7 @@ export namespace Provider {
|
|
|
762
762
|
const [providerId, ...modelParts] = parts;
|
|
763
763
|
const actualModelId = modelParts.join('/');
|
|
764
764
|
|
|
765
|
-
// Return our custom
|
|
765
|
+
// Return our custom AI SDK language model.
|
|
766
766
|
return createCacheModel(providerId, actualModelId);
|
|
767
767
|
},
|
|
768
768
|
options: {},
|
|
@@ -1772,8 +1772,16 @@ export namespace Provider {
|
|
|
1772
1772
|
const isSyntheticProvider =
|
|
1773
1773
|
providerID === 'link-assistant' || providerID === 'link-assistant/cache';
|
|
1774
1774
|
|
|
1775
|
-
//
|
|
1776
|
-
let info =
|
|
1775
|
+
// Synthetic models still need metadata for context limits and accounting.
|
|
1776
|
+
let info =
|
|
1777
|
+
provider.info.models[modelID] ??
|
|
1778
|
+
(isSyntheticProvider
|
|
1779
|
+
? {
|
|
1780
|
+
...s.providers['link-assistant'].info.models.echo,
|
|
1781
|
+
id: modelID,
|
|
1782
|
+
name: modelID,
|
|
1783
|
+
}
|
|
1784
|
+
: undefined);
|
|
1777
1785
|
if (!isSyntheticProvider && !info) {
|
|
1778
1786
|
// Model not in provider's known list - try refreshing the cache first (#200)
|
|
1779
1787
|
// This handles stale bundled data or expired cache (1-hour TTL)
|
|
@@ -387,7 +387,7 @@ export namespace SessionCompaction {
|
|
|
387
387
|
headers: model.info.headers,
|
|
388
388
|
abortSignal: input.abort,
|
|
389
389
|
tools: model.info.tool_call ? {} : undefined,
|
|
390
|
-
|
|
390
|
+
instructions: system.map((content) => ({ role: 'system', content })),
|
|
391
391
|
messages: [
|
|
392
392
|
...safeModelMessages,
|
|
393
393
|
{
|
package/src/session/processor.ts
CHANGED
|
@@ -116,7 +116,7 @@ export namespace SessionProcessor {
|
|
|
116
116
|
let reasoningMap: Record<string, MessageV2.ReasoningPart> = {};
|
|
117
117
|
const stream = fn();
|
|
118
118
|
|
|
119
|
-
for await (const value of stream.
|
|
119
|
+
for await (const value of stream.stream) {
|
|
120
120
|
input.abort.throwIfAborted();
|
|
121
121
|
switch (value.type) {
|
|
122
122
|
case 'start':
|
package/src/session/prompt.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type Tool as AITool,
|
|
16
16
|
tool,
|
|
17
17
|
wrapLanguageModel,
|
|
18
|
-
|
|
18
|
+
isStepCount,
|
|
19
19
|
jsonSchema,
|
|
20
20
|
} from 'ai';
|
|
21
21
|
import { SessionCompaction } from './compaction';
|
|
@@ -1011,10 +1011,10 @@ export namespace SessionPrompt {
|
|
|
1011
1011
|
model.providerID,
|
|
1012
1012
|
params.options
|
|
1013
1013
|
),
|
|
1014
|
-
stopWhen:
|
|
1014
|
+
stopWhen: isStepCount(1),
|
|
1015
1015
|
temperature: params.temperature,
|
|
1016
1016
|
topP: params.topP,
|
|
1017
|
-
|
|
1017
|
+
instructions: system.map((content) => ({ role: 'system', content })),
|
|
1018
1018
|
messages: safeModelMessages,
|
|
1019
1019
|
tools: model.info?.tool_call === false ? undefined : tools,
|
|
1020
1020
|
model: wrapLanguageModel({
|
|
@@ -1059,6 +1059,9 @@ export namespace SessionPrompt {
|
|
|
1059
1059
|
if (input.model) {
|
|
1060
1060
|
return input.model;
|
|
1061
1061
|
}
|
|
1062
|
+
if (config.dryRun) {
|
|
1063
|
+
return Provider.defaultModel();
|
|
1064
|
+
}
|
|
1062
1065
|
if (input.agent.model) {
|
|
1063
1066
|
return input.agent.model;
|
|
1064
1067
|
}
|
|
@@ -1942,7 +1945,7 @@ export namespace SessionPrompt {
|
|
|
1942
1945
|
small.providerID,
|
|
1943
1946
|
options
|
|
1944
1947
|
),
|
|
1945
|
-
|
|
1948
|
+
instructions: safeTitleSystemMessages.map((content) => ({
|
|
1946
1949
|
role: 'system',
|
|
1947
1950
|
content,
|
|
1948
1951
|
})),
|
package/src/session/summary.ts
CHANGED
|
@@ -222,10 +222,10 @@ export namespace SessionSummary {
|
|
|
222
222
|
model.providerID,
|
|
223
223
|
{}
|
|
224
224
|
),
|
|
225
|
-
// The system prompt belongs in `
|
|
225
|
+
// The system prompt belongs in `instructions`, not in `messages`: the AI SDK
|
|
226
226
|
// warns that a system message inside `messages` enables prompt
|
|
227
227
|
// injection, and providers are free to treat it as ordinary content.
|
|
228
|
-
|
|
228
|
+
instructions: systemPrompts.map((content) => ({
|
|
229
229
|
role: 'system' as const,
|
|
230
230
|
content,
|
|
231
231
|
})),
|