@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.
Files changed (36) hide show
  1. package/dist/connector.js +1 -1
  2. package/dist/database.js +53 -31
  3. package/dist/migrations/v021.js +64 -0
  4. package/dist/native-session-history.js +31 -1
  5. package/dist/runner/claude/conversation-parser.js +42 -0
  6. package/dist/runner/claude/managed-run-controller.js +78 -60
  7. package/dist/runner/claude/tool-normalizer.js +10 -0
  8. package/dist/runner/codex/conversation-parser.js +18 -3
  9. package/dist/runner/codex/managed-run-controller.js +25 -10
  10. package/dist/runner/mar-agent/conversation-normalizer.js +23 -9
  11. package/dist/runner/mar-agent/history-projection.js +3 -3
  12. package/dist/runner/mar-agent/managed-run-controller.js +1 -0
  13. package/dist/runner/opencode/conversation-parser.js +16 -3
  14. package/dist/runner/opencode/managed-run-controller.js +9 -4
  15. package/dist/runner/opencode/tool-normalizer.js +10 -0
  16. package/dist/runtime-state.js +47 -4
  17. package/dist/service/conversation-history-service.js +2 -1
  18. package/dist/service/conversation-segment-service.js +3 -1
  19. package/dist/service/conversation-work-process.js +102 -0
  20. package/dist/service/node-workspace-coordinator.js +21 -3
  21. package/dist/service/run-event-service.js +9 -2
  22. package/dist/service/skill-directory-service.js +2 -2
  23. package/dist/service/skill-install-service.js +15 -7
  24. package/dist/service/skill-node-operation-service.js +5 -4
  25. package/dist/service/workbench-event-projection.js +2 -0
  26. package/dist/service/workbench-manifest-service.js +1 -1
  27. package/dist/service/workspace-change-service.js +29 -13
  28. package/dist/service/workspace-file-service.js +3 -1
  29. package/dist/service/workspace-git-exclude-service.js +59 -32
  30. package/dist/service/workspace-service.js +34 -9
  31. package/dist/service/workspace-watch-snapshot-service.js +35 -5
  32. package/dist/service/workspace-workbench-service.js +23 -11
  33. package/dist/util/node-operation-parsers.js +0 -10
  34. package/dist/util/runner-native-session-parsers.js +15 -6
  35. package/dist/workspace.js +27 -3
  36. package/package.json +4 -4
@@ -1,5 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { listGitHistoryRefs, openGitHistoryFileContent, readGitHistory, readGitHistoryFileDiff, readGitHistoryFileContentRange, readGitHistoryFiles, readCurrentChangeDiff, readRestrictedDiff, resolveGitRepository, resolveGitRepositoryTarget, restoreCurrentChange } from '../workspace.js';
2
+ import { isAbsolute, win32 } from 'node:path';
3
+ import { listGitHistoryRefs, normalizeFilePreviewPath, openGitHistoryFileContent, readGitHistory, readGitHistoryFileDiff, readGitHistoryFileContentRange, readGitHistoryFiles, readCurrentChangeDiff, readRestrictedDiff, resolveGitRepository, resolveGitRepositoryTarget, resolveWorkspaceGitRoot, restoreCurrentChange } from '../workspace.js';
3
4
  import { WorkspaceCodeNavigationService } from './workspace-code-navigation-service.js';
4
5
  import { WorkspaceContentSearchService } from './workspace-content-search-service.js';
5
6
  import { WorkspaceWatchController } from './workspace-watch-controller.js';
@@ -98,7 +99,7 @@ export class WorkspaceWorkbenchService {
98
99
  }
99
100
  async readPredictionFile(workspaceId, path, worktreePath) {
100
101
  const workspace = this.options.requireWorkspace(workspaceId);
101
- const repository = await this.fileRepository(workspace, worktreePath ? { worktreePath } : {});
102
+ const repository = await this.fileRepository(workspace, worktreePath ? { worktreePath, path } : {});
102
103
  const { readWorkspaceTextFile } = await import('../workspace.js');
103
104
  return readWorkspaceTextFile(repository, path, 0, 64 * 1024);
104
105
  }
@@ -180,7 +181,7 @@ export class WorkspaceWorkbenchService {
180
181
  (input.worktreePath !== undefined && typeof input.worktreePath !== 'string'))
181
182
  throw new Error('GIT_DIFF_INVALID');
182
183
  const workspace = this.gitWorkspace(input.workspaceId);
