@amalgm/chat 0.2.8 → 0.2.9-main.34746990150

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 (114) hide show
  1. package/PURPOSE.md +94 -41
  2. package/README.md +17 -4
  3. package/dist/acp/projection.d.ts +8 -0
  4. package/dist/acp/projection.d.ts.map +1 -1
  5. package/dist/acp/projection.js +10 -3
  6. package/dist/acp/projection.js.map +1 -1
  7. package/dist/conversations/index.d.ts +1 -0
  8. package/dist/conversations/index.d.ts.map +1 -1
  9. package/dist/conversations/index.js +1 -0
  10. package/dist/conversations/index.js.map +1 -1
  11. package/dist/conversations/transcript-parts.d.ts +38 -0
  12. package/dist/conversations/transcript-parts.d.ts.map +1 -0
  13. package/dist/conversations/transcript-parts.js +2 -0
  14. package/dist/conversations/transcript-parts.js.map +1 -0
  15. package/dist/conversations/transcript-tools.d.ts +11 -0
  16. package/dist/conversations/transcript-tools.d.ts.map +1 -0
  17. package/dist/conversations/transcript-tools.js +47 -0
  18. package/dist/conversations/transcript-tools.js.map +1 -0
  19. package/dist/conversations/transcript.d.ts +5 -4
  20. package/dist/conversations/transcript.d.ts.map +1 -1
  21. package/dist/conversations/transcript.js +43 -143
  22. package/dist/conversations/transcript.js.map +1 -1
  23. package/dist/conversations/types.d.ts +19 -0
  24. package/dist/conversations/types.d.ts.map +1 -1
  25. package/dist/conversations/types.js.map +1 -1
  26. package/dist/events/constructors.d.ts.map +1 -1
  27. package/dist/events/constructors.js +2 -52
  28. package/dist/events/constructors.js.map +1 -1
  29. package/dist/events/schema.d.ts +72 -72
  30. package/dist/events/schema.js +16 -16
  31. package/dist/events/schema.js.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/input/prompt.js +1 -1
  37. package/dist/input/prompt.js.map +1 -1
  38. package/dist/mcp/server.js +13 -13
  39. package/dist/mcp/server.js.map +1 -1
  40. package/dist/normalizers/claude.d.ts +142 -142
  41. package/dist/normalizers/claude.d.ts.map +1 -1
  42. package/dist/normalizers/claude.js +23 -37
  43. package/dist/normalizers/claude.js.map +1 -1
  44. package/dist/normalizers/codex.d.ts +71 -71
  45. package/dist/normalizers/codex.d.ts.map +1 -1
  46. package/dist/normalizers/codex.js +12 -24
  47. package/dist/normalizers/codex.js.map +1 -1
  48. package/dist/normalizers/index.d.ts +0 -1
  49. package/dist/normalizers/index.d.ts.map +1 -1
  50. package/dist/normalizers/index.js +0 -1
  51. package/dist/normalizers/index.js.map +1 -1
  52. package/dist/normalizers/opencode.d.ts.map +1 -1
  53. package/dist/normalizers/opencode.js +9 -6
  54. package/dist/normalizers/opencode.js.map +1 -1
  55. package/dist/normalizers/pi.d.ts.map +1 -1
  56. package/dist/normalizers/pi.js +27 -14
  57. package/dist/normalizers/pi.js.map +1 -1
  58. package/dist/sessions/chat.d.ts +5 -5
  59. package/dist/sessions/chat.d.ts.map +1 -1
  60. package/dist/sessions/chat.js +51 -19
  61. package/dist/sessions/chat.js.map +1 -1
  62. package/dist/sessions/index.d.ts +1 -1
  63. package/dist/sessions/index.d.ts.map +1 -1
  64. package/dist/sessions/index.js +1 -1
  65. package/dist/sessions/index.js.map +1 -1
  66. package/dist/usage/acp.d.ts +4 -0
  67. package/dist/usage/acp.d.ts.map +1 -0
  68. package/dist/usage/acp.js +20 -0
  69. package/dist/usage/acp.js.map +1 -0
  70. package/dist/usage/finalize.d.ts +1 -11
  71. package/dist/usage/finalize.d.ts.map +1 -1
  72. package/dist/usage/finalize.js +29 -47
  73. package/dist/usage/finalize.js.map +1 -1
  74. package/dist/usage/index.d.ts +2 -1
  75. package/dist/usage/index.d.ts.map +1 -1
  76. package/dist/usage/index.js +2 -1
  77. package/dist/usage/index.js.map +1 -1
  78. package/dist/usage/normalize.d.ts +8 -9
  79. package/dist/usage/normalize.d.ts.map +1 -1
  80. package/dist/usage/normalize.js +20 -20
  81. package/dist/usage/normalize.js.map +1 -1
  82. package/dist/usage/ports.d.ts +4 -4
  83. package/dist/usage/ports.d.ts.map +1 -1
  84. package/dist/usage/ports.js +5 -5
  85. package/dist/usage/ports.js.map +1 -1
  86. package/dist/usage/tokens.d.ts +13 -38
  87. package/dist/usage/tokens.d.ts.map +1 -1
  88. package/dist/usage/tokens.js +39 -72
  89. package/dist/usage/tokens.js.map +1 -1
  90. package/host/adapters/acp-prompt.js +36 -0
  91. package/host/adapters/acp.js +50 -71
  92. package/host/adapters/attachments.js +37 -0
  93. package/host/adapters/claude.js +9 -2
  94. package/host/adapters/codex.js +36 -18
  95. package/host/adapters/cursor.js +72 -99
  96. package/host/adapters/opencode.js +39 -25
  97. package/host/adapters/pi.js +24 -19
  98. package/host/adapters/prompt.js +13 -21
  99. package/host/adapters/provider-url.js +8 -0
  100. package/host/auth.js +10 -9
  101. package/host/http.js +3 -2
  102. package/host/model-resolution.js +7 -0
  103. package/host/native-contract.js +4 -3
  104. package/host/native-runtime.js +25 -11
  105. package/host/sqlite/index.d.ts +2 -0
  106. package/host/sqlite/index.js +48 -4
  107. package/host/tooling/runtime-home.js +1 -1
  108. package/host/tooling/system-instructions.js +7 -6
  109. package/host/tooling/system-prompt.js +12 -18
  110. package/package.json +26 -2
  111. package/dist/normalizers/cursor.d.ts +0 -33
  112. package/dist/normalizers/cursor.d.ts.map +0 -1
  113. package/dist/normalizers/cursor.js +0 -196
  114. package/dist/normalizers/cursor.js.map +0 -1
