@myagentroam/node 0.9.4 → 0.9.6

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 (61) hide show
  1. package/dist/claude-agent-sdk.d.ts +1 -0
  2. package/dist/claude-agent-sdk.js +9 -0
  3. package/dist/codex-app-server.d.ts +1 -0
  4. package/dist/codex-app-server.js +4 -2
  5. package/dist/connector.d.ts +1 -0
  6. package/dist/connector.js +16 -11
  7. package/dist/native-jsonl-reader.d.ts +21 -0
  8. package/dist/native-jsonl-reader.js +89 -0
  9. package/dist/native-session-history.d.ts +20 -3
  10. package/dist/native-session-history.js +245 -38
  11. package/dist/opencode-server.d.ts +1 -0
  12. package/dist/opencode-server.js +1 -0
  13. package/dist/runner/abstract-runner.d.ts +11 -2
  14. package/dist/runner/abstract-runner.js +4 -0
  15. package/dist/runner/claude/managed-run-controller.js +3 -0
  16. package/dist/runner/claude-code-runner.d.ts +2 -1
  17. package/dist/runner/claude-code-runner.js +4 -0
  18. package/dist/runner/codex/managed-run-controller.js +6 -0
  19. package/dist/runner/codex-runner.d.ts +2 -1
  20. package/dist/runner/codex-runner.js +13 -2
  21. package/dist/runner/opencode/managed-run-controller.js +4 -0
  22. package/dist/runner/opencode-runner.d.ts +10 -3
  23. package/dist/runner/opencode-runner.js +87 -14
  24. package/dist/runner/runner-registry.js +16 -1
  25. package/dist/runtime-command-detector.js +1 -6
  26. package/dist/runtime-state.js +1 -0
  27. package/dist/service/conversation-history-service.d.ts +6 -5
  28. package/dist/service/conversation-history-service.js +151 -55
  29. package/dist/service/external-session-resume-service.d.ts +2 -0
  30. package/dist/service/external-session-resume-service.js +45 -34
  31. package/dist/service/native-session-projection-service.d.ts +1 -0
  32. package/dist/service/native-session-projection-service.js +4 -0
  33. package/dist/service/native-session-watch-service.d.ts +18 -0
  34. package/dist/service/native-session-watch-service.js +232 -34
  35. package/dist/service/run-workbench-service.d.ts +2 -4
  36. package/dist/service/run-workbench-service.js +2 -7
  37. package/dist/service/session-command-service.js +1 -1
  38. package/dist/service/session-identity-service.d.ts +1 -1
  39. package/dist/service/session-identity-service.js +1 -1
  40. package/dist/service/session-lifecycle-service.js +3 -0
  41. package/dist/service/session-message-service.d.ts +0 -1
  42. package/dist/service/session-message-service.js +15 -23
  43. package/dist/service/skill-directory-service.d.ts +1 -0
  44. package/dist/service/skill-directory-service.js +40 -6
  45. package/dist/service/workspace-domain-state.d.ts +3 -0
  46. package/dist/service/workspace-domain-state.js +1 -0
  47. package/dist/service/workspace-file-service.d.ts +1 -0
  48. package/dist/service/workspace-file-service.js +14 -1
  49. package/dist/service/workspace-queue-workbench-service.d.ts +17 -0
  50. package/dist/service/workspace-queue-workbench-service.js +66 -6
  51. package/dist/service/workspace-watch-service.d.ts +7 -2
  52. package/dist/service/workspace-watch-service.js +32 -6
  53. package/dist/service/workspace-workbench-service.d.ts +14 -0
  54. package/dist/service/workspace-workbench-service.js +215 -16
  55. package/dist/util/personal-instructions.d.ts +2 -0
  56. package/dist/util/personal-instructions.js +31 -0
  57. package/dist/util/runner-native-session-parsers.d.ts +1 -0
  58. package/dist/util/runner-native-session-parsers.js +76 -12
  59. package/dist/workspace.d.ts +14 -8
  60. package/dist/workspace.js +65 -42
  61. package/package.json +2 -2
@@ -23,6 +23,7 @@ export interface ClaudeQueryInput {
23
23
  readonly onChannelReply?: (content: string) => void;
24
24
  readonly environment?: Readonly<Record<string, string>>;
25
25
  readonly mcpServers?: NonNullable<Options['mcpServers']>;
26
+ readonly personalInstructions?: string;
26
27
  }