183
- const repository = await resolveGitRepositoryTarget(workspace.path, input.repositoryPath ?? '', input.worktreePath);
184
+ const repository = await resolveGitRepositoryTarget(await resolveWorkspaceGitRoot(workspace.path, workspace.includeParentRepository ?? true), input.repositoryPath ?? '', input.worktreePath);
184
185
  return { diff: await readCurrentChangeDiff(repository, input.path) };
185
186
  }
186
187
  async restoreChange(input) {
@@ -191,7 +192,7 @@ export class WorkspaceWorkbenchService {
191
192
  (input.state !== 'STAGED' && input.state !== 'UNSTAGED' && input.state !== 'UNTRACKED'))
192
193
  throw new Error('GIT_CHANGE_RESTORE_UNAVAILABLE');
193
194
  const workspace = this.gitWorkspace(input.workspaceId);
194
- const repository = await resolveGitRepositoryTarget(workspace.path, input.repositoryPath ?? '', input.worktreePath);
195
+ const repository = await resolveGitRepositoryTarget(await resolveWorkspaceGitRoot(workspace.path, workspace.includeParentRepository ?? true), input.repositoryPath ?? '', input.worktreePath);
195
196
  await restoreCurrentChange(repository, input.path, input.state);
196
197
  this.options.invalidate(workspace.id);
197
198
  return { restored: true };
@@ -201,14 +202,22 @@ export class WorkspaceWorkbenchService {
201
202
  (input.repositoryPath !== undefined && typeof input.repositoryPath !== 'string'))
202
203
  throw new Error('GIT_REPOSITORY_INVALID');
203
204
  const workspace = this.gitWorkspace(input.workspaceId);
204
- return resolveGitRepository(workspace.path, input.repositoryPath ?? '');
205
+ return resolveGitRepository(await resolveWorkspaceGitRoot(workspace.path, workspace.includeParentRepository ?? true), input.repositoryPath ?? '');
205
206
  }
206
207
  async fileRepository(workspace, input) {
207
- if (input.worktreePath === undefined)
208
+ if (input.worktreePath === undefined && input.repositoryPath === undefined)
208
209
  return workspace.path;
209
- if (typeof input.worktreePath !== 'string')
210
+ if ((input.worktreePath !== undefined && typeof input.worktreePath !== 'string') ||
211
+ (input.repositoryPath !== undefined && typeof input.repositoryPath !== 'string'))
210
212
  throw new Error('GIT_REPOSITORY_INVALID');
211
- return resolveGitRepositoryTarget(workspace.path, '', input.worktreePath);
213
+ if (typeof input.path !== 'string' ||
214
+ input.path.startsWith('mar-attachment:') ||
215
+ input.previewDirectory !== undefined)
216
+ throw new Error('FILE_PATH_ESCAPE');
217
+ const path = normalizeFilePreviewPath(input.path);
218
+ if (isAbsolute(path) || win32.isAbsolute(path))
219
+ throw new Error('FILE_PATH_ESCAPE');
220
+ return resolveGitRepositoryTarget(await resolveWorkspaceGitRoot(workspace.path, workspace.includeParentRepository ?? true), input.repositoryPath ?? '', input.worktreePath);
212
221
  }
