@link-assistant/agent 0.0.8 → 0.0.11

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.
Files changed (104) hide show
  1. package/EXAMPLES.md +80 -1
  2. package/MODELS.md +72 -24
  3. package/README.md +95 -2
  4. package/TOOLS.md +20 -0
  5. package/package.json +36 -2
  6. package/src/agent/agent.ts +68 -54
  7. package/src/auth/claude-oauth.ts +426 -0
  8. package/src/auth/index.ts +28 -26
  9. package/src/auth/plugins.ts +876 -0
  10. package/src/bun/index.ts +53 -43
  11. package/src/bus/global.ts +5 -5
  12. package/src/bus/index.ts +59 -53
  13. package/src/cli/bootstrap.js +12 -12
  14. package/src/cli/bootstrap.ts +6 -6
  15. package/src/cli/cmd/agent.ts +97 -92
  16. package/src/cli/cmd/auth.ts +468 -0
  17. package/src/cli/cmd/cmd.ts +2 -2
  18. package/src/cli/cmd/export.ts +41 -41
  19. package/src/cli/cmd/mcp.ts +210 -53
  20. package/src/cli/cmd/models.ts +30 -29
  21. package/src/cli/cmd/run.ts +269 -213
  22. package/src/cli/cmd/stats.ts +185 -146
  23. package/src/cli/error.ts +17 -13
  24. package/src/cli/ui.ts +78 -0
  25. package/src/command/index.ts +26 -26
  26. package/src/config/config.ts +528 -288
  27. package/src/config/markdown.ts +15 -15
  28. package/src/file/ripgrep.ts +201 -169
  29. package/src/file/time.ts +21 -18
  30. package/src/file/watcher.ts +51 -42
  31. package/src/file.ts +1 -1
  32. package/src/flag/flag.ts +26 -11
  33. package/src/format/formatter.ts +206 -162
  34. package/src/format/index.ts +61 -61
  35. package/src/global/index.ts +21 -21
  36. package/src/id/id.ts +47 -33
  37. package/src/index.js +554 -332
  38. package/src/json-standard/index.ts +173 -0
  39. package/src/mcp/index.ts +135 -128
  40. package/src/patch/index.ts +336 -267
  41. package/src/project/bootstrap.ts +15 -15
  42. package/src/project/instance.ts +43 -36
  43. package/src/project/project.ts +47 -47
  44. package/src/project/state.ts +37 -33
  45. package/src/provider/models-macro.ts +5 -5
  46. package/src/provider/models.ts +32 -32
  47. package/src/provider/opencode.js +19 -19
  48. package/src/provider/provider.ts +518 -277
  49. package/src/provider/transform.ts +143 -102
  50. package/src/server/project.ts +21 -21
  51. package/src/server/server.ts +111 -105
  52. package/src/session/agent.js +66 -60
  53. package/src/session/compaction.ts +136 -111
  54. package/src/session/index.ts +189 -156
  55. package/src/session/message-v2.ts +312 -268
  56. package/src/session/message.ts +73 -57
  57. package/src/session/processor.ts +180 -166
  58. package/src/session/prompt.ts +678 -533
  59. package/src/session/retry.ts +26 -23
  60. package/src/session/revert.ts +76 -62
  61. package/src/session/status.ts +26 -26
  62. package/src/session/summary.ts +97 -76
  63. package/src/session/system.ts +77 -63
  64. package/src/session/todo.ts +22 -16
  65. package/src/snapshot/index.ts +92 -76
  66. package/src/storage/storage.ts +157 -120
  67. package/src/tool/bash.ts +116 -106
  68. package/src/tool/batch.ts +73 -59
  69. package/src/tool/codesearch.ts +60 -53
  70. package/src/tool/edit.ts +319 -263
  71. package/src/tool/glob.ts +32 -28
  72. package/src/tool/grep.ts +72 -53
  73. package/src/tool/invalid.ts +7 -7
  74. package/src/tool/ls.ts +77 -64
  75. package/src/tool/multiedit.ts +30 -21
  76. package/src/tool/patch.ts +121 -94
  77. package/src/tool/read.ts +140 -122
  78. package/src/tool/registry.ts +38 -38
  79. package/src/tool/task.ts +93 -60
  80. package/src/tool/todo.ts +16 -16
  81. package/src/tool/tool.ts +45 -36
  82. package/src/tool/webfetch.ts +97 -74
  83. package/src/tool/websearch.ts +78 -64
  84. package/src/tool/write.ts +21 -15
  85. package/src/util/binary.ts +27 -19
  86. package/src/util/context.ts +8 -8
  87. package/src/util/defer.ts +7 -5
  88. package/src/util/error.ts +24 -19
  89. package/src/util/eventloop.ts +16 -10
  90. package/src/util/filesystem.ts +37 -33
  91. package/src/util/fn.ts +11 -8
  92. package/src/util/iife.ts +1 -1
  93. package/src/util/keybind.ts +44 -44
  94. package/src/util/lazy.ts +7 -7
  95. package/src/util/locale.ts +20 -16
  96. package/src/util/lock.ts +43 -38
  97. package/src/util/log.ts +95 -85
  98. package/src/util/queue.ts +8 -8
  99. package/src/util/rpc.ts +35 -23
  100. package/src/util/scrap.ts +4 -4
  101. package/src/util/signal.ts +5 -5
  102. package/src/util/timeout.ts +6 -6
  103. package/src/util/token.ts +2 -2
  104. package/src/util/wildcard.ts +38 -27
