@myagentroam/node 0.9.2 → 0.9.4

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 (81) hide show
  1. package/dist/codex-app-server.d.ts +24 -0
  2. package/dist/codex-app-server.js +100 -5
  3. package/dist/connector.js +18 -15
  4. package/dist/control-outbound-scheduler.d.ts +4 -1
  5. package/dist/control-outbound-scheduler.js +39 -6
  6. package/dist/database.d.ts +2 -19
  7. package/dist/database.js +1 -62
  8. package/dist/event-buffer.d.ts +2 -5
  9. package/dist/event-buffer.js +3 -12
  10. package/dist/native-session-history.js +33 -12
  11. package/dist/runner/claude/managed-run-controller.d.ts +0 -1
  12. package/dist/runner/claude/managed-run-controller.js +17 -12
  13. package/dist/runner/codex/conversation-parser.d.ts +1 -1
  14. package/dist/runner/codex/conversation-parser.js +3 -0
  15. package/dist/runner/codex/managed-run-controller.d.ts +11 -2
  16. package/dist/runner/codex/managed-run-controller.js +79 -24
  17. package/dist/runner/codex-runner.d.ts +3 -0
  18. package/dist/runner/codex-runner.js +42 -3
  19. package/dist/runner/opencode/conversation-parser.js +1 -1
  20. package/dist/runner/opencode/managed-run-controller.d.ts +7 -2
  21. package/dist/runner/opencode/managed-run-controller.js +72 -14
  22. package/dist/runner/opencode-runner.d.ts +3 -0
  23. package/dist/runner/opencode-runner.js +25 -0
  24. package/dist/runner-profiles.js +10 -6
  25. package/dist/runtime-state.d.ts +7 -12
  26. package/dist/runtime-state.js +43 -53
  27. package/dist/service/attachment-domain-state.d.ts +2 -0
  28. package/dist/service/attachment-domain-state.js +2 -0
  29. package/dist/service/conversation-history-service.d.ts +8 -0
  30. package/dist/service/conversation-history-service.js +181 -9
  31. package/dist/service/conversation-segment-service.js +17 -10
  32. package/dist/service/external-session-resume-service.js +9 -0
  33. package/dist/service/fake-managed-run-service.d.ts +0 -1
  34. package/dist/service/fake-managed-run-service.js +0 -2
  35. package/dist/service/native-session-projection-service.js +5 -0
  36. package/dist/service/native-session-watch-service.d.ts +8 -2
  37. package/dist/service/native-session-watch-service.js +28 -10
  38. package/dist/service/node-connection-lifecycle-service.d.ts +0 -2
  39. package/dist/service/node-connection-lifecycle-service.js +0 -1
  40. package/dist/service/node-control-channel.js +1 -1
  41. package/dist/service/node-control-message-service.d.ts +0 -1
  42. package/dist/service/node-control-message-service.js +9 -6
  43. package/dist/service/node-request-service.js +2 -1
  44. package/dist/service/node-workspace-coordinator.d.ts +1 -4
  45. package/dist/service/node-workspace-coordinator.js +2 -12
  46. package/dist/service/run-attachment-service.js +30 -6
  47. package/dist/service/run-event-service.d.ts +1 -0
  48. package/dist/service/run-event-service.js +80 -0
  49. package/dist/service/run-workbench-service.d.ts +1 -1
  50. package/dist/service/run-workbench-service.js +1 -5
  51. package/dist/service/runner-interaction-service.d.ts +1 -2
  52. package/dist/service/runner-interaction-service.js +1 -4
  53. package/dist/service/session-context-service.d.ts +1 -0
  54. package/dist/service/session-context-service.js +14 -0
  55. package/dist/service/session-domain-state.d.ts +1 -0
  56. package/dist/service/session-domain-state.js +1 -0
  57. package/dist/service/session-lifecycle-service.js +4 -12
  58. package/dist/service/session-message-service.d.ts +1 -0
  59. package/dist/service/session-message-service.js +109 -85
  60. package/dist/service/skill-directory-service.d.ts +3 -1
  61. package/dist/service/skill-directory-service.js +28 -4
  62. package/dist/service/skill-node-operation-service.d.ts +1 -1
  63. package/dist/service/skill-node-operation-service.js +4 -2
  64. package/dist/service/workbench-event-service.d.ts +1 -13
  65. package/dist/service/workbench-event-service.js +2 -36
  66. package/dist/service/workbench-manifest-service.d.ts +0 -1
  67. package/dist/service/workbench-manifest-service.js +0 -1
  68. package/dist/service/workspace-change-service.js +1 -1
  69. package/dist/service/workspace-domain-state.d.ts +1 -0
  70. package/dist/service/workspace-queue-workbench-service.d.ts +13 -2
  71. package/dist/service/workspace-queue-workbench-service.js +8 -2
  72. package/dist/service/workspace-upload-service.d.ts +2 -0
  73. package/dist/service/workspace-upload-service.js +28 -6
  74. package/dist/service/workspace-workbench-service.d.ts +0 -3
  75. package/dist/service/workspace-workbench-service.js +0 -17
  76. package/dist/util/runner-error.d.ts +6 -0
  77. package/dist/util/runner-error.js +52 -0
  78. package/dist/util/runner-native-session-parsers.d.ts +2 -1
  79. package/dist/util/runner-native-session-parsers.js +63 -15
  80. package/dist/workspace.js +33 -8
  81. package/package.json +2 -2