@@ -6,6 +6,7 @@ import { OpenCodeAdapter } from './adapters/opencode.js';
6
6
  import { PiAdapter } from './adapters/pi.js';
7
7
  import { AcpAdapter } from './adapters/acp.js';
8
8
  import { assertExecutionInput } from './adapters/input-capabilities.js';
9
+ import { materializePromptAttachments } from './adapters/attachments.js';
9
10
  import { NativeExecutionPreparer } from './native-contract.js';
10
11
  import { forwardPlatformEgress } from './platform-egress.js';
11
12
  import { generateConversationTitle } from './title-generator.js';
@@ -78,6 +79,8 @@ export class NativeChatRuntime {
78
79
  this.sessions.set(sessionId, session);
79
80
  try {
80
81
  session.native = await execution.binding.adapter.create(contract);
82
+ session.recoveryHistory = providerSessionId && session.native?.providerSessionId === providerSessionId
83
+ ? null : messages;
81
84
  } catch (error) {
82
85
  this.sessions.delete(sessionId);
83
86
  throw error;
@@ -99,10 +102,12 @@ export class NativeChatRuntime {
99
102
  null,
100
103
  );
101
104
  const adapter = request.execution.binding.adapter;
105
+ const native = await adapter.create(contract);
102
106
  state.adapter = adapter;
103
107
  state.contract = contract;
104
108
  state.executionRevision = request.execution.revision;
105
- state.native = await adapter.create(contract);
109
+ state.native = native;
110
+ state.recoveryHistory = request.previousMessages;
106
111
  }
107
112
 
108
113
  const contract = this.preparer.forSession(
@@ -112,16 +117,27 @@ export class NativeChatRuntime {
112
117
  state.native?.providerSessionId || state.contract.providerSessionId,
113
118
  );
114
119
  state.contract = contract;
115
- const prompt = state.recoveryHistory
120
+ const input = state.recoveryHistory
116
121
  ? promptWithRecoveryHistory(state.recoveryHistory, request.prompt)
117
122
  : request.prompt;
123
+ const materialize = state.adapter.materializeAttachments ?? state.adapter.outputType !== 'acp';
124
+ const prompt = materialize ? await materializePromptAttachments(input, this.preparer.runtimeHomeRoot) : input;
118
125
  state.recoveryHistory = null;
119
126
  let sequence = 0;
120
- for await (const event of state.adapter.prompt(state.native, prompt, contract)) {
127
+ for await (const event of state.adapter.prompt(state.native, prompt, contract, { recordUsage: request.recordUsage })) {
121
128
  if (state.adapter.outputType === 'acp') {
122
129
  yield event;
123
130
  continue;
124
131
  }
132
+ if (event.type === 'error' || (event.type === 'done' && event.stopReason === 'error')) {
133
+ throw Object.assign(new Error(event.message || 'The provider turn failed'), {
134
+ code: 'PROVIDER_TURN_FAILED',
135
+ cause: event.raw,
136
+ });
137
+ }
138
+ if (event.type === 'usage.final') {
139
+ await request.recordUsage?.({ usage: event.usage, raw: event.raw ?? null });
140
+ }
125
141
  const update = toAcpSessionUpdate(event, {
126
142
  conversationId: request.sessionId,
127
143
  turnId: request.turnId,
@@ -186,16 +202,14 @@ export class NativeChatRuntime {
186
202
  }
187
203
 
188
204
  function promptWithRecoveryHistory(messages, prompt) {
189
- const lines = [];
190
- for (const message of messages.slice(-40)) {
191
- const label = message.role === 'user' ? 'Human' : 'Assistant';
192
- const text = typeof message.contentPlain === 'string' ? message.contentPlain.trim() : '';
193
- if (text) lines.push(`${label}: ${text}`);
194
- }
195
- if (lines.length === 0) return prompt;
205
+ if (messages.length === 0) return prompt;
196
206
  return {
197
207
  parts: [
198
- { type: 'text', text: `<conversation_history>\n${lines.join('\n')}\n</conversation_history>\n\n` },
208
+ { type: 'resource', resource: {
209
+ uri: 'amalgm:conversation-history',
210
+ mimeType: 'application/json',
211
+ text: JSON.stringify(messages.map(({ role, parts, contentPlain, error }) => ({ role, parts, contentPlain, error }))),
212
+ } },
199
213
  ...prompt.parts,
200
214
  ],
201
215
  };
@@ -1,6 +1,7 @@
1
1
  import type Database from 'better-sqlite3';
2
2
  import type {
3
3
  AppendTurnUpdateInput,
4
+ AppendTurnUsageInput,
4
5
  AppendTurnUpdatesInput,
5
6
  BeginConversationTurnInput,
6
7
  ConversationMessage,
@@ -56,6 +57,7 @@ export class SqliteConversationStore implements ConversationStorePort {
56
57
  }, updatedAt: string): ConversationRecord;
57
58
  archiveConversation(scope: ConversationScope, conversationId: string, archivedAt: string): Promise<ConversationRecord>;
58
59
  beginTurn(input: BeginConversationTurnInput): Promise<ConversationTurn>;
60
+ appendTurnUsage(input: AppendTurnUsageInput): Promise<void>;
59
61
  appendTurnUpdate(input: AppendTurnUpdateInput): Promise<StoredTurnUpdate>;
60
62
  appendTurnUpdates(input: AppendTurnUpdatesInput): Promise<readonly StoredTurnUpdate[]>;
61
63
  sealTurn(input: SealConversationTurnInput): Promise<ConversationTurn>;
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import Database from 'better-sqlite3';
4
+ import { finalizeTurnUsage, normalizeUsage } from '../../dist/usage/index.js';
4
5
  import {
5
6
  applyAcpTranscriptUpdate,
6
7
  createAcpTranscriptState,
@@ -8,7 +9,7 @@ import {
8
9
  promptContentPlain,
9
10
  } from '../../dist/conversations/index.js';
10
11
 
11
- const SCHEMA_VERSION = 2;
12
+ const SCHEMA_VERSION = 3;
12
13
  const TERMINAL = new Set(['completed', 'failed', 'cancelled', 'interrupted']);
13
14
 
14
15
  export class SqliteConversationStoreError extends Error {
@@ -77,12 +78,22 @@ function conversationFromRow(row) {
77
78
  }
78
79
 
79
80
  function turnFromRow(row) {
81
+ const execution = Object.freeze(parseJson(row.execution_json, {}));
82
+ const observations = Object.freeze(parseJson(row.usage_json, []));
83
+ const records = observations.map(record => record.usage);
84
+ const usage = finalizeTurnUsage({
85
+ contract: { harness: execution.agent?.adapterId },
86
+ usage: records.at(-1), usageRecords: records,
87
+ });
80
88
  return Object.freeze({
89
+ contextUsage: parseJson(row.context_usage_json, null),
90
+ usage: usage ? Object.freeze(usage) : null,
91
+ usageRecords: observations,
81
92
  id: row.id,
82
93
  conversationId: row.conversation_id,
83
94
  status: row.status,
84
95
  prompt: Object.freeze(parseJson(row.prompt_json, { parts: [] })),
85
- execution: Object.freeze(parseJson(row.execution_json, {})),
96
+ execution,
86
97
  error: row.error_json ? Object.freeze(parseJson(row.error_json, {})) : null,
87
98
  createdAt: row.created_at,
88
99
  startedAt: row.started_at,
@@ -110,6 +121,7 @@ function messageFromRow(row) {
110
121
  parts: Object.freeze(parseJson(row.parts_json, [])),
111
122
  contentPlain: row.content_plain,
112
123
  modelId: row.model_id,
124
+ error: row.role === 'assistant' && row.error_json ? Object.freeze(parseJson(row.error_json, {})) : null,
113
125
  createdAt: row.created_at,
114
126
  updatedAt: row.updated_at,
115
127
  });
@@ -134,6 +146,18 @@ function migrate(database) {
134
146
  );
135
147
  }
136
148
  if (version === SCHEMA_VERSION) return;
149
+ if (version === 2) {
150
+ database.exec(`
151
+ BEGIN IMMEDIATE;
152
+ ALTER TABLE turns ADD COLUMN usage_json TEXT NOT NULL DEFAULT '[]';
153
+ ALTER TABLE turns ADD COLUMN context_usage_json TEXT;
154
+ INSERT INTO schema_migrations(version, applied_at)
155
+ VALUES (3, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
156
+ PRAGMA user_version = 3;
157
+ COMMIT;
158
+ `);
159
+ return;
160
+ }
137
161
  if (version === 1) {
138
162
  database.exec(`
139
163
  BEGIN IMMEDIATE;
@@ -144,6 +168,7 @@ function migrate(database) {
144
168
  PRAGMA user_version = 2;
145
169
  COMMIT;
146
170
  `);
171
+ migrate(database);
147
172
  return;
148
173
  }
149
174
  database.exec(`
@@ -181,6 +206,8 @@ function migrate(database) {
181
206
  prompt_json TEXT NOT NULL,
182
207
  execution_json TEXT NOT NULL,
183
208
  error_json TEXT,
209
+ usage_json TEXT NOT NULL DEFAULT '[]',
210
+ context_usage_json TEXT,
184
211
  created_at TEXT NOT NULL,
185
212
  started_at TEXT NOT NULL,
186
213
  completed_at TEXT,
@@ -237,7 +264,9 @@ function migrate(database) {
237
264
  VALUES (1, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
238
265
  INSERT INTO schema_migrations(version, applied_at)
239
266
  VALUES (2, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
240
- PRAGMA user_version = 2;
267
+ INSERT INTO schema_migrations(version, applied_at)
268
+ VALUES (3, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
269
+ PRAGMA user_version = 3;
241
270
  COMMIT;
242
271
  `);
243
272
  }
@@ -506,6 +535,18 @@ export class SqliteConversationStore {
506
535
  }
507
536
  }
508
537
 
538
+ async appendTurnUsage(input) {
539
+ this.database.transaction(() => {
540
+ const turn = this.#requireTurn(input, input.turnId);
541
+ if (turn.conversation_id !== input.conversationId || turn.status !== 'running') {
542
+ throw new SqliteConversationStoreError('TURN_NOT_RUNNING', `Turn is not running: ${input.turnId}`);
543
+ }
544
+ const records = parseJson(turn.usage_json, []);
545
+ records.push({ usage: normalizeUsage(input.observation.usage), raw: input.observation.raw ?? null });
546
+ this.database.prepare('UPDATE turns SET usage_json = ? WHERE id = ?').run(stringify(records), turn.id);
547
+ })();
548
+ }
549
+
509
550
  async appendTurnUpdate(input) {
510
551
  const updates = await this.appendTurnUpdates({
511
552
  ...input,
@@ -543,6 +584,8 @@ export class SqliteConversationStore {
543
584
  sequence += 1;
544
585
  }
545
586
  this.database.prepare('UPDATE turns SET next_sequence = ? WHERE id = ?').run(sequence, turn.id);
587
+ const context = input.updates.findLast(item => item.update.sessionUpdate === 'usage_update');
588
+ if (context) this.database.prepare('UPDATE turns SET context_usage_json = ? WHERE id = ?').run(stringify(context.update), turn.id);
546
589
  return stored;
547
590
  });
548
591
  return transaction().map(updateFromRow);
@@ -641,7 +684,8 @@ export class SqliteConversationStore {
641
684
  async listMessages(scope, conversationId) {
642
685
  const conversation = this.#requireConversation(scope, conversationId);
643
686
  return this.database.prepare(`
644
- SELECT * FROM messages WHERE conversation_id = ? ORDER BY created_at ASC, role DESC
687
+ SELECT m.*, t.error_json FROM messages m JOIN turns t ON t.id = m.turn_id
688
+ WHERE m.conversation_id = ? ORDER BY m.created_at ASC, m.role DESC
645
689
  `).all(conversation.id).map(messageFromRow);
646
690
  }
647
691
 
@@ -1,4 +1,4 @@
1
- /** Prepare the one managed CLI home owned by an agent installation. */
1
+ /** Prepare the managed CLI home owned by an installation and auth authority. */
2
2
 
3
3
  import fs from 'node:fs';
4
4
  import { runtimeEnv } from '../auth.js';
@@ -38,8 +38,8 @@ function cappedDocContent(content, maxChars = CONTEXT_FILE_MAX_CHARS) {
38
38
  return `${text.slice(0, maxChars).trimEnd()}\n[truncated]`;
39
39
  }
40
40
 
41
- export function systemInstructionsPath() {
42
- return systemContextPaths(amalgmDir()).instructions;
41
+ export function systemInstructionsPath(dir = amalgmDir()) {
42
+ return systemContextPaths(dir).instructions;
43
43
  }
44
44
 
45
45
  function escapeAttribute(value) {
@@ -50,13 +50,14 @@ function escapeAttribute(value) {
50
50
  .replace(/>/g, '&gt;');
51
51
  }
52
52
 
53
- export function systemInstructionsBlock() {
54
- const filePath = systemInstructionsPath();
53
+ export function systemInstructionsBlock(dir) {
54
+ const filePath = systemInstructionsPath(dir);
55
55
  let content = '';
56
56
  try {
57
57
  content = fs.readFileSync(filePath, 'utf8');
58
- } catch {
59
- return '';
58
+ } catch (error) {
59
+ if (error.code === 'ENOENT') return '';
60
+ throw error;
60
61
  }
61
62
  if (isEmptyContextDoc(content)) return '';
62
63
  return [
@@ -4,27 +4,12 @@ function trimBlock(value) {
4
4
  return String(value || '').trim();
5
5
  }
6
6
 
7
- function safeBlock(label, render) {
8
- if (typeof render !== 'function') return '';
9
- try {
10
- return trimBlock(render());
11
- } catch (error) {
12
- const message = error instanceof Error ? error.message.split('\n')[0] : String(error);
13
- console.warn(`[chat] ${label} failed:`, message);
14
- return '';
15
- }
16
- }
17
-
18
7
  /** Compose machine, project, and exact agent-revision instructions at preparation. */
19
8
  export function composeSystemPrompt(contract, options = {}) {
20
9
  const parts = [];
21
- const systemInstructions = safeBlock('system instructions', systemInstructionsBlock);
22
- const projectContext = safeBlock(
23
- 'project context',
24
- typeof options.projectContextPromptBlock === 'function'
25
- ? () => options.projectContextPromptBlock(contract)
26
- : null,
27
- );
10
+ const systemInstructions = systemInstructionsBlock(options.amalgmDir);
11
+ const projectContext = typeof options.projectContextPromptBlock === 'function'
12
+ ? trimBlock(options.projectContextPromptBlock(contract)) : '';
28
13
  const agentInstructions = trimBlock(contract?.compiledAgentInstructions);
29
14
  if (systemInstructions) parts.push(systemInstructions);
30
15
  if (projectContext) parts.push(projectContext);
@@ -36,3 +21,12 @@ export function composeSystemPrompt(contract, options = {}) {
36
21
  export function preparedSystemPrompt(contract) {
37
22
  return trimBlock(contract?.instructions?.text);
38
23
  }
24
+
25
+ /** Describe the exact regular-tool projection supplied by the composing host. */
26
+ export function toolSystemInstructions(servers) {
27
+ const tools = servers.flatMap((server) => {
28
+ const selected = server._meta?.['amalgm.dev/tools'];
29
+ return Array.isArray(selected) ? selected.map((tool) => ({ server: server.name, ...tool })) : [];
30
+ });
31
+ return tools.length ? `Selected tools (call through their MCP server):\n${JSON.stringify(tools)}` : '';
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/chat",
3
- "version": "0.2.8",
3
+ "version": "0.2.9-main.34746990150",
4
4
  "description": "A provider-agnostic agent chat SDK with ACP content, prepared execution, normalized streams, and usage.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -86,7 +86,12 @@
86
86
  },
87
87
  "scripts": {
88
88
  "build": "node scripts/clean-dist.mjs && tsc -p tsconfig.json",
89
- "test": "npm run build && node --test"
89
+ "test": "npm run build && node --test",
90
+ "release:admit": "node scripts/release/package.mjs admit",
91
+ "release:prepare": "node scripts/release/package.mjs prepare",
92
+ "release:examine": "node scripts/release/package.mjs examine",
93
+ "release:publish": "node scripts/release/package.mjs publish",
94
+ "test:release": "node --test scripts/release/*.test.mjs"
90
95
  },
91
96
  "dependencies": {
92
97
  "@agentclientprotocol/sdk": "^1.4.0",
@@ -100,5 +105,24 @@
100
105
  "devDependencies": {
101
106
  "@types/better-sqlite3": "^7.6.13",
102
107
  "typescript": "^5.7.3"
108
+ },
109
+ "amalgmRelease": {
110
+ "schema": 1,
111
+ "workflow": "verify.yml",
112
+ "jobs": [
113
+ "verify"
114
+ ],
115
+ "verify": "test",
116
+ "cli": []
117
+ },
118
+ "allowScripts": {
119
+ "better-sqlite3@12.11.1": true
120
+ },
121
+ "gitHead": "d2d09289c5a18d926cf7c4d2f083d32760003d2d",
122
+ "amalgmSource": {
123
+ "repository": "amalgm-inc/amalgm-chat",
124
+ "branch": "main",
125
+ "commit": "d2d09289c5a18d926cf7c4d2f083d32760003d2d",
126
+ "occurrence": "34746990150"
103
127
  }
104
128
  }
@@ -1,33 +0,0 @@
1
- /**
2
- * Cursor ACP normalizer. `cursorStopReason` is pure and exported at
3
- * module level; `normalizeCursorUpdate` / `cursorPromptUsage` construct
4
- * events, so they live behind `createCursorNormalizer(events)`.
5
- *
6
- * Because amalgm's event vocabulary already mirrors ACP `session/update`,
7
- * this is a thin shim, not a per-event translation table.
8
- */
9
- import type { AmalgmEvent, EventConstructors } from '../events/index.js';
10
- /** Engine cursor.js:27-35. ACP PromptResponse stopReason -> amalgm done stopReason. */
11
- export declare function cursorStopReason(stopReason: unknown): string;
12
- /** Per-tool bookkeeping row (engine `state.tools` Map values). */
13
- export interface CursorToolRecord {
14
- toolName: any;
15
- kind: any;
16
- input: any;
17
- }
18
- /**
19
- * Per-prompt normalizer state — created by the adapter per prompt, carries
20
- * `providerSessionId` and the tool-call bookkeeping map (engine contract).
21
- */
22
- export interface CursorNormalizerState {
23
- providerSessionId?: string | null | undefined;
24
- tools: Map<string, CursorToolRecord>;
25
- }
26
- /** The engine `normalizers/cursor.js` event-constructing export set. */
27
- export interface CursorNormalizer {
28
- normalizeCursorUpdate: (update: unknown, state: CursorNormalizerState) => AmalgmEvent[];
29
- cursorPromptUsage: (usage: unknown, base?: Record<string, unknown>) => AmalgmEvent | null;
30
- }
31
- /** Build the Cursor normalizer over injected event constructors. */
32
- export declare function createCursorNormalizer(events: EventConstructors): CursorNormalizer;
33
- //# sourceMappingURL=cursor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/normalizers/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAkBzE,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAO5D;AA8CD,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;IACV,KAAK,EAAE,GAAG,CAAC;CACZ;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACtC;AAcD,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,qBAAqB,KAAK,WAAW,EAAE,CAAC;IACxF,iBAAiB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,WAAW,GAAG,IAAI,CAAC;CAC3F;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,gBAAgB,CAwGlF"}
@@ -1,196 +0,0 @@
1
- /**
2
- * Cursor ACP normalizer. `cursorStopReason` is pure and exported at
3
- * module level; `normalizeCursorUpdate` / `cursorPromptUsage` construct
4
- * events, so they live behind `createCursorNormalizer(events)`.
5
- *
6
- * Because amalgm's event vocabulary already mirrors ACP `session/update`,
7
- * this is a thin shim, not a per-event translation table.
8
- */
9
- /** ACP tool kinds → Amalgm tool kinds. */
10
- const ACP_TOOL_KINDS = {
11
- read: 'file_read',
12
- edit: 'file_edit',
13
- delete: 'file_edit',
14
- move: 'file_write',
15
- search: 'search',
16
- execute: 'bash',
17
- fetch: 'browser',
18
- think: 'unknown',
19
- switch_mode: 'unknown',
20
- other: 'unknown',
21
- };
22
- /** Engine cursor.js:27-35. ACP PromptResponse stopReason -> amalgm done stopReason. */
23
- export function cursorStopReason(stopReason) {
24
- const clean = String(stopReason || '').trim();
25
- if (!clean || clean === 'end_turn')
26
- return 'end_turn';
27
- if (clean === 'cancelled')
28
- return 'cancelled';
29
- if (clean === 'refusal')
30
- return 'refusal';
31
- if (clean === 'max_tokens' || clean === 'max_turn_requests')
32
- return clean;
33
- return 'end_turn';
34
- }
35
- /** Engine cursor.js:37-41. */
36
- function contentText(content) {
37
- if (!content || typeof content !== 'object')
38
- return '';
39
- if (typeof content['text'] === 'string')
40
- return content['text'];
41
- return '';
42
- }
43
- /** Engine cursor.js:43-63. ACP ToolCallContent[] -> a renderable output value. */
44
- function toolOutput(content) {
45
- if (!Array.isArray(content) || content.length === 0)
46
- return undefined;
47
- const texts = [];
48
- const diffs = [];
49
- for (const item of content) {
50
- if (!item || typeof item !== 'object')
51
- continue;
52
- if (item['type'] === 'diff') {
53
- diffs.push({ path: item['path'], oldText: item['oldText'] ?? null, newText: item['newText'] ?? '' });
54
- }
55
- else if (item['type'] === 'content') {
56
- const text = contentText(item['content']);
57
- if (text)
58
- texts.push(text);
59
- }
60
- else if (item['type'] === 'terminal' && item['terminalId']) {
61
- texts.push(`[terminal ${item['terminalId']}]`);
62
- }
63
- }
64
- if (diffs.length && texts.length)
65
- return { diffs, text: texts.join('\n') };
66
- if (diffs.length)
67
- return diffs.length === 1 ? diffs[0] : { diffs };
68
- if (texts.length)
69
- return texts.join('\n');
70
- return undefined;
71
- }
72
- /** Engine cursor.js:65-76. Shell `{stdout, stderr, exitCode}` -> `{text?, exitCode?}`. */
73
- function rawOutputValue(rawOutput) {
74
- if (rawOutput === undefined || rawOutput === null)
75
- return undefined;
76
- if (typeof rawOutput !== 'object')
77
- return rawOutput;
78
- const record = rawOutput;
79
- if (typeof record['stdout'] === 'string' || typeof record['stderr'] === 'string') {
80
- const text = [record['stdout'], record['stderr']].filter(Boolean).join('\n').trimEnd();
81
- return {
82
- ...(text ? { text } : {}),
83
- ...(record['exitCode'] !== undefined ? { exitCode: record['exitCode'] } : {}),
84
- };
85
- }
86
- return rawOutput;
87
- }
88
- /** Engine cursor.js:78-87. Merge an update into the known tool row (title/kind/input stick). */
89
- function toolData(state, toolCallId, update = {}) {
90
- const known = state.tools.get(toolCallId) || {};
91
- const next = {
92
- toolName: update['title'] || known.toolName || 'Tool',
93
- kind: ACP_TOOL_KINDS[update['kind']] || known.kind || undefined,
94
- input: update['rawInput'] !== undefined ? update['rawInput'] : known.input,
95
- };
96
- state.tools.set(toolCallId, next);
97
- return next;
98
- }
99
- /** Build the Cursor normalizer over injected event constructors. */
100
- export function createCursorNormalizer(events) {
101
- const { errorEvent, reasoningDelta, textDelta, toolCompleted, toolStarted, toolUpdated, usageFinal, } = events;
102
- /**
103
- * Engine cursor.js:89-149. One ACP session/update notification -> amalgm
104
- * events. `state` carries providerSessionId and per-turn tool
105
- * bookkeeping; it is created per prompt.
106
- */
107
- function normalizeCursorUpdate(updateInput, state) {
108
- const update = updateInput;
109
- const base = { providerSessionId: state.providerSessionId };
110
- const kind = update?.['sessionUpdate'];
111
- if (kind === 'agent_message_chunk') {
112
- const text = contentText(update['content']);
113
- return text ? [textDelta(text, base)] : [];
114
- }
115
- if (kind === 'agent_thought_chunk') {
116
- const text = contentText(update['content']);
117
- return text ? [reasoningDelta(text, base)] : [];
118
- }
119
- if (kind === 'tool_call') {
120
- const data = toolData(state, update['toolCallId'], update);
121
- return [toolStarted(update['toolCallId'], { ...base, ...data })];
122
- }
123
- if (kind === 'tool_call_update') {
124
- const data = toolData(state, update['toolCallId'], update);
125
- // Shell/execute results arrive as rawOutput ({exitCode, stdout, stderr});
126
- // file edits arrive as content diff items.
127
- const output = toolOutput(update['content']) ?? rawOutputValue(update['rawOutput']);
128
- if (update['status'] === 'completed' || update['status'] === 'failed') {
129
- state.tools.delete(update['toolCallId']);
130
- return [toolCompleted(update['toolCallId'], {
131
- ...base,
132
- ...data,
133
- ...(output !== undefined ? { output } : {}),
134
- isError: update['status'] === 'failed',
135
- ...(update['status'] === 'failed' ? { error: output || 'Tool call failed' } : {}),
136
- })];
137
- }
138
- return [toolUpdated(update['toolCallId'], {
139
- ...base,
140
- toolName: data.toolName,
141
- kind: data.kind,
142
- ...(update['rawInput'] !== undefined ? { inputDelta: update['rawInput'] } : {}),
143
- ...(output !== undefined ? { output } : {}),
144
- })];
145
- }
146
- if (kind === 'usage_update') {
147
- // Cursor does not emit these today; mapped so context tracking lights up
148
- // the moment the agent ships the ACP session-usage extension.
149
- return [usageFinal({
150
- usedTokens: update['used'],
151
- maxTokens: update['size'],
152
- costUsd: update['cost']?.amount ?? null,
153
- operation: 'context_snapshot',
154
- billable: false,
155
- exact: false,
156
- source: 'cursor_acp_usage_update',
157
- }, base)];
158
- }
159
- if (kind === 'error') {
160
- return [errorEvent(update['message'] || 'Cursor agent error', base)];
161
- }
162
- // Intentionally ignored: user_message_chunk (session/load replay),
163
- // session_info_update (amalgm owns titles), available_commands_update,
164
- // current_mode_update, config_options_update, plan.
165
- return [];
166
- }
167
- /**
168
- * Engine cursor.js:151-168. ACP PromptResponse.usage (session-usage RFD)
169
- * -> exact turn usage. Cursor does not populate this today; the mapping
170
- * is here for when it does.
171
- */
172
- function cursorPromptUsage(usageInput, base = {}) {
173
- const usage = usageInput;
174
- if (!usage || typeof usage !== 'object')
175
- return null;
176
- const total = Number(usage['total_tokens'] ?? usage['totalTokens']);
177
- if (!Number.isFinite(total) || total <= 0)
178
- return null;
179
- return usageFinal({
180
- inputTokens: usage['input_tokens'] ?? usage['inputTokens'],
181
- outputTokens: usage['output_tokens'] ?? usage['outputTokens'],
182
- cacheReadTokens: usage['cached_read_tokens'] ?? usage['cacheReadTokens'],
183
- cacheWriteTokens: usage['cached_write_tokens'] ?? usage['cacheWriteTokens'],
184
- thoughtTokens: usage['thought_tokens'] ?? usage['thoughtTokens'],
185
- operation: 'chat_turn',
186
- billable: true,
187
- exact: true,
188
- source: 'cursor_acp_prompt_usage',
189
- }, base);
190
- }
191
- return {
192
- normalizeCursorUpdate,
193
- cursorPromptUsage,
194
- };
195
- }
196
- //# sourceMappingURL=cursor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/normalizers/cursor.ts"],"names":[],"mappings":"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"}