@myagentroam/node 0.10.37 → 0.10.39

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.
@@ -20,6 +20,12 @@ import { MarAgentRuntimeModelFactory, sdkModelConfigurations, closeImageCredenti
20
20
  import { createImageGenerationExtensions } from './image-generation-extensions.js';
21
21
  import { createWebToolsExtensions } from './web-tools-extensions.js';
22
22
  import { combineExecutionExtensions } from './execution-extensions.js';
23
+ const HOST_CONFIGURATION_CATEGORIES = [
24
+ 'cwd',
25
+ 'mcpServers',
26
+ 'environment',
27
+ 'guaranteedCommands'
28
+ ];
23
29
  export class MarAgentManagedRunController {
24
30
  runner;
25
31
  host;
@@ -592,7 +598,7 @@ export class MarAgentManagedRunController {
592
598
  throw new Error('MAR_AGENT_SESSION_RUNTIME_ACTIVE');
593
599
  if (!existing.released &&
594
600
  existing.release === undefined &&
595
- existing.fingerprint === fingerprint) {
601
+ existing.fingerprint.value === fingerprint.value) {
596
602
  const configurations = compactRuntimeConfigurations(input.models, input.compact);
597
603
  if (existing.modelFingerprint !== modelFingerprint ||
598
604
  configurations.some((model) => model.credential?.type === 'RUNTIME_TICKET' ||
@@ -631,7 +637,7 @@ export class MarAgentManagedRunController {
631
637
  });
632
638
  return existing;
633
639
  }
634
- await this.#releaseRuntime(existing, 'host_configuration_changed');
640
+ await this.#releaseRuntime(existing, 'host_configuration_changed', HOST_CONFIGURATION_CATEGORIES.filter((category) => existing.fingerprint.categories[category] !== fingerprint.categories[category]));
635
641
  }
636
642
  const creation = this.#createRuntime(input, sessionKey, fingerprint, hostConfiguration);
637
643
  for (const key of creationKeys)
@@ -791,7 +797,7 @@ export class MarAgentManagedRunController {
791
797
  return;
792
798
  this.#scheduleRuntimeRelease(runtime);
793
799
  }
794
- #releaseRuntime(runtime, reason) {
800
+ #releaseRuntime(runtime, reason, changedCategories = []) {
795
801
  if (runtime.release !== undefined)
796
802
  return runtime.release;
797
803
  runtime.release = (async () => {
@@ -800,6 +806,7 @@ export class MarAgentManagedRunController {
800
806
  runtimeId: runtime.runtimeId,
801
807
  sessionId: runtime.session.id,
802
808
  reason,
809
+ ...(changedCategories.length === 0 ? {} : { changedCategories }),
803
810
  ...(runtime.activeRunId === undefined ? {} : { runId: runtime.activeRunId })
804
811
  });
805
812
  if (runtime.idleTimer !== undefined) {
@@ -977,16 +984,25 @@ function isExecutionScopedMcp(server) {
977
984
  (server.transport === 'STDIO' && server.environment?.['MAR_CTL_USER_TOKEN'] !== undefined));
978
985
  }
979
986
  function sessionHostFingerprint(input, hostConfiguration) {
980
- return createHash('sha256')
981
- .update(canonicalJson({
987
+ const configuration = {
982
988
  cwd: input.cwd,
983
989
  mcpServers: input.mcpServers.filter((server) => !isExecutionScopedMcp(server)),
984
990
  environment: Object.entries(hostConfiguration.environment)
985
991
  .filter((entry) => entry[1] !== undefined)
986
992
  .sort(([left], [right]) => left.localeCompare(right)),
987
993
  guaranteedCommands: Object.entries(hostConfiguration.guaranteedCommands).sort(([left], [right]) => left.localeCompare(right))
988
- }))
989
- .digest('hex');
994
+ };
995
+ const digest = (value) => createHash('sha256').update(canonicalJson(value)).digest('hex');
996
+ return {
997
+ value: digest(configuration),
998
+ // Only fixed category names leave this comparison; never log these value fingerprints.
999
+ categories: {
1000
+ cwd: digest(configuration.cwd),
1001
+ mcpServers: digest(configuration.mcpServers),
1002
+ environment: digest(configuration.environment),
1003
+ guaranteedCommands: digest(configuration.guaranteedCommands)
1004
+ }
1005
+ };
990
1006
  }
991
1007
  function sessionModelFingerprint(input) {
992
1008
  const compact = marAgentRuntimeCompactConfigurationSchema.safeParse(input.compact);
@@ -119,19 +119,12 @@ export class ContinuousRunCoordinator {
119
119
  active !== undefined &&
120
120
  active.id === input.activeRunId &&
121
121
  activeTurn?.id === input.steerTarget.turnId &&
122
- (activeTurn === undefined
123
- ? !this.isMessageRun(active)
124
- : this.options.runtime.currentExecutionId(active.id) !== undefined &&
125
- !['SUCCEEDED', 'FAILED', 'INTERRUPTED'].includes(activeTurn.status)) &&
122
+ this.canSteer(active) &&
126
123
  (input.steerTarget.executionId === undefined ||
127
124
  this.options.runtime.currentExecutionId(active.id) === input.steerTarget.executionId);
128
125
  const intent = input.steerTarget !== undefined && !steering ? 'SEND' : input.intent;
129
- if (steering && prepared?.prepareSteerInput !== undefined && !prepared.steering)
130
- this.preparedInputs.set(input.submissionId, {
131
- ...prepared,
132
- input: prepared.prepareSteerInput(),
133
- steering: true
134
- });
126
+ if (steering)
127
+ this.prepareSteering(input.submissionId);
135
128
  const executionEnded = active !== undefined &&
136
129
  this.options.runtime.currentExecutionId(active.id) === undefined &&
137
130
  activeTurn !== undefined &&
@@ -195,10 +188,12 @@ export class ContinuousRunCoordinator {
195
188
  }
196
189
  executeQueueItem(itemId) {
197
190
  const item = this.requirePreparedItem(itemId);
191
+ const active = this.options.runtime.activeSessionRun(item.sessionId);
192
+ if (active !== undefined && this.canSteer(active))
193
+ this.prepareSteering(item.id);
198
194
  this.immediateQueueItems.add(item.id);
199
195
  this.options.runtime.resumeSessionQueue(item.sessionId);
200
196
  this.options.runtime.prioritizeQueueItem(item.id);
201
- const active = this.options.runtime.activeSessionRun(item.sessionId);
202
197
  if (active !== undefined) {
203
198
  if (this.options.runtime.currentExecutionId(active.id) !== undefined)
204
199
  this.replace(item.id, active.id, false);
@@ -215,13 +210,28 @@ export class ContinuousRunCoordinator {
215
210
  throw new Error('RUN_NOT_FOUND');
216
211
  return { run: started, delivery: 'STARTED' };
217
212
  }
218
- updatePreparedInput(itemId, input) {
213
+ canSteer(active) {
214
+ const turn = this.options.runtime.conversationTurnForRun(active.id);
215
+ return turn === undefined
216
+ ? !this.isMessageRun(active)
217
+ : this.options.runtime.currentExecutionId(active.id) !== undefined &&
218
+ !['SUCCEEDED', 'FAILED', 'INTERRUPTED'].includes(turn.status);
219
+ }
220
+ prepareSteering(itemId) {
221
+ const prepared = this.preparedInputs.get(itemId);
222
+ if (prepared?.prepareSteerInput === undefined || prepared.steering)
223
+ return;
224
+ const input = prepareComposerResourceInput(prepared.prepareSteerInput(), prepared.cwd, prepared.workspacePath ?? prepared.cwd);
225
+ this.preparedInputs.set(itemId, { ...prepared, input, steering: true });
226
+ }
227
+ updatePreparedInput(itemId, input, prepareSteerInput) {
219
228
  const pending = this.preparedInputs.get(itemId);
220
229
  if (pending === undefined)
221
230
  return;
222
231
  const resources = prepareComposerResourceInput(input, pending.cwd, pending.workspacePath ?? pending.cwd);
223
232
  this.preparedInputs.set(itemId, {
224
233
  ...pending,
234
+ ...(prepareSteerInput === undefined ? {} : { prepareSteerInput }),
225
235
  ...(pending.restartMessage === undefined
226
236
  ? {}
227
237
  : {
@@ -270,9 +280,12 @@ export class ContinuousRunCoordinator {
270
280
  if (!this.startedExecutions.has(executionId)) {
271
281
  const current = this.preparedInputs.get(executionId);
272
282
  const replacement = this.preparedInputs.get(item.id);
273
- if (current !== undefined && replacement !== undefined)
283
+ if (current !== undefined &&
284
+ replacement !== undefined &&
285
+ replacement.precedingExecutionId !== executionId)
274
286
  this.preparedInputs.set(item.id, {
275
287
  ...replacement,
288
+ precedingExecutionId: executionId,
276
289
  precedingInput: current.input,
277
290
  input: `${current.input}\n\n${replacement.input}`
278
291
  });
@@ -161,17 +161,15 @@ export class SessionMessageService {
161
161
  if (internalSchedule && (active !== undefined || queueBeforeCreate.itemIds.length > 0))
162
162
  throw new Error('SCHEDULE_SESSION_BUSY');
163
163
  const replaceCurrentRun = intent === 'REPLACE_CURRENT' ? active : undefined;
164
- const userSteer = requestedIntent === 'REPLACE_CURRENT' &&
165
- goalMessage?.activated !== true &&
166
- !internalGoal &&
167
- !internalSchedule &&
168
- !internalRestore;
169
- const steerTarget = userSteer
164
+ const userMessage = goalMessage?.activated !== true && !internalGoal && !internalSchedule && !internalRestore;
165
+ const steerTarget = userMessage && requestedIntent === 'REPLACE_CURRENT'
170
166
  ? {
171
167
  turnId: active === undefined
172
168
  ? undefined
173
169
  : this.options.runtime.conversationTurnForRun(active.id)?.id,
174
- executionId: active === undefined ? undefined : this.options.runtime.currentExecutionId(active.id)
170
+ executionId: active === undefined
171
+ ? undefined
172
+ : this.options.runtime.currentExecutionId(active.id)
175
173
  }
176
174
  : undefined;
177
175
  const acceptedMessage = {
@@ -258,7 +256,7 @@ export class SessionMessageService {
258
256
  workspaceId: currentSession.workspaceId,
259
257
  runner: currentSession.runner,
260
258
  input: runner.prepareMessageInput(runnerInput, planActive ? 'plan' : 'default'),
261
- ...(userSteer
259
+ ...(userMessage
262
260
  ? {
263
261
  prepareSteerInput: () => runner.prepareMessageInput(steerComposerMessage(runnerInput), planActive ? 'plan' : 'default')
264
262
  }
@@ -120,12 +120,13 @@ export class WorkspaceQueueWorkbenchService {
120
120
  assertQueueItemOwner(item, input.__requestUserId);
121
121
  const content = input.content.trim();
122
122
  const prepared = this.options.coordinator.pending(item.id);
123
- const runnerInput = prepared === undefined
123
+ const prepareRunnerInput = (steering) => prepared === undefined
124
124
  ? undefined
125
- : this.options.prepareInput(prepared.runner, withNonImageAttachmentPrompt(prepared.steering ? steerComposerMessage(content) : content, prepared.attachmentPaths, prepared.attachmentFiles), prepared.collaborationMode);
125
+ : this.options.prepareInput(prepared.runner, withNonImageAttachmentPrompt(steering ? steerComposerMessage(content) : content, prepared.attachmentPaths, prepared.attachmentFiles), prepared.collaborationMode);
126
+ const runnerInput = prepareRunnerInput(prepared?.steering === true);
126
127
  this.options.runtime.updateQueueItem(item.id, content);
127
128
  if (runnerInput !== undefined)
128
- this.options.coordinator.updatePreparedInput(item.id, runnerInput);
129
+ this.options.coordinator.updatePreparedInput(item.id, runnerInput, prepared?.prepareSteerInput === undefined ? undefined : () => prepareRunnerInput(true));
129
130
  this.options.emit(item.workspaceId, item.sessionId);
130
131
  return {
131
132
  queue: this.present(item.workspaceId, item.sessionId),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.10.37",
3
+ "version": "0.10.39",
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/marctl": "0.10.7",
41
- "@myagentroam/agent": "0.10.37",
40
+ "@myagentroam/agent": "0.10.39",
42
41
  "@myagentroam/code-navigation": "0.10.1",
43
- "@myagentroam/protocol": "0.10.37"
42
+ "@myagentroam/marctl": "0.10.7",
43
+ "@myagentroam/protocol": "0.10.39"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/ws": "^8.18.1"