@@ -0,0 +1,52 @@
1
+ const MAX_RUNNER_ERROR_MESSAGE = 1000;
2
+ export function runnerErrorProjection(error, fallbackCode) {
3
+ const httpStatus = findHttpStatus(error);
4
+ const message = findMessage(error) ?? fallbackCode;
5
+ return {
6
+ code: httpStatus === null ? fallbackCode : `HTTP_${httpStatus}`,
7
+ message: message.slice(0, MAX_RUNNER_ERROR_MESSAGE),
8
+ httpStatus
9
+ };
10
+ }
11
+ function findHttpStatus(value, depth = 0) {
12
+ if (depth > 3 || value === null || typeof value !== 'object')
13
+ return null;
14
+ const record = value;
15
+ for (const key of ['status', 'statusCode', 'status_code', 'httpStatus', 'http_status']) {
16
+ const candidate = record[key];
17
+ if (((typeof candidate === 'number' && Number.isInteger(candidate)) ||
18
+ (typeof candidate === 'string' && /^\d{3}$/u.test(candidate))) &&
19
+ Number(candidate) >= 100 &&
20
+ Number(candidate) <= 599)
21
+ return Number(candidate);
22
+ }
23
+ const match = findMessage(value)?.match(/(?:^|\b(?:http(?:\s+status)?|status(?:\s+code)?|error)\s*[:=_-]?\s*)([1-5]\d{2})\b/iu);
24
+ if (match !== null && match !== undefined)
25
+ return Number(match[1]);
26
+ for (const key of ['error', 'cause', 'response', 'data']) {
27
+ const nested = findHttpStatus(record[key], depth + 1);
28
+ if (nested !== null)
29
+ return nested;
30
+ }
31
+ return null;
32
+ }
33
+ function findMessage(value, depth = 0) {
34
+ if (typeof value === 'string')
35
+ return value.trim() || undefined;
36
+ if (value instanceof Error && value.message.trim())
37
+ return value.message.trim();
38
+ if (depth > 3 || value === null || typeof value !== 'object')
39
+ return undefined;
40
+ const record = value;
41
+ for (const key of ['message', 'error_description', 'detail', 'result']) {
42
+ const candidate = record[key];
43
+ if (typeof candidate === 'string' && candidate.trim())
44
+ return candidate.trim();
45
+ }
46
+ for (const key of ['error', 'cause', 'response', 'data']) {
47
+ const nested = findMessage(record[key], depth + 1);
48
+ if (nested !== undefined)
49
+ return nested;
50
+ }
51
+ return undefined;
52
+ }
@@ -15,6 +15,7 @@ export interface ConversationHistoryPage {
15
15
  readonly source: ConversationHistorySource;
16
16
  readonly readAt: number;
17
17
  readonly latestVisibleAt: number;
18
+ readonly deferredOlderHistory?: boolean;
18
19
  }
