@myagentroam/node 0.11.1 → 0.11.3
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/dist/connector.js +1 -1
- package/dist/database.js +53 -31
- package/dist/migrations/v021.js +64 -0
- package/dist/native-session-history.js +31 -1
- package/dist/runner/claude/conversation-parser.js +42 -0
- package/dist/runner/claude/managed-run-controller.js +78 -60
- package/dist/runner/claude/tool-normalizer.js +10 -0
- package/dist/runner/codex/conversation-parser.js +18 -3
- package/dist/runner/codex/managed-run-controller.js +25 -10
- package/dist/runner/mar-agent/conversation-normalizer.js +23 -9
- package/dist/runner/mar-agent/history-projection.js +3 -3
- package/dist/runner/mar-agent/managed-run-controller.js +1 -0
- package/dist/runner/opencode/conversation-parser.js +16 -3
- package/dist/runner/opencode/managed-run-controller.js +9 -4
- package/dist/runner/opencode/tool-normalizer.js +10 -0
- package/dist/runtime-state.js +47 -4
- package/dist/service/conversation-history-service.js +2 -1
- package/dist/service/conversation-segment-service.js +3 -1
- package/dist/service/conversation-work-process.js +102 -0
- package/dist/service/node-workspace-coordinator.js +21 -3
- package/dist/service/run-event-service.js +9 -2
- package/dist/service/skill-directory-service.js +2 -2
- package/dist/service/skill-install-service.js +15 -7
- package/dist/service/skill-node-operation-service.js +5 -4
- package/dist/service/workbench-event-projection.js +2 -0
- package/dist/service/workbench-manifest-service.js +1 -1
- package/dist/service/workspace-change-service.js +29 -13
- package/dist/service/workspace-file-service.js +3 -1
- package/dist/service/workspace-git-exclude-service.js +59 -32
- package/dist/service/workspace-service.js +34 -9
- package/dist/service/workspace-watch-snapshot-service.js +35 -5
- package/dist/service/workspace-workbench-service.js +23 -11
- package/dist/util/node-operation-parsers.js +0 -10
- package/dist/util/runner-native-session-parsers.js +15 -6
- package/dist/workspace.js +27 -3
- package/package.json +4 -4
package/dist/connector.js
CHANGED
|
@@ -1343,7 +1343,7 @@ export class NodeConnector {
|
|
|
1343
1343
|
this.workspaceCoordinator.clearWatches(workspaceId);
|
|
1344
1344
|
this.workspaceWorkbenchService.clearWorkspace(workspaceId);
|
|
1345
1345
|
this.promptCompletion?.invalidateWorkspace(workspaceId);
|
|
1346
|
-
});
|
|
1346
|
+
}, (workspaceId) => this.workspaceCoordinator.gitPolicyChanged(workspaceId));
|
|
1347
1347
|
this.workspaceWorkbenchService = new WorkspaceWorkbenchService({
|
|
1348
1348
|
requireWorkspace: (workspaceId) => this.workspaceCoordinator.require(workspaceId),
|
|
1349
1349
|
inspectWorkspace: (workspace) => this.workspaceCoordinator.inspectMetadata(workspace),
|
package/dist/database.js
CHANGED
|
@@ -2,6 +2,7 @@ import { NODE_SCHEMA_MIGRATION_V17 } from './migrations/v017.js';
|
|
|
2
2
|
import { NODE_SCHEMA_MIGRATION_V18 } from './migrations/v018.js';
|
|
3
3
|
import { NODE_SCHEMA_MIGRATION_V19 } from './migrations/v019.js';
|
|
4
4
|
import { NODE_SCHEMA_MIGRATION_V20 } from './migrations/v020.js';
|
|
5
|
+
import { NODE_SCHEMA_MIGRATION_V21 } from './migrations/v021.js';
|
|
5
6
|
import { normalizeSessionPrompt } from './util/personal-instructions.js';
|
|
6
7
|
import { randomUUID } from 'node:crypto';
|
|
7
8
|
import { closeSync, existsSync, mkdirSync, openSync, rmSync } from 'node:fs';
|
|
@@ -23,7 +24,8 @@ export const NODE_SCHEMA_MIGRATIONS = [
|
|
|
23
24
|
NODE_SCHEMA_MIGRATION_V17,
|
|
24
25
|
NODE_SCHEMA_MIGRATION_V18,
|
|
25
26
|
NODE_SCHEMA_MIGRATION_V19,
|
|
26
|
-
NODE_SCHEMA_MIGRATION_V20
|
|
27
|
+
NODE_SCHEMA_MIGRATION_V20,
|
|
28
|
+
NODE_SCHEMA_MIGRATION_V21
|
|
27
29
|
];
|
|
28
30
|
const LEGACY_NODE_SCHEMA_V12 = NODE_SCHEMA_VERSION_V13 - 1;
|
|
29
31
|
const OBSOLETE_NODE_SCHEMA_V16 = 16;
|
|
@@ -71,27 +73,22 @@ export class NodeDatabase {
|
|
|
71
73
|
this.raw.close();
|
|
72
74
|
}
|
|
73
75
|
getRunnerDefaults() {
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
const parsed = runnerDefaultConfigurationSchema.safeParse({
|
|
79
|
-
runner: stringValue(row, 'runner'),
|
|
80
|
-
model: stringValue(row, 'model'),
|
|
81
|
-
effort: stringValue(row, 'effort'),
|
|
82
|
-
access: stringValue(row, 'access')
|
|
83
|
-
});
|
|
76
|
+
return Object.entries(this.getNodeSettings().runnerDefaults ?? {})
|
|
77
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
78
|
+
.flatMap(([runner, settings]) => {
|
|
79
|
+
const parsed = runnerDefaultConfigurationSchema.safeParse({ runner, ...settings });
|
|
84
80
|
return parsed.success ? [parsed.data] : [];
|
|
85
81
|
});
|
|
86
82
|
}
|
|
83
|
+
getNodeSettings() {
|
|
84
|
+
const row = this.raw.prepare('SELECT settings FROM node_settings WHERE id = 1').get();
|
|
85
|
+
return JSON.parse(stringValue(row, 'settings'));
|
|
86
|
+
}
|
|
87
87
|
saveRunnerDefaults(input) {
|
|
88
|
+
const { runner, ...settings } = runnerDefaultConfigurationSchema.parse(input);
|
|
88
89
|
this.raw
|
|
89
|
-
.prepare(`
|
|
90
|
-
|
|
91
|
-
ON CONFLICT(runner) DO UPDATE SET
|
|
92
|
-
model = excluded.model, effort = excluded.effort,
|
|
93
|
-
access = excluded.access`)
|
|
94
|
-
.run(input.runner, input.model, input.effort, input.access);
|
|
90
|
+
.prepare(`UPDATE node_settings SET settings = json_patch(settings, ?) WHERE id = 1`)
|
|
91
|
+
.run(JSON.stringify({ runnerDefaults: { [runner]: settings } }));
|
|
95
92
|
return input;
|
|
96
93
|
}
|
|
97
94
|
listMcpInstallations(targetKind, targetId = '') {
|
|
@@ -188,45 +185,64 @@ export class NodeDatabase {
|
|
|
188
185
|
}
|
|
189
186
|
saveWorkspace(input) {
|
|
190
187
|
const current = this.raw
|
|
191
|
-
.prepare('SELECT id,
|
|
188
|
+
.prepare('SELECT id, settings FROM workspaces WHERE path = ?')
|
|
192
189
|
.get(input.path);
|
|
190
|
+
const settings = current === undefined
|
|
191
|
+
? {
|
|
192
|
+
git: {
|
|
193
|
+
preserveGitConfig: input.preserveGitConfig ?? false,
|
|
194
|
+
includeParentRepository: input.includeParentRepository ?? true
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
: JSON.parse(stringValue(current, 'settings'));
|
|
193
198
|
const workspace = {
|
|
194
199
|
id: current === undefined ? (input.id ?? randomUUID()) : stringValue(current, 'id'),
|
|
195
200
|
path: input.path,
|
|
196
201
|
kind: input.kind,
|
|
197
202
|
gitSummary: input.gitSummary,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
settings,
|
|
204
|
+
includeParentRepository: settings.git?.includeParentRepository ?? true,
|
|
205
|
+
preserveGitConfig: settings.git?.preserveGitConfig ?? false
|
|
201
206
|
};
|
|
202
207
|
const timestamp = this.now();
|
|
203
208
|
this.raw
|
|
204
|
-
.prepare(`INSERT INTO workspaces (id, path, kind, git_summary, created_at, updated_at,
|
|
209
|
+
.prepare(`INSERT INTO workspaces (id, path, kind, git_summary, created_at, updated_at, settings)
|
|
205
210
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
206
211
|
ON CONFLICT(path) DO UPDATE SET kind = excluded.kind,
|
|
207
212
|
git_summary = excluded.git_summary, updated_at = excluded.updated_at`)
|
|
208
|
-
.run(workspace.id, workspace.path, workspace.kind, workspace.gitSummary === null ? null : JSON.stringify(workspace.gitSummary), timestamp, timestamp,
|
|
213
|
+
.run(workspace.id, workspace.path, workspace.kind, workspace.gitSummary === null ? null : JSON.stringify(workspace.gitSummary), timestamp, timestamp, JSON.stringify(settings));
|
|
209
214
|
return workspace;
|
|
210
215
|
}
|
|
211
216
|
listWorkspaces() {
|
|
212
217
|
return this.raw
|
|
213
|
-
.prepare('SELECT id, path, kind, git_summary,
|
|
218
|
+
.prepare('SELECT id, path, kind, git_summary, settings FROM workspaces ORDER BY created_at ASC')
|
|
214
219
|
.all()
|
|
215
220
|
.map((row) => workspace(row));
|
|
216
221
|
}
|
|
217
222
|
getWorkspace(id) {
|
|
218
223
|
const row = this.raw
|
|
219
|
-
.prepare('SELECT id, path, kind, git_summary,
|
|
224
|
+
.prepare('SELECT id, path, kind, git_summary, settings FROM workspaces WHERE id = ?')
|
|
220
225
|
.get(id);
|
|
221
226
|
return row === undefined ? undefined : workspace(row);
|
|
222
227
|
}
|
|
223
|
-
|
|
224
|
-
|
|
228
|
+
getWorkspaceByPath(path) {
|
|
229
|
+
const row = this.raw
|
|
230
|
+
.prepare('SELECT id, path, kind, git_summary, settings FROM workspaces WHERE path = ?')
|
|
231
|
+
.get(path);
|
|
232
|
+
return row === undefined ? undefined : workspace(row);
|
|
233
|
+
}
|
|
234
|
+
/** Updates only supplied policy fields without replacing Workspace metadata. */
|
|
235
|
+
setWorkspaceGitPolicy(id, preserveGitConfig, includeParentRepository) {
|
|
225
236
|
if (this.getWorkspace(id) === undefined)
|
|
226
237
|
throw new Error('WORKSPACE_NOT_FOUND');
|
|
227
238
|
this.raw
|
|
228
|
-
.prepare('UPDATE workspaces SET
|
|
229
|
-
.run(
|
|
239
|
+
.prepare('UPDATE workspaces SET settings=json_patch(settings, ?), updated_at=? WHERE id=?')
|
|
240
|
+
.run(JSON.stringify({
|
|
241
|
+
git: {
|
|
242
|
+
preserveGitConfig,
|
|
243
|
+
...(includeParentRepository === undefined ? {} : { includeParentRepository })
|
|
244
|
+
}
|
|
245
|
+
}), this.now(), id);
|
|
230
246
|
return this.getWorkspace(id);
|
|
231
247
|
}
|
|
232
248
|
/** Unregisters a Workspace registration without deleting its local directory. */
|
|
@@ -474,7 +490,10 @@ export function migrate(database, catalog = NODE_SCHEMA_MIGRATIONS) {
|
|
|
474
490
|
throw new Error('NODE_DATABASE_VERSION_UNSUPPORTED');
|
|
475
491
|
if (migratedVersions.some((version, index) => version !== NODE_SCHEMA_VERSION_V13 + index))
|
|
476
492
|
throw new Error('NODE_DATABASE_MIGRATION_HISTORY_INVALID');
|
|
477
|
-
|
|
493
|
+
if (current < NODE_SCHEMA_MIGRATION_V21.version)
|
|
494
|
+
assertNodeSchemaV13(database);
|
|
495
|
+
else
|
|
496
|
+
catalog[current - NODE_SCHEMA_VERSION_V13 - 1].assert(database);
|
|
478
497
|
for (const entry of catalog) {
|
|
479
498
|
if (entry.version <= current)
|
|
480
499
|
continue;
|
|
@@ -623,9 +642,12 @@ function acquireNodeMigrationLock(databasePath) {
|
|
|
623
642
|
}
|
|
624
643
|
function workspace(row) {
|
|
625
644
|
const summary = nullableString(row, 'git_summary');
|
|
645
|
+
const settings = JSON.parse(stringValue(row, 'settings'));
|
|
626
646
|
return {
|
|
627
647
|
id: stringValue(row, 'id'),
|
|
628
|
-
|
|
648
|
+
settings,
|
|
649
|
+
preserveGitConfig: settings.git?.preserveGitConfig ?? false,
|
|
650
|
+
includeParentRepository: settings.git?.includeParentRepository ?? true,
|
|
629
651
|
path: stringValue(row, 'path'),
|
|
630
652
|
kind: stringValue(row, 'kind'),
|
|
631
653
|
gitSummary: summary === null ? null : JSON.parse(summary)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { NODE_SCHEMA_V13 } from './v013.js';
|
|
3
|
+
import { NODE_SCHEMA_MIGRATION_V14 } from './v014.js';
|
|
4
|
+
import { NODE_SCHEMA_MIGRATION_V15 } from './v015.js';
|
|
5
|
+
import { NODE_SCHEMA_MIGRATION_V16 } from './v016.js';
|
|
6
|
+
import { NODE_SCHEMA_MIGRATION_V17 } from './v017.js';
|
|
7
|
+
import { NODE_SCHEMA_MIGRATION_V18 } from './v018.js';
|
|
8
|
+
import { NODE_SCHEMA_MIGRATION_V19 } from './v019.js';
|
|
9
|
+
import { NODE_SCHEMA_MIGRATION_V20 } from './v020.js';
|
|
10
|
+
const sql = `ALTER TABLE workspaces ADD COLUMN settings TEXT NOT NULL DEFAULT '{}'
|
|
11
|
+
CHECK (json_valid(settings) AND json_type(settings) = 'object');
|
|
12
|
+
UPDATE workspaces SET settings = json_object('git', json_object(
|
|
13
|
+
'preserveGitConfig', json(CASE WHEN preserve_git_config = 1 THEN 'true' ELSE 'false' END),
|
|
14
|
+
'includeParentRepository', json('true')));
|
|
15
|
+
ALTER TABLE workspaces DROP COLUMN preserve_git_config;
|
|
16
|
+
CREATE TABLE node_settings (
|
|
17
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
18
|
+
settings TEXT NOT NULL CHECK (json_valid(settings) AND json_type(settings) = 'object')
|
|
19
|
+
) STRICT;
|
|
20
|
+
INSERT INTO node_settings (id, settings)
|
|
21
|
+
SELECT 1, json_object('runnerDefaults', json_group_object(runner,
|
|
22
|
+
json_object('model', model, 'effort', effort, 'access', access))) FROM runner_defaults;
|
|
23
|
+
DROP TABLE runner_defaults;`;
|
|
24
|
+
export const NODE_SCHEMA_MIGRATION_V21 = {
|
|
25
|
+
version: 21,
|
|
26
|
+
sql,
|
|
27
|
+
preflight: (database) => NODE_SCHEMA_MIGRATION_V20.assert(database),
|
|
28
|
+
assert(database) {
|
|
29
|
+
const reference = new DatabaseSync(':memory:');
|
|
30
|
+
try {
|
|
31
|
+
reference.exec(NODE_SCHEMA_V13);
|
|
32
|
+
for (const migration of [
|
|
33
|
+
NODE_SCHEMA_MIGRATION_V14,
|
|
34
|
+
NODE_SCHEMA_MIGRATION_V15,
|
|
35
|
+
NODE_SCHEMA_MIGRATION_V16,
|
|
36
|
+
NODE_SCHEMA_MIGRATION_V17,
|
|
37
|
+
NODE_SCHEMA_MIGRATION_V18,
|
|
38
|
+
NODE_SCHEMA_MIGRATION_V19,
|
|
39
|
+
NODE_SCHEMA_MIGRATION_V20
|
|
40
|
+
])
|
|
41
|
+
reference.exec(migration.sql);
|
|
42
|
+
reference.exec(sql);
|
|
43
|
+
const schema = (db) => db
|
|
44
|
+
.prepare("SELECT type,name,sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%' ORDER BY type,name")
|
|
45
|
+
.all()
|
|
46
|
+
.map((row) => [
|
|
47
|
+
row.type,
|
|
48
|
+
row.name,
|
|
49
|
+
row.name === 'node_schema_migrations'
|
|
50
|
+
? String(row.sql).replace(/\s+/g, '')
|
|
51
|
+
: String(row.sql).replace(/\s+/g, ' ').trim()
|
|
52
|
+
]);
|
|
53
|
+
const expected = schema(reference);
|
|
54
|
+
const actual = schema(database);
|
|
55
|
+
const mismatch = expected.find((row) => !actual.some((value) => JSON.stringify(value) === JSON.stringify(row))) ??
|
|
56
|
+
actual.find((row) => !expected.some((value) => JSON.stringify(value) === JSON.stringify(row)));
|
|
57
|
+
if (mismatch !== undefined)
|
|
58
|
+
throw new Error(`NODE_DATABASE_SCHEMA_INVALID:${String(mismatch[1])}`);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
reference.close();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -7,6 +7,8 @@ import { nodeLog } from './operational.js';
|
|
|
7
7
|
import { stripClaudePlanTag } from './runner-command-engine.js';
|
|
8
8
|
import { stripGoalStartPrompt } from './service/goal-runtime.js';
|
|
9
9
|
import { parseCodexComposerImageMarkers } from './runner/codex/composer-image-markers.js';
|
|
10
|
+
import { codexTranscriptSourceItemId } from './runner/codex/conversation-parser.js';
|
|
11
|
+
import { claudeAssistantBlocks, extractClaudeThinkingText } from './runner/claude/conversation-parser.js';
|
|
10
12
|
import { extractClaudeToolResults } from './runner/claude/tool-normalizer.js';
|
|
11
13
|
const DISCOVERY_READ_BYTES = 32 * 1024;
|
|
12
14
|
const TRANSCRIPT_HEADER_READ_BYTES = 32 * 1024;
|
|
@@ -477,9 +479,11 @@ async function readTranscript(path, runner, byteLimit, page, targetLines = Numbe
|
|
|
477
479
|
const generatedImage = codexGeneratedImageEvent(value);
|
|
478
480
|
if (generatedImage !== undefined)
|
|
479
481
|
items.push(generatedImage);
|
|
482
|
+
const sourceItemId = runner === 'codex' ? codexTranscriptSourceItemId(value, codexTurnId) : undefined;
|
|
480
483
|
for (const parsedItem of transcriptItems(runner, value)) {
|
|
481
484
|
const item = {
|
|
482
485
|
...parsedItem,
|
|
486
|
+
...(sourceItemId === undefined ? {} : { sourceItemId }),
|
|
483
487
|
...(recordId === undefined ? {} : { nativeId: recordId }),
|
|
484
488
|
...(runner === 'codex' &&
|
|
485
489
|
parsedItem.kind === 'message' &&
|
|
@@ -615,6 +619,32 @@ function positiveInteger(value) {
|
|
|
615
619
|
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
616
620
|
}
|
|
617
621
|
function transcriptItems(runner, record) {
|
|
622
|
+
if (runner === 'claude-code' && record.type === 'assistant') {
|
|
623
|
+
return claudeAssistantBlocks(record).flatMap(({ record: blockRecord, blockIndex, sourceItemId }) => {
|
|
624
|
+
const block = contentBlocks(blockRecord)[0];
|
|
625
|
+
if (block?.type === 'thinking') {
|
|
626
|
+
const text = extractClaudeThinkingText(blockRecord)[0] ?? '';
|
|
627
|
+
return text.length === 0
|
|
628
|
+
? []
|
|
629
|
+
: [
|
|
630
|
+
{
|
|
631
|
+
kind: 'reasoning_summary',
|
|
632
|
+
representation: 'visible_thinking',
|
|
633
|
+
sections: [{ index: blockIndex, text }],
|
|
634
|
+
createdAt: timestampOf(record),
|
|
635
|
+
...(sourceItemId === undefined ? {} : { sourceItemId })
|
|
636
|
+
}
|
|
637
|
+
];
|
|
638
|
+
}
|
|
639
|
+
return transcriptRecordItems(runner, blockRecord).map((item) => ({
|
|
640
|
+
...item,
|
|
641
|
+
...(sourceItemId === undefined ? {} : { sourceItemId })
|
|
642
|
+
}));
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
return transcriptRecordItems(runner, record);
|
|
646
|
+
}
|
|
647
|
+
function transcriptRecordItems(runner, record) {
|
|
618
648
|
const createdAt = timestampOf(record);
|
|
619
649
|
const results = [...toolResultsOf(record, createdAt), ...codexToolResultsOf(record, createdAt)];
|
|
620
650
|
if (results.length > 0)
|
|
@@ -689,7 +719,7 @@ function codexReasoningSummariesOf(record, createdAt) {
|
|
|
689
719
|
return text.length === 0 ? [] : [{ index, text }];
|
|
690
720
|
})
|
|
691
721
|
: [];
|
|
692
|
-
return [{ kind: 'reasoning_summary', sections, createdAt }];
|
|
722
|
+
return [{ kind: 'reasoning_summary', representation: 'summary', sections, createdAt }];
|
|
693
723
|
}
|
|
694
724
|
function unsupportedTranscriptItems(record, createdAt) {
|
|
695
725
|
return contentBlocks(record).flatMap((block) => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { boundedConversationItemId, isPlainRecord, claudeContentBlocks, compactRunnerText } from '../../util/node-operation-parsers.js';
|
|
2
|
+
export function extractClaudeThinkingText(record) {
|
|
3
|
+
return claudeContentBlocks(record).flatMap((block) => {
|
|
4
|
+
// Completed public blocks only; signatures and redacted/encrypted data are never read.
|
|
5
|
+
if (block.type !== 'thinking' || typeof block.thinking !== 'string')
|
|
6
|
+
return [];
|
|
7
|
+
const text = compactRunnerText(block.thinking.trim(), 10_000);
|
|
8
|
+
return text.length === 0 ? [] : [text];
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
/** Preserve native block order and identity across SDK callbacks and transcript reads. */
|
|
12
|
+
export function claudeAssistantBlocks(record) {
|
|
13
|
+
const message = isPlainRecord(record.message) ? record.message : undefined;
|
|
14
|
+
if (message === undefined)
|
|
15
|
+
return [];
|
|
16
|
+
const content = typeof message.content === 'string'
|
|
17
|
+
? [{ type: 'text', text: message.content }]
|
|
18
|
+
: Array.isArray(message.content)
|
|
19
|
+
? message.content
|
|
20
|
+
: [];
|
|
21
|
+
const messageId = typeof record.uuid === 'string'
|
|
22
|
+
? record.uuid
|
|
23
|
+
: typeof message.id === 'string'
|
|
24
|
+
? message.id
|
|
25
|
+
: undefined;
|
|
26
|
+
return content.flatMap((block, blockIndex) => {
|
|
27
|
+
if (!isPlainRecord(block))
|
|
28
|
+
return [];
|
|
29
|
+
const sourceItemId = block.type === 'tool_use' && typeof block.id === 'string'
|
|
30
|
+
? boundedConversationItemId('claude-tool', block.id)
|
|
31
|
+
: messageId === undefined
|
|
32
|
+
? undefined
|
|
33
|
+
: JSON.stringify(['claude', messageId, blockIndex]);
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
record: { ...record, message: { ...message, content: [block] } },
|
|
37
|
+
blockIndex,
|
|
38
|
+
...(sourceItemId === undefined ? {} : { sourceItemId })
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { claudeAssistantBlocks, extractClaudeThinkingText } from './conversation-parser.js';
|
|
1
2
|
import { ClaudeUsageNormalizer } from './tool-normalizer.js';
|
|
2
3
|
import { createEnvelope } from '@myagentroam/protocol';
|
|
3
4
|
import { ClaudeChannelGate } from '../../claude-channel.js';
|
|
4
5
|
import { nodeExecutionEnvironment } from '../../node-execution-environment.js';
|
|
5
|
-
import { boundedConversationItemId, claudePlanText, compactRunnerText, extractClaudeText,
|
|
6
|
+
import { boundedConversationItemId, claudePlanText, compactRunnerText, extractClaudeText, extractClaudeToolUses } from '../codex/conversation-parser.js';
|
|
6
7
|
import { normalizeClaudeTool, extractClaudeToolResults } from './tool-normalizer.js';
|
|
7
8
|
import { claudeContentBlocks } from '../../util/node-operation-parsers.js';
|
|
8
9
|
import { parseSecretEnvironment } from '../../util/secret-environment.js';
|
|
@@ -344,67 +345,84 @@ export class ClaudeManagedRunController {
|
|
|
344
345
|
finalOutputTokens.set(snapshotId, snapshotUsage.output_tokens);
|
|
345
346
|
settleGeneration(snapshotId, receiptAt);
|
|
346
347
|
}
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
this.host.
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
348
|
+
const fallbackMessageId = crypto.randomUUID();
|
|
349
|
+
for (const { record: blockRecord, blockIndex, sourceItemId } of claudeAssistantBlocks(record)) {
|
|
350
|
+
const text = extractClaudeText(blockRecord);
|
|
351
|
+
if (mainTask &&
|
|
352
|
+
(text.length > 0 || extractClaudeThinkingText(blockRecord).length > 0))
|
|
353
|
+
observeResponse(typeof record.message?.id === 'string'
|
|
354
|
+
? record.message.id
|
|
355
|
+
: undefined);
|
|
356
|
+
const planText = claudePlanText(text);
|
|
357
|
+
if (planText !== undefined)
|
|
358
|
+
this.host.emitItem(runId, {
|
|
359
|
+
itemId: boundedConversationItemId('claude-plan', sourceItemId ?? `${fallbackMessageId}:${blockIndex}`),
|
|
360
|
+
...(sourceItemId === undefined ? {} : { sourceItemId }),
|
|
361
|
+
kind: 'plan',
|
|
362
|
+
status: 'COMPLETED',
|
|
363
|
+
payload: { explanation: compactRunnerText(planText, 20_000), steps: [] }
|
|
364
|
+
});
|
|
365
|
+
else if (text.length > 0)
|
|
366
|
+
this.host.emitItem(runId, {
|
|
367
|
+
itemId: boundedConversationItemId('claude-text', sourceItemId ?? `${fallbackMessageId}:${blockIndex}`),
|
|
368
|
+
...(sourceItemId === undefined ? {} : { sourceItemId }),
|
|
369
|
+
kind: 'assistant_message',
|
|
370
|
+
status: 'COMPLETED',
|
|
371
|
+
payload: { text, format: 'markdown', model: null }
|
|
372
|
+
});
|
|
373
|
+
for (const text of extractClaudeThinkingText(blockRecord)) {
|
|
374
|
+
this.host.emitItem(runId, {
|
|
375
|
+
itemId: boundedConversationItemId('claude-reasoning', sourceItemId ?? `${fallbackMessageId}:${blockIndex}`),
|
|
376
|
+
...(sourceItemId === undefined ? {} : { sourceItemId }),
|
|
377
|
+
kind: 'reasoning',
|
|
378
|
+
status: 'COMPLETED',
|
|
379
|
+
payload: {
|
|
380
|
+
representation: 'visible_thinking',
|
|
381
|
+
sections: [{ index: blockIndex, text }]
|
|
382
|
+
}
|
|
383
|
+
});
|
|
377
384
|
}
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
kind,
|
|
386
|
-
status: 'IN_PROGRESS',
|
|
387
|
-
payload: normalized.payload
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
for (const [index, block] of claudeContentBlocks(record).entries()) {
|
|
391
|
-
const type = typeof block.type === 'string' ? block.type : 'unknown';
|
|
392
|
-
if (type === 'text' ||
|
|
393
|
-
type === 'thinking' ||
|
|
394
|
-
type === 'redacted_thinking' ||
|
|
395
|
-
type === 'encrypted_thinking' ||
|
|
396
|
-
type === 'tool_use' ||
|
|
397
|
-
type === 'tool_result')
|
|
398
|
-
continue;
|
|
399
|
-
this.host.emitItem(runId, {
|
|
400
|
-
itemId: boundedConversationItemId('claude-item', `${typeof record.uuid === 'string' ? record.uuid : runId}:${index}`),
|
|
401
|
-
kind: 'unknown',
|
|
402
|
-
status: 'COMPLETED',
|
|
403
|
-
payload: {
|
|
404
|
-
sourceEventType: `claude-code:${type}`,
|
|
405
|
-
label: `Claude Code ${compactRunnerText(type, 128)}`
|
|
385
|
+
for (const tool of extractClaudeToolUses(blockRecord)) {
|
|
386
|
+
const latencyMs = mainTask ? responseLatency.toolStarted(tool.id) : undefined;
|
|
387
|
+
if (latencyMs !== undefined)
|
|
388
|
+
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
389
|
+
if (tool.name === 'AskUserQuestion') {
|
|
390
|
+
toolKinds.set(tool.id, 'user_input_request');
|
|
391
|
+
continue;
|
|
406
392
|
}
|
|
407
|
-
|
|
393
|
+
const itemId = boundedConversationItemId('claude-tool', tool.id);
|
|
394
|
+
toolInputs.set(tool.id, tool);
|
|
395
|
+
const normalized = normalizeClaudeTool(tool.name, tool.input, null, false, tool.inputTruncated, knownSubagents);
|
|
396
|
+
const kind = normalized.kind;
|
|
397
|
+
toolKinds.set(tool.id, kind);
|
|
398
|
+
this.host.emitItem(runId, {
|
|
399
|
+
itemId,
|
|
400
|
+
sourceItemId: itemId,
|
|
401
|
+
kind,
|
|
402
|
+
status: 'IN_PROGRESS',
|
|
403
|
+
payload: normalized.payload
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
for (const block of claudeContentBlocks(blockRecord)) {
|
|
407
|
+
const type = typeof block.type === 'string' ? block.type : 'unknown';
|
|
408
|
+
if (type === 'text' ||
|
|
409
|
+
type === 'thinking' ||
|
|
410
|
+
type === 'redacted_thinking' ||
|
|
411
|
+
type === 'encrypted_thinking' ||
|
|
412
|
+
type === 'tool_use' ||
|
|
413
|
+
type === 'tool_result')
|
|
414
|
+
continue;
|
|
415
|
+
this.host.emitItem(runId, {
|
|
416
|
+
itemId: boundedConversationItemId('claude-item', sourceItemId ?? `${fallbackMessageId}:${blockIndex}`),
|
|
417
|
+
...(sourceItemId === undefined ? {} : { sourceItemId }),
|
|
418
|
+
kind: 'unknown',
|
|
419
|
+
status: 'COMPLETED',
|
|
420
|
+
payload: {
|
|
421
|
+
sourceEventType: `claude-code:${type}`,
|
|
422
|
+
label: `Claude Code ${compactRunnerText(type, 128)}`
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
408
426
|
}
|
|
409
427
|
}
|
|
410
428
|
if (record.type === 'tool_progress' && typeof record.tool_use_id === 'string') {
|
|
@@ -135,6 +135,16 @@ export function normalizeClaudeTool(toolName, input, output, failed, truncated,
|
|
|
135
135
|
progressLabel: null,
|
|
136
136
|
errorCode: failed ? 'TOOL_EXECUTION_FAILED' : null,
|
|
137
137
|
truncated: outputTruncated,
|
|
138
|
+
...(toolName === 'Read'
|
|
139
|
+
? {
|
|
140
|
+
toolPresentation: {
|
|
141
|
+
kind: 'READ',
|
|
142
|
+
paths: typeof values?.file_path === 'string' && values.file_path.length > 0
|
|
143
|
+
? [compactRunnerText(values.file_path, 4096)]
|
|
144
|
+
: []
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
: {}),
|
|
138
148
|
...subagent
|
|
139
149
|
}
|
|
140
150
|
};
|
|
@@ -629,6 +629,20 @@ export function codexPlanText(value) {
|
|
|
629
629
|
* A final Claude tagged Plan Mode plan is fully wrapped in `<proposed_plan>` tags;
|
|
630
630
|
* text with missing or unclosed tags remains a regular assistant message.
|
|
631
631
|
*/
|
|
632
|
+
export function codexSourceItemId(nativeTurnId, itemId) {
|
|
633
|
+
return JSON.stringify([nativeTurnId, itemId]);
|
|
634
|
+
}
|
|
635
|
+
export function codexTranscriptSourceItemId(record, nativeTurnId) {
|
|
636
|
+
if (nativeTurnId === undefined ||
|
|
637
|
+
record.type !== 'response_item' ||
|
|
638
|
+
!isPlainRecord(record.payload))
|
|
639
|
+
return undefined;
|
|
640
|
+
const payload = record.payload;
|
|
641
|
+
const id = typeof payload.call_id === 'string' ? payload.call_id : payload.id;
|
|
642
|
+
return typeof id === 'string'
|
|
643
|
+
? codexSourceItemId(nativeTurnId, boundedConversationItemId('codex', id))
|
|
644
|
+
: undefined;
|
|
645
|
+
}
|
|
632
646
|
export function codexOfficialItem(session, nativeTurnId, value, turnSequence, itemSequence, startedAt, completedAt) {
|
|
633
647
|
if (!isPlainRecord(value) || typeof value.type !== 'string' || value.type === 'subAgentActivity')
|
|
634
648
|
return undefined;
|
|
@@ -645,6 +659,7 @@ export function codexOfficialItem(session, nativeTurnId, value, turnSequence, it
|
|
|
645
659
|
: `${nativeTurnId}:${itemSequence}`;
|
|
646
660
|
const base = {
|
|
647
661
|
id: `${session.id}:official:${nativeTurnId}:${id}`,
|
|
662
|
+
sourceItemId: codexSourceItemId(nativeTurnId, boundedConversationItemId('codex', id)),
|
|
648
663
|
sessionId: session.id,
|
|
649
664
|
turnId: `${session.id}:official:${nativeTurnId}`,
|
|
650
665
|
runId: null,
|
|
@@ -689,7 +704,7 @@ export function codexOfficialItem(session, nativeTurnId, value, turnSequence, it
|
|
|
689
704
|
? [{ index, text: compactRunnerText(text, 10_000) }]
|
|
690
705
|
: [])
|
|
691
706
|
: [];
|
|
692
|
-
return { ...base, kind: '
|
|
707
|
+
return { ...base, kind: 'reasoning', payload: { representation: 'summary', sections } };
|
|
693
708
|
}
|
|
694
709
|
if (value.type === 'commandExecution') {
|
|
695
710
|
const output = typeof value.aggregatedOutput === 'string' ? value.aggregatedOutput : '';
|
|
@@ -835,9 +850,9 @@ export function codexLiveConversationItem(value, completed, imageAttachments = [
|
|
|
835
850
|
: [];
|
|
836
851
|
return {
|
|
837
852
|
itemId,
|
|
838
|
-
kind: '
|
|
853
|
+
kind: 'reasoning',
|
|
839
854
|
status: completed ? 'COMPLETED' : 'IN_PROGRESS',
|
|
840
|
-
payload: { sections }
|
|
855
|
+
payload: { representation: 'summary', sections }
|
|
841
856
|
};
|
|
842
857
|
}
|
|
843
858
|
const planText = codexPlanText(value);
|