@@ -1,107 +1,116 @@
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"
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: "session.compaction" })
20
+ const log = Log.create({ service: 'session.compaction' });
21
21
 
22
22
  export const Event = {
23
23
  Compacted: Bus.event(
24
- "session.compacted",
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: { tokens: MessageV2.Assistant["tokens"]; model: ModelsDev.Model }) {
32
- if (Flag.OPENCODE_DISABLE_AUTOCOMPACT) return false
33
- const context = input.model.limit.context
34
- if (context === 0) return false
35
- const count = input.tokens.input + input.tokens.cache.read + input.tokens.output
36
- const output = Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || SessionPrompt.OUTPUT_TOKEN_MAX
37
- const usable = context - output
38
- return count > usable
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("pruning")
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 === "user") turns++
59
- if (turns < 2) continue
60
- if (msg.info.role === "assistant" && msg.info.summary) break loop
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 === "tool")
64
- if (part.state.status === "completed") {
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("found", { pruned, total })
81
+ log.info('found', { pruned, total });
76
82
  if (pruned > PRUNE_MINIMUM) {
77
83
  for (const part of toPrune) {
78
- if (part.state.status === "completed") {
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("pruned", { count: toPrune.length })
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(input.model.providerID, input.model.modelID)
98
- const system = [...SystemPrompt.summarize(model.providerID)]
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("message"),
101
- role: "assistant",
109
+ id: Identifier.ascending('message'),
110
+ role: 'assistant',
102
111
  parentID: input.parentID,
103
112
  sessionID: input.sessionID,
104
- mode: "build",
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("stream 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(model.npm, model.providerID, {
140
- ...ProviderTransform.options(model.providerID, model.modelID, model.npm ?? "", input.sessionID),
141
- ...model.info.options,
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: "system",
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 !== "assistant" || m.info.error === undefined) {
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((part) => part.type !== "step-start" && part.type !== "reasoning")
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: "user",
190
+ role: 'user',
170
191
  content: [
171
192
  {
172
- type: "text",
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 === "stream") {
204
+ if (args.type === 'stream') {
184
205
  // @ts-expect-error
185
- args.params.prompt = ProviderTransform.message(args.params.prompt, model.providerID, model.modelID)
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 === "continue") {
217
+ })
218
+ );
219
+ if (result === 'continue') {
195
220
  const continueMsg = await Session.updateMessage({
196
- id: Identifier.ascending("message"),
197
- role: "user",
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: "build",
227
+ agent: 'build',
203
228
  model: input.model,
204
- })
229
+ });
205
230
  await Session.updatePart({
206
- id: Identifier.ascending("part"),
231
+ id: Identifier.ascending('part'),
207
232
  messageID: continueMsg.id,
208
233
  sessionID: input.sessionID,
209
- type: "text",
234
+ type: 'text',
210
235
  synthetic: true,
211
- text: "Continue if you have next steps",
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 "stop"
219
- return "continue"
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("session"),
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("message"),
233
- role: "user",
257
+ id: Identifier.ascending('message'),
258
+ role: 'user',
234
259
  model: input.model,
235
260
  sessionID: input.sessionID,
236
- agent: "build",
261
+ agent: 'build',
237
262
  time: {
238
263
  created: Date.now(),
239
264
  },
240
- })
265
+ });
241
266
  await Session.updatePart({
242
- id: Identifier.ascending("part"),
267
+ id: Identifier.ascending('part'),
243
268
  messageID: msg.id,
244
269
  sessionID: msg.sessionID,
245
- type: "compaction",
246
- })
247
- },
248
- )
270
+ type: 'compaction',
271
+ });
272
+ }
273
+ );
249
274
  }