19
20
  export declare function extractId(result: unknown, key: 'thread' | 'turn'): string;
20
21
  export declare function settleWithin<T>(promise: Promise<T>, milliseconds: number): Promise<T | undefined>;
@@ -50,7 +51,7 @@ export declare function nativeConversationPage(session: NodeAgentSession, histor
50
51
  */
51
52
  export declare function nativeUserClientMessageId(entry: Extract<NativeSessionHistory['items'][number], {
52
53
  readonly kind: 'message';
53
- }>, createdAt: number, runtimeTurns: readonly NodeConversationTurn[]): string | null;
54
+ }>, createdAt: number | null, runtimeTurns: readonly NodeConversationTurn[]): string | null;
54
55
  /**
55
56
  * Normalize the documented App Server `thread/read(includeTurns: true)`
56
57
  * result. Only items with a user-visible Workbench representation are kept;
@@ -146,23 +146,53 @@ export function deduplicateDirectSessions(sessions) {
146
146
  export function nativeConversationPage(session, history, input, runtimeTurns = [], registerImages = () => []) {
147
147
  const limit = Math.min(Math.max(input.limit ?? 30, 1), 500);
148
148
  const nativeTurns = nativeTranscriptTurns(history.items);
149
+ const representedClientMessageIds = new Set(nativeTurns.flatMap((entries) => entries.flatMap(({ entry, index }) => {
150
+ if (entry.kind !== 'message' || entry.role !== 'USER')
151
+ return [];
152
+ const clientMessageId = nativeUserClientMessageId(entry, entry.createdAt ?? index, runtimeTurns);
153
+ return clientMessageId === null ? [] : [clientMessageId];
154
+ })));
155
+ const missingRuntimeErrorTurns = input.cursor === undefined
156
+ ? runtimeTurns.filter((turn) => {
157
+ const clientMessageId = runtimeTurnClientMessageId(turn);
158
+ return (turn.items.some((item) => item.kind === 'error') &&
159
+ (clientMessageId === undefined || !representedClientMessageIds.has(clientMessageId)));
160
+ })
161
+ : [];
149
162
  const end = nativePageEnd(input.cursor, session.id, nativeTurns.length);
150
163
  const start = Math.max(0, end - limit);
151
- const turns = nativeTurns
164
+ const nativePageTurns = nativeTurns
152
165
  .slice(start, end)
153
166
  .map((entries, relativeIndex) => {
154
167
  const firstIndex = entries[0]?.index ?? 0;
155
- const turnId = `${session.id}:native:${firstIndex}`;
156
- const items = entries.map(({ entry, index }) => nativeTranscriptConversationItem(session, entry, index, turnId, runtimeTurns, registerImages));
157
- const startedAt = items[0]?.startedAt ?? firstIndex;
168
+ const nativeUser = entries.find(({ entry }) => entry.kind === 'message' && entry.role === 'USER')?.entry;
169
+ const matchedClientMessageId = nativeUser?.kind === 'message'
170
+ ? nativeUserClientMessageId(nativeUser, nativeUser.createdAt, runtimeTurns)
171
+ : null;
172
+ const runtimeTurn = typeof matchedClientMessageId === 'string'
173
+ ? runtimeTurns.find((turn) => runtimeTurnClientMessageId(turn) === matchedClientMessageId)
174
+ : undefined;
175
+ const turnId = runtimeTurn?.id ?? `${session.id}:native:${firstIndex}`;
176
+ const nativeItems = entries.map(({ entry, index }) => ({
177
+ ...nativeTranscriptConversationItem(session, entry, index, turnId, runtimeTurns, registerImages),
178
+ runId: runtimeTurn?.runId ?? null
179
+ }));
180
+ const runtimeErrors = (runtimeTurn?.items ?? [])
181
+ .filter((item) => item.kind === 'error')
182
+ .map((item) => ({ ...item, turnId, runId: runtimeTurn?.runId ?? null }));
183
+ const items = [
184
+ ...nativeItems,
185
+ ...runtimeErrors.filter((error) => !nativeItems.some((item) => item.id === error.id))
186
+ ];
187
+ const startedAt = items[0]?.startedAt ?? null;
158
188
  const completedAt = items.at(-1)?.completedAt ?? startedAt;
159
189
  const after = lastNativeItemId(entries);
160
190
  return {
161
191
  id: turnId,
162
192
  sessionId: session.id,
163
- runId: null,
193
+ runId: runtimeTurn?.runId ?? null,
164
194
  userItemId: items.find((item) => item.kind === 'user_message')?.id ?? null,
165
- status: 'SUCCEEDED',
195
+ status: runtimeErrors.length > 0 ? 'FAILED' : 'SUCCEEDED',
166
196
  model: null,
167
197
  effort: null,
168
198
  access: null,
@@ -179,13 +209,24 @@ export function nativeConversationPage(session, history, input, runtimeTurns = [
179
209
  completedAt
180
210
  };
181
211
  });
212
+ const combined = [...nativePageTurns, ...missingRuntimeErrorTurns];
213
+ const dropped = combined.slice(0, Math.max(0, combined.length - limit));
214
+ const turns = combined.slice(-limit);
215
+ const nativePageIds = new Set(nativePageTurns.map((turn) => turn.id));
216
+ const nextEnd = start + dropped.filter((turn) => nativePageIds.has(turn.id)).length;
182
217
  return {
183
218
  turns,
184
- nextCursor: start > 0
185
- ? Buffer.from(JSON.stringify({ sessionId: session.id, end: start })).toString('base64url')
219
+ nextCursor: nextEnd > 0
220
+ ? Buffer.from(JSON.stringify({ sessionId: session.id, end: nextEnd })).toString('base64url')
186
221
  : null
187
222
  };
188
223
  }
224
+ function runtimeTurnClientMessageId(turn) {
225
+ const user = turn.items.find((item) => item.kind === 'user_message');
226
+ return isPlainRecord(user?.payload) && typeof user.payload.clientMessageId === 'string'
227
+ ? user.payload.clientMessageId
228
+ : undefined;
229
+ }
189
230
  function lastNativeItemId(entries) {
190
231
  return entries.findLast(({ entry }) => entry.nativeId !== undefined)?.entry.nativeId;
191
232
  }
@@ -204,7 +245,7 @@ function nativeTranscriptTurns(items) {
204
245
  return turns;
205
246
  }
206
247
  function nativeTranscriptConversationItem(session, entry, index, turnId, runtimeTurns, registerImages) {
207
- const time = entry.createdAt ?? index;
248
+ const time = entry.createdAt;
208
249
  const isToolCall = entry.kind === 'tool_call';
209
250
  const isUnknown = entry.kind === 'unknown';
210
251
  const isReasoning = entry.kind === 'reasoning_summary';
@@ -299,6 +340,7 @@ function nativeTranscriptConversationItem(session, entry, index, turnId, runtime
299
340
  * runtime user messages by content and creation time so native history replaces the optimistic Turn.
300
341
  */
