@link-assistant/agent 0.0.9 → 0.0.12
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/EXAMPLES.md +36 -0
- package/MODELS.md +72 -24
- package/README.md +59 -2
- package/TOOLS.md +20 -0
- package/package.json +35 -2
- package/src/agent/agent.ts +68 -54
- package/src/auth/claude-oauth.ts +426 -0
- package/src/auth/index.ts +28 -26
- package/src/auth/plugins.ts +876 -0
- package/src/bun/index.ts +53 -43
- package/src/bus/global.ts +5 -5
- package/src/bus/index.ts +59 -53
- package/src/cli/bootstrap.js +12 -12
- package/src/cli/bootstrap.ts +6 -6
- package/src/cli/cmd/agent.ts +97 -92
- package/src/cli/cmd/auth.ts +469 -0
- package/src/cli/cmd/cmd.ts +2 -2
- package/src/cli/cmd/export.ts +41 -41
- package/src/cli/cmd/mcp.ts +144 -119
- package/src/cli/cmd/models.ts +30 -29
- package/src/cli/cmd/run.ts +269 -213
- package/src/cli/cmd/stats.ts +185 -146
- package/src/cli/error.ts +17 -13
- package/src/cli/ui.ts +39 -24
- package/src/command/index.ts +26 -26
- package/src/config/config.ts +528 -288
- package/src/config/markdown.ts +15 -15
- package/src/file/ripgrep.ts +201 -169
- package/src/file/time.ts +21 -18
- package/src/file/watcher.ts +51 -42
- package/src/file.ts +1 -1
- package/src/flag/flag.ts +26 -11
- package/src/format/formatter.ts +206 -162
- package/src/format/index.ts +61 -61
- package/src/global/index.ts +21 -21
- package/src/id/id.ts +47 -33
- package/src/index.js +346 -199
- package/src/json-standard/index.ts +67 -51
- package/src/mcp/index.ts +135 -128
- package/src/patch/index.ts +336 -267
- package/src/project/bootstrap.ts +15 -15
- package/src/project/instance.ts +43 -36
- package/src/project/project.ts +47 -47
- package/src/project/state.ts +37 -33
- package/src/provider/models-macro.ts +5 -5
- package/src/provider/models.ts +32 -32
- package/src/provider/opencode.js +19 -19
- package/src/provider/provider.ts +518 -277
- package/src/provider/transform.ts +143 -102
- package/src/server/project.ts +21 -21
- package/src/server/server.ts +111 -105
- package/src/session/agent.js +66 -60
- package/src/session/compaction.ts +136 -111
- package/src/session/index.ts +189 -156
- package/src/session/message-v2.ts +312 -268
- package/src/session/message.ts +73 -57
- package/src/session/processor.ts +180 -166
- package/src/session/prompt.ts +678 -533
- package/src/session/retry.ts +26 -23
- package/src/session/revert.ts +76 -62
- package/src/session/status.ts +26 -26
- package/src/session/summary.ts +97 -76
- package/src/session/system.ts +77 -63
- package/src/session/todo.ts +22 -16
- package/src/snapshot/index.ts +92 -76
- package/src/storage/storage.ts +157 -120
- package/src/tool/bash.ts +116 -106
- package/src/tool/batch.ts +73 -59
- package/src/tool/codesearch.ts +60 -53
- package/src/tool/edit.ts +319 -263
- package/src/tool/glob.ts +32 -28
- package/src/tool/grep.ts +72 -53
- package/src/tool/invalid.ts +7 -7
- package/src/tool/ls.ts +77 -64
- package/src/tool/multiedit.ts +30 -21
- package/src/tool/patch.ts +121 -94
- package/src/tool/read.ts +140 -122
- package/src/tool/registry.ts +38 -38
- package/src/tool/task.ts +93 -60
- package/src/tool/todo.ts +16 -16
- package/src/tool/tool.ts +45 -36
- package/src/tool/webfetch.ts +97 -74
- package/src/tool/websearch.ts +78 -64
- package/src/tool/write.ts +21 -15
- package/src/util/binary.ts +27 -19
- package/src/util/context.ts +8 -8
- package/src/util/defer.ts +7 -5
- package/src/util/error.ts +24 -19
- package/src/util/eventloop.ts +16 -10
- package/src/util/filesystem.ts +37 -33
- package/src/util/fn.ts +11 -8
- package/src/util/iife.ts +1 -1
- package/src/util/keybind.ts +44 -44
- package/src/util/lazy.ts +7 -7
- package/src/util/locale.ts +20 -16
- package/src/util/lock.ts +43 -38
- package/src/util/log.ts +95 -85
- package/src/util/queue.ts +8 -8
- package/src/util/rpc.ts +35 -23
- package/src/util/scrap.ts +4 -4
- package/src/util/signal.ts +5 -5
- package/src/util/timeout.ts +6 -6
- package/src/util/token.ts +2 -2
- package/src/util/wildcard.ts +38 -27
|
@@ -1,107 +1,116 @@
|
|
|
1
|
-
import { streamText, wrapLanguageModel, type ModelMessage } from
|
|
2
|
-
import { Session } from
|
|
3
|
-
import { Identifier } from
|
|
4
|
-
import { Instance } from
|
|
5
|
-
import { Provider } from
|
|
6
|
-
import { MessageV2 } from
|
|
7
|
-
import { SystemPrompt } from
|
|
8
|
-
import { Bus } from
|
|
9
|
-
import z from
|
|
10
|
-
import type { ModelsDev } from
|
|
11
|
-
import { SessionPrompt } from
|
|
12
|
-
import { Flag } from
|
|
13
|
-
import { Token } from
|
|
14
|
-
import { Log } from
|
|
15
|
-
import { ProviderTransform } from
|
|
16
|
-
import { SessionProcessor } from
|
|
17
|
-
import { fn } from
|
|
1
|
+
import { streamText, wrapLanguageModel, type ModelMessage } from 'ai';
|
|
2
|
+
import { Session } from '.';
|
|
3
|
+
import { Identifier } from '../id/id';
|
|
4
|
+
import { Instance } from '../project/instance';
|
|
5
|
+
import { Provider } from '../provider/provider';
|
|
6
|
+
import { MessageV2 } from './message-v2';
|
|
7
|
+
import { SystemPrompt } from './system';
|
|
8
|
+
import { Bus } from '../bus';
|
|
9
|
+
import z from 'zod';
|
|
10
|
+
import type { ModelsDev } from '../provider/models';
|
|
11
|
+
import { SessionPrompt } from './prompt';
|
|
12
|
+
import { Flag } from '../flag/flag';
|
|
13
|
+
import { Token } from '../util/token';
|
|
14
|
+
import { Log } from '../util/log';
|
|
15
|
+
import { ProviderTransform } from '../provider/transform';
|
|
16
|
+
import { SessionProcessor } from './processor';
|
|
17
|
+
import { fn } from '../util/fn';
|
|
18
18
|
|
|
19
19
|
export namespace SessionCompaction {
|
|
20
|
-
const log = Log.create({ service:
|
|
20
|
+
const log = Log.create({ service: 'session.compaction' });
|
|
21
21
|
|
|
22
22
|
export const Event = {
|
|
23
23
|
Compacted: Bus.event(
|
|
24
|
-
|
|
24
|
+
'session.compacted',
|
|
25
25
|
z.object({
|
|
26
26
|
sessionID: z.string(),
|
|
27
|
-
})
|
|
27
|
+
})
|
|
28
28
|
),
|
|
29
|
-
}
|
|
29
|
+
};
|
|
30
30
|
|
|
31
|
-
export function isOverflow(input: {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
export function isOverflow(input: {
|
|
32
|
+
tokens: MessageV2.Assistant['tokens'];
|
|
33
|
+
model: ModelsDev.Model;
|
|
34
|
+
}) {
|
|
35
|
+
if (Flag.OPENCODE_DISABLE_AUTOCOMPACT) return false;
|
|
36
|
+
const context = input.model.limit.context;
|
|
37
|
+
if (context === 0) return false;
|
|
38
|
+
const count =
|
|
39
|
+
input.tokens.input + input.tokens.cache.read + input.tokens.output;
|
|
40
|
+
const output =
|
|
41
|
+
Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) ||
|
|
42
|
+
SessionPrompt.OUTPUT_TOKEN_MAX;
|
|
43
|
+
const usable = context - output;
|
|
44
|
+
return count > usable;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
export const PRUNE_MINIMUM = 20_000
|
|
42
|
-
export const PRUNE_PROTECT = 40_000
|
|
47
|
+
export const PRUNE_MINIMUM = 20_000;
|
|
48
|
+
export const PRUNE_PROTECT = 40_000;
|
|
43
49
|
|
|
44
50
|
// goes backwards through parts until there are 40_000 tokens worth of tool
|
|
45
51
|
// calls. then erases output of previous tool calls. idea is to throw away old
|
|
46
52
|
// tool calls that are no longer relevant.
|
|
47
53
|
export async function prune(input: { sessionID: string }) {
|
|
48
|
-
if (Flag.OPENCODE_DISABLE_PRUNE) return
|
|
49
|
-
log.info(
|
|
50
|
-
const msgs = await Session.messages({ sessionID: input.sessionID })
|
|
51
|
-
let total = 0
|
|
52
|
-
let pruned = 0
|
|
53
|
-
const toPrune = []
|
|
54
|
-
let turns = 0
|
|
54
|
+
if (Flag.OPENCODE_DISABLE_PRUNE) return;
|
|
55
|
+
log.info('pruning');
|
|
56
|
+
const msgs = await Session.messages({ sessionID: input.sessionID });
|
|
57
|
+
let total = 0;
|
|
58
|
+
let pruned = 0;
|
|
59
|
+
const toPrune = [];
|
|
60
|
+
let turns = 0;
|
|
55
61
|
|
|
56
62
|
loop: for (let msgIndex = msgs.length - 1; msgIndex >= 0; msgIndex--) {
|
|
57
|
-
const msg = msgs[msgIndex]
|
|
58
|
-
if (msg.info.role ===
|
|
59
|
-
if (turns < 2) continue
|
|
60
|
-
if (msg.info.role ===
|
|
63
|
+
const msg = msgs[msgIndex];
|
|
64
|
+
if (msg.info.role === 'user') turns++;
|
|
65
|
+
if (turns < 2) continue;
|
|
66
|
+
if (msg.info.role === 'assistant' && msg.info.summary) break loop;
|
|
61
67
|
for (let partIndex = msg.parts.length - 1; partIndex >= 0; partIndex--) {
|
|
62
|
-
const part = msg.parts[partIndex]
|
|
63
|
-
if (part.type ===
|
|
64
|
-
if (part.state.status ===
|
|
65
|
-
if (part.state.time.compacted) break loop
|
|
66
|
-
const estimate = Token.estimate(part.state.output)
|
|
67
|
-
total += estimate
|
|
68
|
+
const part = msg.parts[partIndex];
|
|
69
|
+
if (part.type === 'tool')
|
|
70
|
+
if (part.state.status === 'completed') {
|
|
71
|
+
if (part.state.time.compacted) break loop;
|
|
72
|
+
const estimate = Token.estimate(part.state.output);
|
|
73
|
+
total += estimate;
|
|
68
74
|
if (total > PRUNE_PROTECT) {
|
|
69
|
-
pruned += estimate
|
|
70
|
-
toPrune.push(part)
|
|
75
|
+
pruned += estimate;
|
|
76
|
+
toPrune.push(part);
|
|
71
77
|
}
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
|
-
log.info(
|
|
81
|
+
log.info('found', { pruned, total });
|
|
76
82
|
if (pruned > PRUNE_MINIMUM) {
|
|
77
83
|
for (const part of toPrune) {
|
|
78
|
-
if (part.state.status ===
|
|
79
|
-
part.state.time.compacted = Date.now()
|
|
80
|
-
await Session.updatePart(part)
|
|
84
|
+
if (part.state.status === 'completed') {
|
|
85
|
+
part.state.time.compacted = Date.now();
|
|
86
|
+
await Session.updatePart(part);
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
|
-
log.info(
|
|
89
|
+
log.info('pruned', { count: toPrune.length });
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
export async function process(input: {
|
|
88
|
-
parentID: string
|
|
89
|
-
messages: MessageV2.WithParts[]
|
|
90
|
-
sessionID: string
|
|
94
|
+
parentID: string;
|
|
95
|
+
messages: MessageV2.WithParts[];
|
|
96
|
+
sessionID: string;
|
|
91
97
|
model: {
|
|
92
|
-
providerID: string
|
|
93
|
-
modelID: string
|
|
94
|
-
}
|
|
95
|
-
abort: AbortSignal
|
|
98
|
+
providerID: string;
|
|
99
|
+
modelID: string;
|
|
100
|
+
};
|
|
101
|
+
abort: AbortSignal;
|
|
96
102
|
}) {
|
|
97
|
-
const model = await Provider.getModel(
|
|
98
|
-
|
|
103
|
+
const model = await Provider.getModel(
|
|
104
|
+
input.model.providerID,
|
|
105
|
+
input.model.modelID
|
|
106
|
+
);
|
|
107
|
+
const system = [...SystemPrompt.summarize(model.providerID)];
|
|
99
108
|
const msg = (await Session.updateMessage({
|
|
100
|
-
id: Identifier.ascending(
|
|
101
|
-
role:
|
|
109
|
+
id: Identifier.ascending('message'),
|
|
110
|
+
role: 'assistant',
|
|
102
111
|
parentID: input.parentID,
|
|
103
112
|
sessionID: input.sessionID,
|
|
104
|
-
mode:
|
|
113
|
+
mode: 'build',
|
|
105
114
|
summary: true,
|
|
106
115
|
path: {
|
|
107
116
|
cwd: Instance.directory,
|
|
@@ -119,57 +128,69 @@ export namespace SessionCompaction {
|
|
|
119
128
|
time: {
|
|
120
129
|
created: Date.now(),
|
|
121
130
|
},
|
|
122
|
-
})) as MessageV2.Assistant
|
|
131
|
+
})) as MessageV2.Assistant;
|
|
123
132
|
const processor = SessionProcessor.create({
|
|
124
133
|
assistantMessage: msg,
|
|
125
134
|
sessionID: input.sessionID,
|
|
126
135
|
providerID: input.model.providerID,
|
|
127
136
|
model: model.info,
|
|
128
137
|
abort: input.abort,
|
|
129
|
-
})
|
|
138
|
+
});
|
|
130
139
|
const result = await processor.process(() =>
|
|
131
140
|
streamText({
|
|
132
141
|
onError(error) {
|
|
133
|
-
log.error(
|
|
142
|
+
log.error('stream error', {
|
|
134
143
|
error,
|
|
135
|
-
})
|
|
144
|
+
});
|
|
136
145
|
},
|
|
137
146
|
// set to 0, we handle loop
|
|
138
147
|
maxRetries: 0,
|
|
139
|
-
providerOptions: ProviderTransform.providerOptions(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
providerOptions: ProviderTransform.providerOptions(
|
|
149
|
+
model.npm,
|
|
150
|
+
model.providerID,
|
|
151
|
+
{
|
|
152
|
+
...ProviderTransform.options(
|
|
153
|
+
model.providerID,
|
|
154
|
+
model.modelID,
|
|
155
|
+
model.npm ?? '',
|
|
156
|
+
input.sessionID
|
|
157
|
+
),
|
|
158
|
+
...model.info.options,
|
|
159
|
+
}
|
|
160
|
+
),
|
|
143
161
|
headers: model.info.headers,
|
|
144
162
|
abortSignal: input.abort,
|
|
145
163
|
tools: model.info.tool_call ? {} : undefined,
|
|
146
164
|
messages: [
|
|
147
165
|
...system.map(
|
|
148
166
|
(x): ModelMessage => ({
|
|
149
|
-
role:
|
|
167
|
+
role: 'system',
|
|
150
168
|
content: x,
|
|
151
|
-
})
|
|
169
|
+
})
|
|
152
170
|
),
|
|
153
171
|
...MessageV2.toModelMessage(
|
|
154
172
|
input.messages.filter((m) => {
|
|
155
|
-
if (m.info.role !==
|
|
156
|
-
return true
|
|
173
|
+
if (m.info.role !== 'assistant' || m.info.error === undefined) {
|
|
174
|
+
return true;
|
|
157
175
|
}
|
|
158
176
|
if (
|
|
159
177
|
MessageV2.AbortedError.isInstance(m.info.error) &&
|
|
160
|
-
m.parts.some(
|
|
178
|
+
m.parts.some(
|
|
179
|
+
(part) =>
|
|
180
|
+
part.type !== 'step-start' && part.type !== 'reasoning'
|
|
181
|
+
)
|
|
161
182
|
) {
|
|
162
|
-
return true
|
|
183
|
+
return true;
|
|
163
184
|
}
|
|
164
185
|
|
|
165
|
-
return false
|
|
166
|
-
})
|
|
186
|
+
return false;
|
|
187
|
+
})
|
|
167
188
|
),
|
|
168
189
|
{
|
|
169
|
-
role:
|
|
190
|
+
role: 'user',
|
|
170
191
|
content: [
|
|
171
192
|
{
|
|
172
|
-
type:
|
|
193
|
+
type: 'text',
|
|
173
194
|
text: "Provide a detailed but concise summary of our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next.",
|
|
174
195
|
},
|
|
175
196
|
],
|
|
@@ -180,48 +201,52 @@ export namespace SessionCompaction {
|
|
|
180
201
|
middleware: [
|
|
181
202
|
{
|
|
182
203
|
async transformParams(args) {
|
|
183
|
-
if (args.type ===
|
|
204
|
+
if (args.type === 'stream') {
|
|
184
205
|
// @ts-expect-error
|
|
185
|
-
args.params.prompt = ProviderTransform.message(
|
|
206
|
+
args.params.prompt = ProviderTransform.message(
|
|
207
|
+
args.params.prompt,
|
|
208
|
+
model.providerID,
|
|
209
|
+
model.modelID
|
|
210
|
+
);
|
|
186
211
|
}
|
|
187
|
-
return args.params
|
|
212
|
+
return args.params;
|
|
188
213
|
},
|
|
189
214
|
},
|
|
190
215
|
],
|
|
191
216
|
}),
|
|
192
|
-
})
|
|
193
|
-
)
|
|
194
|
-
if (result ===
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
if (result === 'continue') {
|
|
195
220
|
const continueMsg = await Session.updateMessage({
|
|
196
|
-
id: Identifier.ascending(
|
|
197
|
-
role:
|
|
221
|
+
id: Identifier.ascending('message'),
|
|
222
|
+
role: 'user',
|
|
198
223
|
sessionID: input.sessionID,
|
|
199
224
|
time: {
|
|
200
225
|
created: Date.now(),
|
|
201
226
|
},
|
|
202
|
-
agent:
|
|
227
|
+
agent: 'build',
|
|
203
228
|
model: input.model,
|
|
204
|
-
})
|
|
229
|
+
});
|
|
205
230
|
await Session.updatePart({
|
|
206
|
-
id: Identifier.ascending(
|
|
231
|
+
id: Identifier.ascending('part'),
|
|
207
232
|
messageID: continueMsg.id,
|
|
208
233
|
sessionID: input.sessionID,
|
|
209
|
-
type:
|
|
234
|
+
type: 'text',
|
|
210
235
|
synthetic: true,
|
|
211
|
-
text:
|
|
236
|
+
text: 'Continue if you have next steps',
|
|
212
237
|
time: {
|
|
213
238
|
start: Date.now(),
|
|
214
239
|
end: Date.now(),
|
|
215
240
|
},
|
|
216
|
-
})
|
|
241
|
+
});
|
|
217
242
|
}
|
|
218
|
-
if (processor.message.error) return
|
|
219
|
-
return
|
|
243
|
+
if (processor.message.error) return 'stop';
|
|
244
|
+
return 'continue';
|
|
220
245
|
}
|
|
221
246
|
|
|
222
247
|
export const create = fn(
|
|
223
248
|
z.object({
|
|
224
|
-
sessionID: Identifier.schema(
|
|
249
|
+
sessionID: Identifier.schema('session'),
|
|
225
250
|
model: z.object({
|
|
226
251
|
providerID: z.string(),
|
|
227
252
|
modelID: z.string(),
|
|
@@ -229,21 +254,21 @@ export namespace SessionCompaction {
|
|
|
229
254
|
}),
|
|
230
255
|
async (input) => {
|
|
231
256
|
const msg = await Session.updateMessage({
|
|
232
|
-
id: Identifier.ascending(
|
|
233
|
-
role:
|
|
257
|
+
id: Identifier.ascending('message'),
|
|
258
|
+
role: 'user',
|
|
234
259
|
model: input.model,
|
|
235
260
|
sessionID: input.sessionID,
|
|
236
|
-
agent:
|
|
261
|
+
agent: 'build',
|
|
237
262
|
time: {
|
|
238
263
|
created: Date.now(),
|
|
239
264
|
},
|
|
240
|
-
})
|
|
265
|
+
});
|
|
241
266
|
await Session.updatePart({
|
|
242
|
-
id: Identifier.ascending(
|
|
267
|
+
id: Identifier.ascending('part'),
|
|
243
268
|
messageID: msg.id,
|
|
244
269
|
sessionID: msg.sessionID,
|
|
245
|
-
type:
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
|
-
)
|
|
270
|
+
type: 'compaction',
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
);
|
|
249
274
|
}
|