213
222
  refs(input) {
214
223
  if (input.refs === undefined)
@@ -280,6 +289,9 @@ export class WorkspaceWorkbenchService {
280
289
  const input = {
281
290
  workspaceId: workspace.id,
282
291
  path: descriptor.resource.path,
292
+ ...(descriptor.resource.repositoryPath === undefined
293
+ ? {}
294
+ : { repositoryPath: descriptor.resource.repositoryPath }),
283
295
  ...(descriptor.resource.worktreePath === undefined
284
296
  ? {}
285
297
  : { worktreePath: descriptor.resource.worktreePath })
@@ -372,12 +384,12 @@ export class WorkspaceWorkbenchService {
372
384
  typeof input.toRef !== 'string')
373
385
  throw new Error('GIT_DIFF_INVALID');
374
386
  const workspace = this.gitWorkspace(input.workspaceId);
375
- return { diff: await readRestrictedDiff(workspace.path, input.fromRef, input.toRef) };
387
+ return {
388
+ diff: await readRestrictedDiff(await resolveWorkspaceGitRoot(workspace.path, workspace.includeParentRepository ?? true), input.fromRef, input.toRef)
389
+ };
376
390
  }
377
391
  gitWorkspace(workspaceId) {
378
392
  const workspace = this.options.requireWorkspace(workspaceId);
379
- if (workspace.kind !== 'GIT_WORKSPACE')
380
- throw new Error('WORKSPACE_NOT_GIT');
381
393
  return workspace;
382
394
  }
383
395
  }
@@ -133,16 +133,6 @@ export function extractClaudeText(message) {
133
133
  .flatMap((block) => block.type === 'text' && typeof block.text === 'string' ? [block.text] : [])
134
134
  .join('');
135
135
  }
136
- export function extractClaudeThinkingSummaries(message) {
137
- return claudeContentBlocks(message).flatMap((block) => {
138
- // `showThinkingSummaries` asks the SDK/API for exactly these public
139
- // summaries. Partial raw thinking events are intentionally not read.
140
- if (block.type !== 'thinking' || typeof block.thinking !== 'string')
141
- return [];
142
- const text = compactRunnerText(block.thinking.trim(), 10_000);
143
- return text.length === 0 ? [] : [text];
144
- });
145
- }
146
136
  export function extractClaudeToolUses(message) {
147
137
  return claudeContentBlocks(message).flatMap((block) => {
148
138
  if (block.type !== 'tool_use' || typeof block.id !== 'string' || typeof block.name !== 'string')
@@ -263,7 +263,9 @@ function nativeTranscriptTurnIds(sessionId, turns) {
263
263
  return turns.map((entries) => {
264
264
  const user = entries.find(({ entry }) => entry.kind === 'message' && entry.role === 'USER')?.entry;
265
265
  const first = user ?? entries[0]?.entry;
266
- const base = first?.nativeId ?? nativeTranscriptItemFingerprint(first);
266
+ const base = first?.nativeId ??
267
+ nativeTranscriptSourceIdentity(first) ??
268
+ nativeTranscriptItemFingerprint(first);
267
269
  const occurrence = occurrences.get(base) ?? 0;
268
270
  occurrences.set(base, occurrence + 1);
269
271
  return `${sessionId}:native:${base}${occurrence === 0 ? '' : `:${occurrence}`}`;
@@ -276,14 +278,20 @@ function nativeTranscriptItemIdentities(entries) {
276
278
  nativeIdCounts.set(entry.nativeId, (nativeIdCounts.get(entry.nativeId) ?? 0) + 1);
277
279
  const occurrences = new Map();
278
280
  return entries.map(({ entry }) => {
279
- const base = entry.nativeId !== undefined && nativeIdCounts.get(entry.nativeId) === 1
280
- ? entry.nativeId
281
- : `${entry.nativeId === undefined ? '' : `${entry.nativeId}:`}${nativeTranscriptItemFingerprint(entry)}`;
281
+ const base = nativeTranscriptSourceIdentity(entry) ??
282
+ (entry.nativeId !== undefined && nativeIdCounts.get(entry.nativeId) === 1
283
+ ? entry.nativeId
284
+ : `${entry.nativeId === undefined ? '' : `${entry.nativeId}:`}${nativeTranscriptItemFingerprint(entry)}`);
282
285
  const occurrence = occurrences.get(base) ?? 0;
283
286
  occurrences.set(base, occurrence + 1);
284
287
  return occurrence === 0 ? base : `${base}:${occurrence}`;
285
288
  });
286
289
  }
290
+ function nativeTranscriptSourceIdentity(entry) {
291
+ return entry?.sourceItemId === undefined
292
+ ? undefined
293
+ : `source:${createHash('sha256').update(entry.sourceItemId).digest('base64url').slice(0, 24)}`;
294
+ }
287
295
  function nativeTranscriptItemFingerprint(entry) {
288
296
  return createHash('sha256')
289
297
  .update(JSON.stringify(entry ?? null))
@@ -350,6 +358,7 @@ function nativeTranscriptConversationItem(session, entry, index, itemIdentity, t
350
358
  const planText = entry.kind === 'message' && entry.role !== 'USER' ? claudePlanText(entryText) : undefined;
351
359
  const item = {
352
360
  id: `${turnId}:item:${itemIdentity}`,
361
+ ...(entry.sourceItemId === undefined ? {} : { sourceItemId: entry.sourceItemId }),
353
362
  sessionId: session.id,
354
363
  turnId,
355
364
  runId: null,
@@ -359,7 +368,7 @@ function nativeTranscriptConversationItem(session, entry, index, itemIdentity, t
359
368
  kind: isUserInputRequest
360
369
  ? 'user_input_request'
361
370
  : isReasoning
362
- ? 'reasoning_summary'
371
+ ? 'reasoning'
363
372
  : isContextCompaction
364
373
  ? 'context_compaction'
365
374
  : isToolCall
@@ -378,7 +387,7 @@ function nativeTranscriptConversationItem(session, entry, index, itemIdentity, t
378
387
  questions: userInputQuestions
379
388
  }
380
389
  : isReasoning
381
- ? { sections: entry.sections }
390
+ ? { representation: entry.representation ?? 'unspecified', sections: entry.sections }
382
391
  : isContextCompaction
383
392
  ? { phase: entry.phase, summary: entry.summary }
384
393
  : isToolCall
package/dist/workspace.js CHANGED
@@ -1187,7 +1187,7 @@ export function isWithinAllowedRoot(target, roots, platform = hostPlatform()) {
1187
1187
  });
1188
1188
  }
1189
1189
  /** Resolving both candidate and roots rejects Linux symlinks which point outside a root. */
1190
- export async function inspectWorkspace(requestedPath, allowedRoots) {
1190
+ export async function inspectWorkspace(requestedPath, allowedRoots, includeParentRepository = true) {
1191
1191
  const resolvedPath = await realpath(requestedPath);
1192
1192
  const resolvedRoots = await Promise.all(allowedRoots.map((root) => realpath(root)));
1193
1193
  const directory = await stat(resolvedPath);
@@ -1195,16 +1195,40 @@ export async function inspectWorkspace(requestedPath, allowedRoots) {
1195
1195
  throw new Error('WORKSPACE_NOT_ALLOWED');
1196
1196
  }
1197
1197
  try {
1198
- const summary = await readGitSummary(resolvedPath);
1198
+ const repositoryRoot = await resolveWorkspaceGitRoot(resolvedPath, includeParentRepository);
1199
+ const summary = { ...(await readGitSummary(repositoryRoot)), repositoryRoot };
1199
1200
  return { path: resolvedPath, kind: 'GIT_WORKSPACE', gitSummary: summary };
1200
1201
  }
1201
1202
  catch (error) {
1202
- if ((error instanceof GitCommandError && error.code === 128) || isGitUnavailable(error)) {
1203
+ if ((error instanceof GitCommandError && error.code === 128) ||
1204
+ isGitUnavailable(error) ||
1205
+ (error instanceof Error && error.message === 'WORKSPACE_NOT_GIT')) {
1203
1206
  return { path: resolvedPath, kind: 'DIRECTORY' };
1204
1207
  }
1205
1208
  throw error;
1206
1209
  }
1207
1210
  }
1211
+ /** Resolves the working tree without changing the registered Workspace identity. */
1212
+ export async function resolveWorkspaceGitRoot(workspacePath, includeParentRepository = true) {
1213
+ const workspace = await resolveRegisteredWorkspaceRoot(workspacePath);
1214
+ if (!includeParentRepository) {
1215
+ const marker = await lstat(resolve(workspace, '.git')).catch((error) => {
1216
+ if (error.code === 'ENOENT')
1217
+ return undefined;
1218
+ throw error;
1219
+ });
1220
+ if (marker === undefined ||
1221
+ marker.isSymbolicLink() ||
1222
+ (!marker.isFile() && !marker.isDirectory()))
1223
+ throw new Error('WORKSPACE_NOT_GIT');
1224
+ }
1225
+ const result = await runReadOnlyGit(workspace, ['rev-parse', '--show-toplevel']);
1226
+ const root = await realpath(result.text.replace(/\n$/u, ''));
1227
+ if (!includeParentRepository &&
1228
+ normalizeWorkspacePath(root) !== normalizeWorkspacePath(workspace))
1229
+ throw new Error('WORKSPACE_NOT_GIT');
1230
+ return root;
1231
+ }
1208
1232
  export async function readGitSummary(workspacePath) {
1209
1233
  const commonDir = (await runReadOnlyGit(workspacePath, ['rev-parse', '--git-common-dir'])).text.trim();
1210
1234
  const headResult = await runReadOnlyGitAllowFailure(workspacePath, [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -37,10 +37,10 @@
37
37
  "playwright": "1.62.1",
38
38
  "ws": "^8.21.3",
39
39
  "zod": "4.4.3",
40
- "@myagentroam/agent": "0.11.1",
40
+ "@myagentroam/agent": "0.11.3",
41
41
  "@myagentroam/code-navigation": "0.11.0",
42
- "@myagentroam/marctl": "0.11.0",
43
- "@myagentroam/protocol": "0.11.1"
42
+ "@myagentroam/protocol": "0.11.3",
43
+ "@myagentroam/marctl": "0.11.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/ws": "^8.18.1"