301
342
  export function nativeUserClientMessageId(entry, createdAt, runtimeTurns) {
343
+ const candidates = [];
302
344
  for (const turn of runtimeTurns) {
303
345
  const user = turn.items.find((item) => item.kind === 'user_message');
304
346
  if (user === undefined || !isPlainRecord(user.payload))
@@ -309,13 +351,19 @@ export function nativeUserClientMessageId(entry, createdAt, runtimeTurns) {
309
351
  if (clientMessageId === null || payload.text !== stripClaudePlanTag(entry.text))
310
352
  continue;
311
353
  const runtimeCreatedAt = user.startedAt ?? turn.startedAt;
312
- if (runtimeCreatedAt !== null &&
313
- runtimeCreatedAt !== undefined &&
314
- Math.abs(createdAt - runtimeCreatedAt) > 60_000)
315
- continue;
316
- return clientMessageId;
354
+ candidates.push({ clientMessageId, createdAt: runtimeCreatedAt ?? null });
317
355
  }
318
- return null;
356
+ if (createdAt === null)
357
+ return candidates.length === 1 ? candidates[0].clientMessageId : null;
358
+ const timed = candidates
359
+ .filter((candidate) => candidate.createdAt !== null)
360
+ .map((candidate) => ({
361
+ ...candidate,
362
+ distance: Math.abs(createdAt - candidate.createdAt)
363
+ }))
364
+ .filter((candidate) => candidate.distance <= 60_000)
365
+ .sort((left, right) => left.distance - right.distance);
366
+ return timed[0]?.clientMessageId ?? null;
319
367
  }
320
368
  /**
321
369
  * Normalize the documented App Server `thread/read(includeTurns: true)`
package/dist/workspace.js CHANGED
@@ -1232,14 +1232,7 @@ export async function readCurrentChanges(workspacePath) {
1232
1232
  export async function readCurrentChangeDiff(workspacePath, requestedPath) {
1233
1233
  const path = safeGitWorkspacePath(requestedPath);
1234
1234
  await assertCurrentChangePathAccessible(workspacePath, path);
1235
- const result = await runGit(workspacePath, [
1236
- 'diff',
1237
- '--no-ext-diff',
1238
- '--no-textconv',
1239
- 'HEAD',
1240
- '--',
1241
- `:(literal)${path}`
1242
- ]);
1235
+ const result = await readCurrentChangeGitDiff(workspacePath, path);
1243
1236
  const after = await readWorkingTreeText(workspacePath, path);
1244
1237
  const binary = result.output.includes(0) || result.text.includes('Binary files ') || after?.binary === true;
1245
1238
  if (binary) {
@@ -1263,6 +1256,38 @@ export async function readCurrentChangeDiff(workspacePath, requestedPath) {
1263
1256
  ...(after === undefined ? {} : { afterTruncated: after.truncated })
1264
1257
  };
1265
1258
  }
1259
+ async function readCurrentChangeGitDiff(workspacePath, path) {
1260
+ const untracked = await runGit(workspacePath, [
1261
+ 'ls-files',
1262
+ '--others',
1263
+ '--exclude-standard',
1264
+ '-z',
1265
+ '--',
1266
+ `:(literal)${path}`
1267
+ ]);
1268
+ if (untracked.text.split('\0').includes(path)) {
1269
+ const result = await runGitAllowFailure(workspacePath, [
1270
+ 'diff',
1271
+ '--no-index',
1272
+ '--no-ext-diff',
1273
+ '--no-textconv',
1274
+ '--',
1275
+ '/dev/null',
1276
+ path
1277
+ ]);
1278
+ if (result.code !== 0 && result.code !== 1)
1279
+ throw new GitCommandError(result.code, result.stderr);
1280
+ return result;
1281
+ }
1282
+ return runGit(workspacePath, [
1283
+ 'diff',
1284
+ '--no-ext-diff',
1285
+ '--no-textconv',
1286
+ 'HEAD',
1287
+ '--',
1288
+ `:(literal)${path}`
1289
+ ]);
1290
+ }
1266
1291
  /** Current Changes must not expose an external link. */
1267
1292
  export async function isWorkspaceChangeVisible(workspacePath, requestedPath) {
1268
1293
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "type": "module",
6
6
  "files": [
@@ -24,7 +24,7 @@
24
24
  "node-pty": "1.1.0",
25
25
  "ws": "^8.21.3",
26
26
  "zod": "4.4.3",
27
- "@myagentroam/protocol": "^0.9.2"
27
+ "@myagentroam/protocol": "^0.9.4"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/ws": "^8.18.1"