27
28
  /** Image blocks accepted by the Claude Agent SDK message input. */
28
29
  export interface ClaudeImageAttachment {
@@ -64,6 +64,15 @@ export class ClaudeAgentSdkAdapter {
64
64
  // This is the SDK's documented API-side public summary mode. We never
65
65
  // enable or forward raw thinking deltas to the Workbench.
66
66
  settings: { showThinkingSummaries: true },
67
+ ...(input.personalInstructions === undefined
68
+ ? {}
69
+ : {
70
+ systemPrompt: {
71
+ type: 'preset',
72
+ preset: 'claude_code',
73
+ append: input.personalInstructions
74
+ }
75
+ }),
67
76
  abortController,
68
77
  canUseTool: input.onPermission,
69
78
  ...(input.mcpServers !== undefined || channelEnabled
@@ -50,6 +50,7 @@ export interface CodexRunConfiguration {
50
50
  readonly serviceTier?: 'fast';
51
51
  readonly collaborationMode?: 'default' | 'plan';
52
52
  readonly mcpServers?: unknown;
53
+ readonly developerInstructions?: string;
53
54
  }
54
55
  interface CodexRpcErrorPayload {
55
56
  readonly code?: unknown;
@@ -358,6 +358,9 @@ export class CodexAppServerClient {
358
358
  function codexThreadConfiguration(cwd, configuration) {
359
359
  return {
360
360
  ...modelOption(configuration.model),
361
+ ...(configuration.developerInstructions === undefined
362
+ ? {}
363
+ : { developerInstructions: configuration.developerInstructions }),
361
364
  ...(configuration.mcpServers === undefined
362
365
  ? {}
363
366
  : { config: { mcp_servers: configuration.mcpServers } }),
@@ -376,8 +379,7 @@ function codexTurnConfiguration(cwd, configuration) {
376
379
  mode: configuration.collaborationMode,
377
380
  settings: {
378
381
  model: configuration.model ?? 'gpt-5.6-sol',
379
- reasoningEffort: configuration.effort ?? null,
380
- developerInstructions: null
382
+ reasoningEffort: configuration.effort ?? null
381
383
  }
382
384
  }
383
385
  }),
@@ -3,6 +3,7 @@ export type { NodeConnectorOptions } from './connector/node-connector-options.js
3
3
  export { nodeConnectEndpoint, validatedCapabilities } from './runner/codex/conversation-parser.js';
4
4
  export declare function nextReconnectDelay(attempt: number): number;
5
5
  export declare class NodeConnector {
6
+ private readonly nodeGeneration;
6
7
  private readonly controlChannel;
7
8
  private readonly controlMessages;
8
9
  private readonly nodeRequests;
package/dist/connector.js CHANGED
@@ -86,6 +86,7 @@ function upgradeMetadata(metadata, status) {
86
86
  return next;
87
87
  }
88
88
  export class NodeConnector {
89
+ nodeGeneration = crypto.randomUUID().replace(/-/g, '');
89
90
  controlChannel;
90
91
  controlMessages;
91
92
  nodeRequests;
@@ -153,7 +154,7 @@ export class NodeConnector {
153
154
  discover: (workspaceId) => this.discoverWorkspaceSessions(workspaceId),
154
155
  listPage: (workspaceId) => this.executeNodeOperation('session.list', { workspaceId, limit: 20 }),
155
156
  active: (workspaceId) => this.workspaceWatchService.topicActive(workspaceId, 'sessions'),
156
- emitRevision: (workspaceId, revision) => this.emitWorkbenchEvent('workspace', { workspaceId, topic: 'sessions', revision }),
157
+ emitRevision: (workspaceId) => void this.workspaceWorkbenchService?.refreshTopic(workspaceId, 'sessions'),
157
158
  onPending: (workspaceId, waitMs) => nodeLog('native.session.discovery.pending', {
158
159
  nodeId: this.config?.nodeId,
159
160
  workspaceId,
@@ -192,8 +193,9 @@ export class NodeConnector {
192
193
  });
193
194
  nativeSessionWatchService = new NativeSessionWatchService({
194
195
  resolve: async (sessionId) => this.runtime.getAgentSession(sessionId) ??
195
- this.nativeSessionProjectionService.resolve(sessionId),
196
+ this.nativeSessionProjectionService.resolveCurrent(sessionId),
196
197
  managedActive: (session) => hasManagedActiveRun(this.runtime, session),
198
+ suspended: (sessionId) => this.externalSessionResumeService.transitioning(sessionId),
197
199
  refreshActivity: (session) => this.conversationHistoryService.refreshExternalActivity(session),
198
200
  present: (session) => this.sessionPresentationService.present(session),
199
201
  readPage: (session, limit) => this.conversationHistoryService.read(session, { limit }),
@@ -203,7 +205,10 @@ export class NodeConnector {
203
205
  emitPage: (session, page) => this.emitWorkbenchEvent('conversation', {
204
206
  sessionId: session.id,
205
207
  snapshot: this.conversationSegmentService.projectInitial(session, page, 10)
206
- })
208
+ }),
209
+ nodeId: () => this.config?.nodeId ?? 'runtime-node',
210
+ nodeGeneration: () => this.nodeGeneration,
211
+ emitWatchEvent: (payload) => this.send('watch.event', payload)
207
212
  });
208
213
  constructor(options = {}) {
209
214
  this.config = options.config;
@@ -396,10 +401,8 @@ export class NodeConnector {
396
401
  readImage: async (runId, imageIndex) => this.runAttachmentService.read(runId, imageIndex) ??
397
402
  (await this.runAttachmentService.readNative(runId, imageIndex)),
398
403
  respondUserInput: (runId, requestId, answers) => this.runnerInteractionService.respondUserInput(runId, requestId, answers),
399
- deleteQueuedStart: (runId) => this.workspaceQueueWorkbenchService.remove(runId),
400
- clearImages: (runId) => this.runAttachmentService.clear(runId),
401
- cleanupAttachments: (runId) => this.runAttachmentService.cleanup(runId),
402
- emitQueue: (workspaceId, sessionId) => this.runEventBridge.emitQueue(workspaceId, sessionId),
404
+ cancelQueued: (runId) => this.workspaceQueueWorkbenchService.cancelQueued(runId),
405
+ pauseQueue: (sessionId) => this.workspaceQueueWorkbenchService.pauseForInterrupt(sessionId),
403
406
  emitRun: (runId, eventType, payload, status) => this.runEventBridge.emitRun(runId, eventType, payload, status),
404
407
  control: (operation, payload) => this.controlMessages.handle(JSON.stringify(createEnvelope(operation, payload))),
405
408
  markInterrupted: (runId) => this.runState.interruptRequested.add(runId)
@@ -447,7 +450,7 @@ export class NodeConnector {
447
450
  migrateRunnerState: (previousId, nextId) => this.runEventBridge.migrateRunnerSessionIdentity(previousId, nextId),
448
451
  emitSession: (session) => this.emitWorkbenchEvent('session', { session }),
449
452
  emitRun: (run) => this.emitWorkbenchEvent('run', { run }),
450
- emitQueue: (workspaceId, sessionId) => this.runEventBridge.emitQueue(workspaceId, sessionId),
453
+ emitQueue: (sessionId) => this.workspaceQueueWorkbenchService.publish(sessionId),
451
454
  emitTurn: (turn) => this.emitConversationTurn(turn)
452
455
  });
453
456
  this.sessionLifecycleService = new SessionLifecycleService({
@@ -501,7 +504,6 @@ export class NodeConnector {
501
504
  emitSession: (session) => this.emitWorkbenchEvent('session', {
502
505
  session: this.sessionPresentationService.present(session)
503
506
  }),
504
- emitQueue: (workspaceId, sessionId) => this.runEventBridge.emitQueue(workspaceId, sessionId),
505
507
  channelToken: (sessionId) => {
506
508
  const session = this.runtime.getAgentSession(sessionId);
507
509
  return session === undefined
@@ -650,7 +652,7 @@ export class NodeConnector {
650
652
  watches: this.workspaceWatchService,
651
653
  sessions: this.workspaceSessionService,
652
654
  changes: this.workspaceChangeService,
653
- emit: (workspaceId, topic, revision) => this.emitWorkbenchEvent('workspace', { workspaceId, topic, revision }),
655
+ emit: (workspaceId, topic) => void this.workspaceWorkbenchService?.refreshTopic(workspaceId, topic),
654
656
  send: (type, payload, replyTo) => this.send(type, payload, replyTo)
655
657
  });
656
658
  this.runEventBridge = new NodeRunEventBridge({
@@ -711,7 +713,10 @@ export class NodeConnector {
711
713
  uploads: this.workspaceUploadService,
712
714
  changes: this.workspaceChangeService,
713
715
  listSessions: (workspaceId) => this.executeNodeOperation('session.list', { workspaceId, limit: 20 }),
714
- invalidate: (workspaceId) => this.workspaceCoordinator.invalidate(workspaceId)
716
+ invalidate: (workspaceId) => this.workspaceCoordinator.invalidate(workspaceId),
717
+ nodeId: () => this.config?.nodeId ?? 'runtime-node',
718
+ nodeGeneration: () => this.nodeGeneration,
719
+ emitWatchEvent: (payload) => this.send('watch.event', payload)
715
720
  });
716
721
  this.operationRouter.registerAll(this.runnerService.operations());
717
722
  this.operationRouter.registerAll(this.terminalService.operations());
@@ -3,6 +3,27 @@ export interface JsonlWindow {
3
3
  readonly truncated: boolean;
4
4
  readonly size: number;
5
5
  }
6
+ export interface JsonlBackwardWindow extends JsonlWindow {
7
+ readonly pageContent: string;
8
+ readonly start: number;
9
+ readonly end: number;
10
+ readonly snapshotEnd: number;
11
+ readonly fileAnchor: string;
12
+ readonly anchorBytes: number;
13
+ }
14
+ export declare function readJsonlFileAnchor(path: string, anchorBytes: number): Promise<string | undefined>;
15
+ /** Reads one newline-aligned window backwards from an opaque byte boundary. */
16
+ export declare function readJsonlBackwardWindow(path: string, options: {
17
+ readonly headBytes: number;
18
+ readonly targetLines: number;
19
+ readonly maxBytes: number;
20
+ readonly page?: {
21
+ readonly snapshotEnd: number;
22
+ readonly nextEnd: number;
23
+ readonly fileAnchor: string;
24
+ readonly anchorBytes: number;
25
+ };
26
+ }): Promise<JsonlBackwardWindow | undefined>;
6
27
  /** Reads bounded head/tail windows without ever loading an unbounded transcript. */
7
28
  export declare function readJsonlWindow(path: string, options: {
8
29
  readonly headBytes?: number;
@@ -1,5 +1,94 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { open, readdir } from 'node:fs/promises';
2
3
  import { join } from 'node:path';
4
+ export async function readJsonlFileAnchor(path, anchorBytes) {
5
+ try {
6
+ const handle = await open(path, 'r');
7
+ try {
8
+ const metadata = await handle.stat();
9
+ if (anchorBytes < 0 || anchorBytes > metadata.size)
10
+ return undefined;
11
+ return createHash('sha256')
12
+ .update(await readAt(handle, anchorBytes, 0))
13
+ .digest('base64url');
14
+ }
15
+ finally {
16
+ await handle.close();
17
+ }
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ /** Reads one newline-aligned window backwards from an opaque byte boundary. */
24
+ export async function readJsonlBackwardWindow(path, options) {
25
+ try {
26
+ const handle = await open(path, 'r');
27
+ try {
28
+ const metadata = await handle.stat();
29
+ const snapshotEnd = options.page?.snapshotEnd ?? metadata.size;
30
+ if (snapshotEnd > metadata.size)
31
+ return undefined;
32
+ const anchorBytes = options.page?.anchorBytes ?? Math.min(4096, snapshotEnd);
33
+ if (anchorBytes < 0 || anchorBytes > snapshotEnd)
34
+ return undefined;
35
+ const anchor = await readAt(handle, anchorBytes, 0);
36
+ const fileAnchor = createHash('sha256').update(anchor).digest('base64url');
37
+ if (options.page !== undefined && options.page.fileAnchor !== fileAnchor)
38
+ return undefined;
39
+ const headSize = Math.min(options.headBytes, snapshotEnd);
40
+ const end = Math.min(Math.max(options.page?.nextEnd ?? snapshotEnd, headSize), snapshotEnd);
41
+ const rawStart = Math.max(headSize, end - options.maxBytes);
42
+ const head = await readAt(handle, headSize, 0);
43
+ let tail = await readAt(handle, end - rawStart, rawStart);
44
+ let start = rawStart;
45
+ if (rawStart > headSize) {
46
+ const newline = tail.indexOf(0x0a);
47
+ if (newline < 0) {
48
+ tail = Buffer.alloc(0);
49
+ start = end;
50
+ }
51
+ else {
52
+ start += newline + 1;
53
+ tail = tail.subarray(newline + 1);
54
+ }
55
+ }
56
+ if (tail.length > 0) {
57
+ let lines = tail.at(-1) === 0x0a ? 0 : 1;
58
+ for (let index = tail.length - 1; index >= 0; index -= 1) {
59
+ if (tail[index] !== 0x0a)
60
+ continue;
61
+ lines += 1;
62
+ if (lines <= options.targetLines)
63
+ continue;
64
+ start += index + 1;
65
+ tail = tail.subarray(index + 1);
66
+ break;
67
+ }
68
+ }
69
+ const contiguous = start === headSize;
70
+ return {
71
+ content: contiguous
72
+ ? Buffer.concat([head, tail]).toString('utf8')
73
+ : `${head.toString('utf8')}${headSize > 0 && tail.length > 0 ? '\n' : ''}${tail.toString('utf8')}`,
74
+ truncated: start > headSize,
75
+ pageContent: tail.toString('utf8'),
76
+ size: metadata.size,
77
+ start,
78
+ end,
79
+ snapshotEnd,
80
+ fileAnchor,
81
+ anchorBytes
82
+ };
83
+ }
84
+ finally {
85
+ await handle.close();
86
+ }
87
+ }
88
+ catch {
89
+ return undefined;
90
+ }
91
+ }
3
92
  /** Reads bounded head/tail windows without ever loading an unbounded transcript. */
4
93
  export async function readJsonlWindow(path, options) {
5
94
  try {
@@ -59,6 +59,16 @@ export interface NativeSessionHistory {
59
59
  readonly lastActivityAt: number;
60
60
  /** The bounded reader omitted bytes between its header and tail windows. */
61
61
  readonly truncated?: boolean;
62
+ /** Opaque paging facts for reading the preceding newline-aligned JSONL window. */
63
+ readonly windowStart?: number;
64
+ readonly windowEnd?: number;
65
+ readonly snapshotEnd?: number;
66
+ readonly fileAnchor?: string;
67
+ readonly anchorBytes?: number;
68
+ /** The supplied page cursor no longer matched this transcript. */
69
+ readonly cursorReset?: boolean;
70
+ /** Page-size input used to derive the stable JSONL line window. */
71
+ readonly windowReadLimit?: number;
62
72
  }
63
73
  /** Actual context-window facts emitted by the Codex native transcript. */
64
74
  export interface NativeCodexContextUsage {
@@ -78,12 +88,19 @@ export interface NativeClaudeContextUsage {
78
88
  export declare function discoverNativeSessions(runner: RunnerName, workspacePath: string): Promise<readonly NativeSessionHistory[]>;
79
89
  /**
80
90
  * Reads every supported transcript header before applying a Workspace filter.
81
- * The caller owns in-process caching; a fixed file-count cutoff would silently
82
- * hide older sessions from a project with a large global history.
91
+ * A short Runner-level snapshot is shared across Workspaces and refreshes only
92
+ * files whose size or modification time changed. A fixed file-count cutoff
93
+ * would silently hide older sessions from a project with a large global history.
83
94
  */
84
95
  export declare function discoverAllNativeSessions(runner: RunnerName): Promise<readonly NativeSessionHistory[]>;
85
96
  /** Reads one already-discovered external session again to follow appended JSONL records. */
86
- export declare function readNativeSession(runner: RunnerName, workspacePath: string, externalSessionId: string): Promise<NativeSessionHistory | undefined>;
97
+ export declare function readNativeSession(runner: RunnerName, workspacePath: string, externalSessionId: string, page?: {
98
+ readonly snapshotEnd: number;
99
+ readonly nextEnd: number;
100
+ readonly fileAnchor: string;
101
+ readonly anchorBytes: number;
102
+ readonly readLimit?: number;
103
+ }, limit?: number): Promise<NativeSessionHistory | undefined>;
87
104
  /**
88
105
  * Codex records the most recent prompt token count alongside the model context
89
106
  * window in `event_msg/token_count`. Read only the tail of an already