@myagentroam/node 0.9.145 → 0.9.147

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.
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { LocalAgentHost, MarAgentError, createMarAgent, openMarAgentSessionCatalog } from '@myagentroam/agent';
2
+ import { LocalAgentHost, MarAgentError, createMarAgent, generateSessionTitle, openMarAgentSessionCatalog } from '@myagentroam/agent';
3
3
  import { projectMarAgentHistory } from './history-projection.js';
4
4
  import { MarAgentConversationNormalizer } from './conversation-normalizer.js';
5
5
  import { hostedConversationGeneratedImagesDisplayDirectory, hostedConversationImageRunId, marAgentGeneratedImageSources, marAgentGeneratedImagesDirectory } from './generated-images.js';
@@ -12,6 +12,7 @@ import { createImageGenerationExtensions } from './image-generation-extensions.j
12
12
  */
13
13
  export class MarAgentHostedConversationRuntime {
14
14
  #createAgent;
15
+ #generateTitle;
15
16
  #createHost;
16
17
  #openCatalog;
17
18
  #now;
@@ -22,6 +23,7 @@ export class MarAgentHostedConversationRuntime {
22
23
  #registerNativeImages;
23
24
  constructor(options = {}) {
24
25
  this.#createAgent = options.createAgent ?? createMarAgent;
26
+ this.#generateTitle = options.generateTitle ?? generateSessionTitle;
25
27
  this.#createHost = options.createHost ?? ((input) => new LocalAgentHost(input));
26
28
  this.#openCatalog = options.openCatalog ?? openMarAgentSessionCatalog;
27
29
  this.#now = options.now ?? Date.now;
@@ -109,7 +111,10 @@ export class MarAgentHostedConversationRuntime {
109
111
  async startRun(input) {
110
112
  if (this.#active.has(input.sessionId))
111
113
  throw new Error('HOSTED_CONVERSATION_RUN_ACTIVE');
112
- const { agent, mcp, closeImages } = await this.#createRuntime(input);
114
+ const { agent, mcp, closeImages, models } = await this.#createRuntime(input);
115
+ const titleController = new AbortController();
116
+ let titleTask;
117
+ const metadata = await agent.getSession(input.sessionId).catch(() => undefined);
113
118
  let execution;
114
119
  try {
115
120
  const session = await agent.openSession(input.sessionId);
@@ -132,6 +137,13 @@ export class MarAgentHostedConversationRuntime {
132
137
  const completion = (async () => {
133
138
  try {
134
139
  for await (const event of execution) {
140
+ if (event.type === 'execution.started' && metadata?.automaticTitlePending && !titleTask)
141
+ titleTask = this.#generateTitle({
142
+ models: sdkModelConfigurations(models),
143
+ currentModelId: input.modelId,
144
+ prompt: input.userPrompt,
145
+ signal: titleController.signal
146
+ }).catch(() => undefined);
135
147
  await this.#onEvent?.(input.conversationId, event);
136
148
  for (const projected of projectHostedEvent(normalizer, event, {
137
149
  clientMessageId: input.clientMessageId,
@@ -140,6 +152,17 @@ export class MarAgentHostedConversationRuntime {
140
152
  await input.onEvent?.(projected);
141
153
  }
142
154
  const result = await execution.result();
155
+ if (result.status === 'cancelled')
156
+ titleController.abort();
157
+ const generatedTitle = await titleTask;
158
+ if (generatedTitle && !titleController.signal.aborted)
159
+ await agent
160
+ .setSessionTitle(input.sessionId, generatedTitle, {
161
+ automatic: true,
162
+ expectedTitle: metadata.title,
163
+ executionId: execution.executionId
164
+ })
165
+ .catch(() => undefined);
143
166
  const title = await this.#readGeneratedTitle(input.homeDirectory, input.sessionId).catch(() => undefined);
144
167
  return {
145
168
  status: result.status === 'completed'
@@ -160,6 +183,8 @@ export class MarAgentHostedConversationRuntime {
160
183
  };
161
184
  }
162
185
  finally {
186
+ titleController.abort();
187
+ await titleTask;
163
188
  settled = true;
164
189
  this.#active.delete(input.sessionId);
165
190
  await agent.dispose().catch(() => undefined);
@@ -178,6 +203,7 @@ export class MarAgentHostedConversationRuntime {
178
203
  cancel: () => {
179
204
  if (settled || this.#active.get(input.sessionId)?.handle !== handle)
180
205
  return false;
206
+ titleController.abort();
181
207
  execution.cancel();
182
208
  return true;
183
209
  }
@@ -297,6 +323,7 @@ export class MarAgentHostedConversationRuntime {
297
323
  };
298
324
  return {
299
325
  agent: await this.#createAgent(options),
326
+ models,
300
327
  closeImages,
301
328
  ...(mcp === undefined ? {} : { mcp })
302
329
  };
@@ -4,7 +4,7 @@ import { homedir } from 'node:os';
4
4
  import { access, constants } from 'node:fs/promises';
5
5
  import { nodeExecutionEnvironment } from '../../node-execution-environment.js';
6
6
  import { BUNDLED_RIPGREP_VERSION, bundledRipgrepPath, prependBundledRipgrepToPath } from '../../ripgrep-binary.js';
7
- import { LocalAgentHost, createMarAgent } from '@myagentroam/agent';
7
+ import { LocalAgentHost, createMarAgent, generateSessionTitle } from '@myagentroam/agent';
8
8
  import { marAgentReasoningEffortSchema, marAgentRuntimeModelConfigurationSchema } from '@myagentroam/protocol';
9
9
  import { boundedConversationItemId } from '../codex/conversation-parser.js';
10
10
  import { parseSecretEnvironment } from '../../util/secret-environment.js';
@@ -29,6 +29,7 @@ export class MarAgentManagedRunController {
29
29
  #createAgent;
30
30
  #modelFactory;
31
31
  #computerUse;
32
+ #generateTitle;
32
33
  #disposePromise;
33
34
  #disposed = false;
34
35
  constructor(runner, host, options = {}) {
@@ -44,6 +45,7 @@ export class MarAgentManagedRunController {
44
45
  }
45
46
  });
46
47
  this.#computerUse = options.computerUse;
48
+ this.#generateTitle = options.generateTitle ?? generateSessionTitle;
47
49
  }
48
50
  dispose() {
49
51
  if (this.#disposePromise !== undefined)
@@ -244,6 +246,8 @@ export class MarAgentManagedRunController {
244
246
  let executionMcpBinding;
245
247
  let computerUseBinding;
246
248
  let executionModels = [];
249
+ const titleController = new AbortController();
250
+ let titleTask;
247
251
  try {
248
252
  const sessionKey = input.externalSessionId ?? input.sessionId;
249
253
  // Reserve both identities while opening so a concurrent release can await the same runtime.
@@ -364,6 +368,9 @@ export class MarAgentManagedRunController {
364
368
  ...(reasoningEffort === undefined ? {} : { reasoningEffort }),
365
369
  images: input.images
366
370
  };
371
+ const metadata = input.mode === 'compact' || !runtime.automaticTitleEnabled
372
+ ? undefined
373
+ : await runtime.agent.getSession(session.id).catch(() => undefined);
367
374
  const handle = input.mode === 'plan'
368
375
  ? session.plan({
369
376
  ...runInput
@@ -372,12 +379,36 @@ export class MarAgentManagedRunController {
372
379
  ? session.compact(runInput)
373
380
  : session.run(runInput);
374
381
  this.#executions.set(input.runId, {
375
- cancel: () => handle.cancel()
382
+ cancel: () => {
383
+ titleController.abort();
384
+ handle.cancel();
385
+ }
376
386
  });
377
387
  this.host.emit(input.runId, 'run.running', {}, 'RUNNING');
378
- for await (const event of handle)
388
+ for await (const event of handle) {
389
+ if (event.type === 'execution.started' && metadata?.automaticTitlePending && !titleTask) {
390
+ titleTask = this.#generateTitle({
391
+ models: sdkModelConfigurations(executionModels),
392
+ currentModelId: input.model.id,
393
+ prompt: input.input,
394
+ signal: titleController.signal
395
+ }).catch(() => undefined);
396
+ runtime.automaticTitleTask = { cancel: () => titleController.abort(), result: titleTask };
397
+ }
379
398
  this.project(input.runId, event);
399
+ }
380
400
  const result = await handle.result();
401
+ if (result.status === 'cancelled')
402
+ titleController.abort();
403
+ const title = await titleTask;
404
+ if (title && !titleController.signal.aborted)
405
+ await runtime.agent
406
+ .setSessionTitle(session.id, title, {
407
+ automatic: true,
408
+ expectedTitle: metadata.title,
409
+ executionId: handle.executionId
410
+ })
411
+ .catch(() => undefined);
381
412
  await this.host.syncTitle(session.id, input.cwd);
382
413
  const status = result.status === 'completed'
383
414
  ? 'SUCCEEDED'
@@ -412,6 +443,10 @@ export class MarAgentManagedRunController {
412
443
  this.host.emit(input.runId, 'run.failed', projected, 'FAILED');
413
444
  }
414
445
  finally {
446
+ titleController.abort();
447
+ await titleTask;
448
+ if (runtime !== undefined && runtime.automaticTitleTask?.result === titleTask)
449
+ delete runtime.automaticTitleTask;
415
450
  this.clearInteractions(input.runId);
416
451
  this.#normalizers.delete(input.runId);
417
452
  this.host.active.delete(input.runId);
@@ -554,6 +589,7 @@ export class MarAgentManagedRunController {
554
589
  normalizer: new MarAgentConversationNormalizer(),
555
590
  mcpLease,
556
591
  dynamicCredentials,
592
+ automaticTitleEnabled: input.externalSessionId === undefined,
557
593
  models,
558
594
  released: false
559
595
  };
@@ -608,6 +644,10 @@ export class MarAgentManagedRunController {
608
644
  delete runtime.idleTimer;
609
645
  }
610
646
  try {
647
+ if (runtime.automaticTitleTask !== undefined) {
648
+ runtime.automaticTitleTask.cancel();
649
+ await runtime.automaticTitleTask.result;
650
+ }
611
651
  try {
612
652
  await this.#computerUse?.releaseSession(runtime.session.id);
613
653
  }
@@ -76,9 +76,6 @@ export class MarAgentRuntimeModelFactory {
76
76
  ...(configuration.responsesTransport === undefined
77
77
  ? {}
78
78
  : { responsesTransport: configuration.responsesTransport }),
79
- ...(configuration.responsesEncoding === undefined
80
- ? {}
81
- : { responsesEncoding: configuration.responsesEncoding }),
82
79
  responsesToolNamespace: configuration.responsesToolNamespace,
83
80
  responsesPreviousResponseId: configuration.responsesPreviousResponseId,
84
81
  enabled: configuration.enabled
@@ -114,7 +111,6 @@ export class MarAgentRuntimeModelFactory {
114
111
  }
115
112
  }
116
113
  : {}),
117
- responsesEncoding: configuration.responsesEncoding ?? 'STANDARD',
118
114
  responsesConfigurationUpdates: configuration.responsesConfigurationUpdates === true,
119
115
  credentialProvider: provider
120
116
  };
@@ -152,6 +152,18 @@ export class ScheduledSessionStore {
152
152
  .prepare('SELECT * FROM scheduled_session_occurrences WHERE task_id=? ORDER BY due_at DESC LIMIT ?')
153
153
  .all(taskId, Math.max(1, Math.min(limit, 100)));
154
154
  }
155
+ recentPage(taskId, page = 1) {
156
+ if (!Number.isSafeInteger(page) || page < 1)
157
+ throw new Error('SCHEDULE_INVALID');
158
+ const total = this.database
159
+ .prepare('SELECT COUNT(*) AS count FROM scheduled_session_occurrences WHERE task_id=?')
160
+ .get(taskId).count;
161
+ const actualPage = Math.min(page, Math.max(1, Math.ceil(total / 10)));
162
+ const occurrences = this.database
163
+ .prepare('SELECT * FROM scheduled_session_occurrences WHERE task_id=? ORDER BY due_at DESC, id DESC LIMIT 10 OFFSET ?')
164
+ .all(taskId, (actualPage - 1) * 10);
165
+ return { occurrences, occurrenceTotal: total, occurrencePage: actualPage };
166
+ }
155
167
  pendingProposals(taskId) {
156
168
  return this.database
157
169
  .prepare(`SELECT * FROM scheduled_session_proposals
@@ -30,11 +30,16 @@ export class SessionScheduleService {
30
30
  const session = await this.requireSession(input);
31
31
  const store = this.store();
32
32
  const task = store.get(session.externalSessionId ?? session.id);
33
+ const page = input.occurrencePage === undefined ? 1 : input.occurrencePage;
34
+ if (typeof page !== 'number' || !Number.isSafeInteger(page) || page < 1)
35
+ throw new Error('SCHEDULE_INVALID');
33
36
  return {
34
37
  task: task ?? null,
35
38
  nodeTimezone: nodeTimezone(),
36
39
  canResolveProposals: task !== undefined && input.__requestUserId === task.ownerUserId,
37
- occurrences: task === undefined ? [] : store.recent(task.id),
40
+ ...(task === undefined
41
+ ? { occurrences: [], occurrenceTotal: 0, occurrencePage: 1 }
42
+ : store.recentPage(task.id, page)),
38
43
  proposals: task === undefined ? [] : store.pendingProposals(task.id)
39
44
  };
40
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.145",
3
+ "version": "0.9.147",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -36,8 +36,8 @@
36
36
  "zod": "4.4.3",
37
37
  "@myagentroam/marctl": "0.9.120",
38
38
  "@myagentroam/code-navigation": "0.9.129",
39
- "@myagentroam/protocol": "0.9.145",
40
- "@myagentroam/agent": "0.9.145"
39
+ "@myagentroam/agent": "0.9.147",
40
+ "@myagentroam/protocol": "0.9.147"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/ws": "^8.18.1"