@link-assistant/agent 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -178,7 +178,7 @@ export namespace Session {
178
178
  updated: Date.now(),
179
179
  },
180
180
  };
181
- log.info('created', result);
181
+ log.info(() => ({ message: 'created', ...result }));
182
182
  await Storage.write(['session', Instance.project.id, result.id], result);
183
183
  Bus.publish(Event.Created, {
184
184
  info: result,
@@ -273,7 +273,7 @@ export namespace Session {
273
273
  info: session,
274
274
  });
275
275
  } catch (e) {
276
- log.error(e);
276
+ log.error(() => ({ error: e }));
277
277
  }
278
278
  });
279
279
 
@@ -39,7 +39,7 @@ export namespace SessionProcessor {
39
39
  return toolcalls[toolCallID];
40
40
  },
41
41
  async process(fn: () => StreamTextResult<Record<string, AITool>, never>) {
42
- log.info('process');
42
+ log.info(() => ({ message: 'process' }));
43
43
  while (true) {
44
44
  try {
45
45
  let currentText: MessageV2.TextPart | undefined;
@@ -305,16 +305,12 @@ export namespace SessionProcessor {
305
305
  break;
306
306
 
307
307
  default:
308
- log.info('unhandled', {
309
- ...value,
310
- });
308
+ log.info(() => ({ message: 'unhandled', ...value }));
311
309
  continue;
312
310
  }
313
311
  }
314
312
  } catch (e) {
315
- log.error('process', {
316
- error: e,
317
- });
313
+ log.error(() => ({ message: 'process', error: e }));
318
314
  const error = MessageV2.fromError(e, {
319
315
  providerID: input.providerID,
320
316
  });
@@ -216,7 +216,7 @@ export namespace SessionPrompt {
216
216
  }
217
217
 
218
218
  export function cancel(sessionID: string) {
219
- log.info('cancel', { sessionID });
219
+ log.info(() => ({ message: 'cancel', sessionID }));
220
220
  const s = state();
221
221
  const match = s[sessionID];
222
222
  if (!match) return;
@@ -242,7 +242,7 @@ export namespace SessionPrompt {
242
242
 
243
243
  let step = 0;
244
244
  while (true) {
245
- log.info('loop', { step, sessionID });
245
+ log.info(() => ({ message: 'loop', step, sessionID }));
246
246
  if (abort.aborted) break;
247
247
  let msgs = await MessageV2.filterCompacted(MessageV2.stream(sessionID));
248
248
 
@@ -276,7 +276,7 @@ export namespace SessionPrompt {
276
276
  lastAssistant.finish !== 'tool-calls' &&
277
277
  lastUser.id < lastAssistant.id
278
278
  ) {
279
- log.info('exiting loop', { sessionID });
279
+ log.info(() => ({ message: 'exiting loop', sessionID }));
280
280
  break;
281
281
  }
282
282
 
@@ -297,14 +297,13 @@ export namespace SessionPrompt {
297
297
  lastUser.model.modelID
298
298
  );
299
299
  } catch (error) {
300
- log.warn(
301
- 'Failed to initialize specified model, falling back to default model',
302
- {
303
- providerID: lastUser.model.providerID,
304
- modelID: lastUser.model.modelID,
305
- error: error instanceof Error ? error.message : String(error),
306
- }
307
- );
300
+ log.warn(() => ({
301
+ message:
302
+ 'Failed to initialize specified model, falling back to default model',
303
+ providerID: lastUser.model.providerID,
304
+ modelID: lastUser.model.modelID,
305
+ error: error instanceof Error ? error.message : String(error),
306
+ }));
308
307
  const defaultModel = await Provider.defaultModel();
309
308
  model = await Provider.getModel(
310
309
  defaultModel.providerID,
@@ -551,53 +550,79 @@ export namespace SessionPrompt {
551
550
  );
552
551
  const totalEstimatedTokens = systemTokens + userTokens;
553
552
 
554
- log.info('=== VERBOSE: API Request Details ===');
555
- log.info(`Model: ${model.providerID}/${model.modelID}`);
556
- log.info(`Session ID: ${sessionID}`);
557
- log.info(`Agent: ${agent.name}`);
558
- log.info(`Temperature: ${params.temperature ?? 'default'}`);
559
- log.info(`Top P: ${params.topP ?? 'default'}`);
560
- log.info(
561
- `Active Tools: ${Object.keys(tools)
562
- .filter((x) => x !== 'invalid')
563
- .join(', ')}`
564
- );
565
- log.info('--- System Prompt ---');
553
+ log.info(() => ({
554
+ message: '=== VERBOSE: API Request Details ===',
555
+ }));
556
+ log.info(() => ({
557
+ message: 'Model',
558
+ model: `${model.providerID}/${model.modelID}`,
559
+ }));
560
+ log.info(() => ({ message: 'Session ID', sessionID }));
561
+ log.info(() => ({ message: 'Agent', agent: agent.name }));
562
+ log.info(() => ({
563
+ message: 'Temperature',
564
+ temperature: params.temperature ?? 'default',
565
+ }));
566
+ log.info(() => ({
567
+ message: 'Top P',
568
+ topP: params.topP ?? 'default',
569
+ }));
570
+ log.info(() => ({
571
+ message: 'Active Tools',
572
+ tools: Object.keys(tools).filter((x) => x !== 'invalid'),
573
+ }));
574
+ log.info(() => ({ message: '--- System Prompt ---' }));
566
575
  for (let i = 0; i < system.length; i++) {
567
576
  const tokens = Token.estimate(system[i]);
568
- log.info(`System Message ${i + 1} (${tokens} tokens estimated):`);
569
- log.info(
570
- system[i].slice(0, 2000) +
571
- (system[i].length > 2000 ? '... [truncated]' : '')
572
- );
577
+ log.info(() => ({
578
+ message: 'System Message',
579
+ index: i + 1,
580
+ tokens,
581
+ }));
582
+ log.info(() => ({
583
+ message: 'System Message Content',
584
+ content:
585
+ system[i].slice(0, 2000) +
586
+ (system[i].length > 2000 ? '... [truncated]' : ''),
587
+ }));
573
588
  }
574
- log.info('--- Token Summary ---');
575
- log.info(`System prompt tokens (estimated): ${systemTokens}`);
576
- log.info(`User message tokens (estimated): ${userTokens}`);
577
- log.info(`Total estimated tokens: ${totalEstimatedTokens}`);
578
- log.info(
579
- `Model context limit: ${model.info?.limit?.context || 'unknown'}`
580
- );
581
- log.info(
582
- `Model output limit: ${model.info?.limit?.output || 'unknown'}`
583
- );
584
- log.info('=== END VERBOSE ===');
589
+ log.info(() => ({ message: '--- Token Summary ---' }));
590
+ log.info(() => ({
591
+ message: 'System prompt tokens (estimated)',
592
+ systemTokens,
593
+ }));
594
+ log.info(() => ({
595
+ message: 'User message tokens (estimated)',
596
+ userTokens,
597
+ }));
598
+ log.info(() => ({
599
+ message: 'Total estimated tokens',
600
+ totalEstimatedTokens,
601
+ }));
602
+ log.info(() => ({
603
+ message: 'Model context limit',
604
+ contextLimit: model.info?.limit?.context || 'unknown',
605
+ }));
606
+ log.info(() => ({
607
+ message: 'Model output limit',
608
+ outputLimit: model.info?.limit?.output || 'unknown',
609
+ }));
610
+ log.info(() => ({ message: '=== END VERBOSE ===' }));
585
611
  }
586
612
 
587
613
  const result = await processor.process(() =>
588
614
  streamText({
589
615
  onError(error) {
590
- log.error('stream error', {
591
- error,
592
- });
616
+ log.error(() => ({ message: 'stream error', error }));
593
617
  },
594
618
  async experimental_repairToolCall(input) {
595
619
  const lower = input.toolCall.toolName.toLowerCase();
596
620
  if (lower !== input.toolCall.toolName && tools[lower]) {
597
- log.info('repairing tool call', {
621
+ log.info(() => ({
622
+ message: 'repairing tool call',
598
623
  tool: input.toolCall.toolName,
599
624
  repaired: lower,
600
- });
625
+ }));
601
626
  return {
602
627
  ...input.toolCall,
603
628
  toolName: lower,
@@ -945,7 +970,7 @@ export namespace SessionPrompt {
945
970
  }
946
971
  break;
947
972
  case 'file:':
948
- log.info('file', { mime: part.mime });
973
+ log.info(() => ({ message: 'file', mime: part.mime }));
949
974
  // have to normalize, symbol search returns absolute paths
950
975
  // Decode the pathname since URL constructor doesn't automatically decode it
951
976
  const filepath = fileURLToPath(part.url);
@@ -1012,7 +1037,10 @@ export namespace SessionPrompt {
1012
1037
  );
1013
1038
  })
1014
1039
  .catch((error) => {
1015
- log.error('failed to read file', { error });
1040
+ log.error(() => ({
1041
+ message: 'failed to read file',
1042
+ error,
1043
+ }));
1016
1044
  const message =
1017
1045
  error instanceof Error ? error.message : error.toString();
1018
1046
  Bus.publish(Session.Event.Error, {
@@ -1376,7 +1404,7 @@ export namespace SessionPrompt {
1376
1404
  */
1377
1405
 
1378
1406
  export async function command(input: CommandInput) {
1379
- log.info('command', input);
1407
+ log.info(() => ({ message: 'command', ...input }));
1380
1408
  const command = await Command.get(input.command);
1381
1409
  const agentName = command.agent ?? input.agent ?? 'build';
1382
1410
 
@@ -1572,10 +1600,11 @@ export namespace SessionPrompt {
1572
1600
  });
1573
1601
  })
1574
1602
  .catch((error) => {
1575
- log.error('failed to generate title', {
1603
+ log.error(() => ({
1604
+ message: 'failed to generate title',
1576
1605
  error,
1577
1606
  model: small.info?.id ?? small.modelID,
1578
- });
1607
+ }));
1579
1608
  });
1580
1609
  }
1581
1610
  }
@@ -72,7 +72,7 @@ export namespace SessionRevert {
72
72
  }
73
73
 
74
74
  export async function unrevert(input: { sessionID: string }) {
75
- log.info('unreverting', input);
75
+ log.info(() => ({ message: 'unreverting', ...input }));
76
76
  SessionPrompt.assertNotBusy(input.sessionID);
77
77
  const session = await Session.get(input.sessionID);
78
78
  if (!session.revert) return session;
@@ -115,7 +115,7 @@ export namespace SessionSummary {
115
115
  headers: small.info.headers,
116
116
  model: small.language,
117
117
  });
118
- log.info('title', { title: result.text });
118
+ log.info(() => ({ message: 'title', title: result.text }));
119
119
  userMsg.summary.title = result.text;
120
120
  await Session.updateMessage(userMsg);
121
121
  }
@@ -152,7 +152,7 @@ export namespace SessionSummary {
152
152
  if (result) summary = result.text;
153
153
  }
154
154
  userMsg.summary.body = summary;
155
- log.info('body', { body: summary });
155
+ log.info(() => ({ message: 'body', body: summary }));
156
156
  await Session.updateMessage(userMsg);
157
157
  }
158
158
  }
@@ -28,7 +28,7 @@ export namespace Snapshot {
28
28
  await $`git --git-dir ${git} config core.autocrlf false`
29
29
  .quiet()
30
30
  .nothrow();
31
- log.info('initialized');
31
+ log.info(() => ({ message: 'initialized' }));
32
32
  }
33
33
  await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`
34
34
  .quiet()
@@ -40,7 +40,12 @@ export namespace Snapshot {
40
40
  .cwd(Instance.directory)
41
41
  .nothrow()
42
42
  .text();
43
- log.info('tracking', { hash, cwd: Instance.directory, git });
43
+ log.info(() => ({
44
+ message: 'tracking',
45
+ hash,
46
+ cwd: Instance.directory,
47
+ git,
48
+ }));
44
49
  return hash.trim();
45
50
  }
46
51
 
@@ -64,7 +69,11 @@ export namespace Snapshot {
64
69
 
65
70
  // If git diff fails, return empty patch
66
71
  if (result.exitCode !== 0) {
67
- log.warn('failed to get diff', { hash, exitCode: result.exitCode });
72
+ log.warn(() => ({
73
+ message: 'failed to get diff',
74
+ hash,
75
+ exitCode: result.exitCode,
76
+ }));
68
77
  return { hash, files: [] };
69
78
  }
70
79
 
@@ -81,7 +90,7 @@ export namespace Snapshot {
81
90
  }
82
91
 
83
92
  export async function restore(snapshot: string) {
84
- log.info('restore', { commit: snapshot });
93
+ log.info(() => ({ message: 'restore', commit: snapshot }));
85
94
  const git = gitdir();
86
95
  const result =
87
96
  await $`git --git-dir ${git} --work-tree ${Instance.worktree} read-tree ${snapshot} && git --git-dir ${git} --work-tree ${Instance.worktree} checkout-index -a -f`
@@ -90,12 +99,13 @@ export namespace Snapshot {
90
99
  .nothrow();
91
100
 
92
101
  if (result.exitCode !== 0) {
93
- log.error('failed to restore snapshot', {
102
+ log.error(() => ({
103
+ message: 'failed to restore snapshot',
94
104
  snapshot,
95
105
  exitCode: result.exitCode,
96
106
  stderr: result.stderr.toString(),
97
107
  stdout: result.stdout.toString(),
98
- });
108
+ }));
99
109
  }
100
110
  }
101
111
 
@@ -105,7 +115,7 @@ export namespace Snapshot {
105
115
  for (const item of patches) {
106
116
  for (const file of item.files) {
107
117
  if (files.has(file)) continue;
108
- log.info('reverting', { file, hash: item.hash });
118
+ log.info(() => ({ message: 'reverting', file, hash: item.hash }));
109
119
  const result =
110
120
  await $`git --git-dir ${git} --work-tree ${Instance.worktree} checkout ${item.hash} -- ${file}`
111
121
  .quiet()
@@ -119,11 +129,15 @@ export namespace Snapshot {
119
129
  .cwd(Instance.worktree)
120
130
  .nothrow();
121
131
  if (checkTree.exitCode === 0 && checkTree.text().trim()) {
122
- log.info('file existed in snapshot but checkout failed, keeping', {
132
+ log.info(() => ({
133
+ message: 'file existed in snapshot but checkout failed, keeping',
123
134
  file,
124
- });
135
+ }));
125
136
  } else {
126
- log.info('file did not exist in snapshot, deleting', { file });
137
+ log.info(() => ({
138
+ message: 'file did not exist in snapshot, deleting',
139
+ file,
140
+ }));
127
141
  await fs.unlink(file).catch(() => {});
128
142
  }
129
143
  }
@@ -145,12 +159,13 @@ export namespace Snapshot {
145
159
  .nothrow();
146
160
 
147
161
  if (result.exitCode !== 0) {
148
- log.warn('failed to get diff', {
162
+ log.warn(() => ({
163
+ message: 'failed to get diff',
149
164
  hash,
150
165
  exitCode: result.exitCode,
151
166
  stderr: result.stderr.toString(),
152
167
  stdout: result.stdout.toString(),
153
- });
168
+ }));
154
169
  return '';
155
170
  }
156
171
 
@@ -28,7 +28,7 @@ export namespace Storage {
28
28
  cwd: project,
29
29
  onlyFiles: false,
30
30
  })) {
31
- log.info(`migrating project ${projectDir}`);
31
+ log.info(() => ({ message: 'migrating project', projectDir }));
32
32
  let projectID = projectDir;
33
33
  const fullProjectDir = path.join(project, projectDir);
34
34
  let worktree = '/';
@@ -74,7 +74,10 @@ export namespace Storage {
74
74
  })
75
75
  );
76
76
 
77
- log.info(`migrating sessions for project ${projectID}`);
77
+ log.info(() => ({
78
+ message: 'migrating sessions for project',
79
+ projectID,
80
+ }));
78
81
  for await (const sessionFile of new Bun.Glob(
79
82
  'storage/session/info/*.json'
80
83
  ).scan({
@@ -87,13 +90,13 @@ export namespace Storage {
87
90
  projectID,
88
91
  path.basename(sessionFile)
89
92
  );
90
- log.info('copying', {
91
- sessionFile,
92
- dest,
93
- });
93
+ log.info(() => ({ message: 'copying', sessionFile, dest }));
94
94
  const session = await Bun.file(sessionFile).json();
95
95
  await Bun.write(dest, JSON.stringify(session));
96
- log.info(`migrating messages for session ${session.id}`);
96
+ log.info(() => ({
97
+ message: 'migrating messages for session',
98
+ sessionID: session.id,
99
+ }));
97
100
  for await (const msgFile of new Bun.Glob(
98
101
  `storage/session/message/${session.id}/*.json`
99
102
  ).scan({
@@ -106,14 +109,14 @@ export namespace Storage {
106
109
  session.id,
107
110
  path.basename(msgFile)
108
111
  );
109
- log.info('copying', {
110
- msgFile,
111
- dest,
112
- });
112
+ log.info(() => ({ message: 'copying', msgFile, dest }));
113
113
  const message = await Bun.file(msgFile).json();
114
114
  await Bun.write(dest, JSON.stringify(message));
115
115
 
116
- log.info(`migrating parts for message ${message.id}`);
116
+ log.info(() => ({
117
+ message: 'migrating parts for message',
118
+ messageID: message.id,
119
+ }));
117
120
  for await (const partFile of new Bun.Glob(
118
121
  `storage/session/part/${session.id}/${message.id}/*.json`
119
122
  ).scan({
@@ -127,10 +130,7 @@ export namespace Storage {
127
130
  path.basename(partFile)
128
131
  );
129
132
  const part = await Bun.file(partFile).json();
130
- log.info('copying', {
131
- partFile,
132
- dest,
133
- });
133
+ log.info(() => ({ message: 'copying', partFile, dest }));
134
134
  await Bun.write(dest, JSON.stringify(part));
135
135
  }
136
136
  }
@@ -178,10 +178,10 @@ export namespace Storage {
178
178
  .then((x) => parseInt(x))
179
179
  .catch(() => 0);
180
180
  for (let index = migration; index < MIGRATIONS.length; index++) {
181
- log.info('running migration', { index });
181
+ log.info(() => ({ message: 'running migration', index }));
182
182
  const migration = MIGRATIONS[index];
183
183
  await migration(dir).catch(() =>
184
- log.error('failed to run migration', { index })
184
+ log.error(() => ({ message: 'failed to run migration', index }))
185
185
  );
186
186
  await Bun.write(path.join(dir, 'migration'), (index + 1).toString());
187
187
  }