@myagentroam/agent 0.9.84 → 0.9.85

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.
@@ -80,6 +80,11 @@ const rawModelSchema = z
80
80
  });
81
81
  if (value.protocol !== 'OPENAI_RESPONSES' && value.responsesPreviousResponseId)
82
82
  context.addIssue({ code: 'custom', message: 'Responses continuation requires Responses.' });
83
+ if (value.responsesPreviousResponseId && value.responsesTransport?.transport !== 'WEBSOCKET')
84
+ context.addIssue({
85
+ code: 'custom',
86
+ message: 'Responses continuation requires WebSocket transport.'
87
+ });
83
88
  if (value.protocol !== 'OPENAI_RESPONSES' && value.responsesTransport !== undefined)
84
89
  context.addIssue({ code: 'custom', message: 'Responses transport requires Responses.' });
85
90
  if (value.protocol !== 'OPENAI_RESPONSES' && value.responsesEncoding !== undefined)
@@ -1,10 +1,9 @@
1
1
  import type { MarAgentModelConfiguration, MarAgentReasoningEffort, ModelCredentialAcquireReason } from './configuration.js';
2
2
  export interface ModelContinuationCheckpoint {
3
- readonly version: 1;
3
+ readonly version: 2;
4
4
  readonly protocol: 'OPENAI_RESPONSES';
5
5
  readonly configurationHash: string;
6
6
  readonly responseId: string;
7
- readonly requestPropertiesHash: string;
8
7
  readonly inputPrefixLength: number;
9
8
  readonly inputPrefixHash: string;
10
9
  }
@@ -45,8 +45,7 @@ declare class OpenAiResponsesTurnSession implements ModelTurnSession {
45
45
  private readonly adapter;
46
46
  private readonly state;
47
47
  private readonly cacheLease;
48
- private readonly allowExternalContinuation;
49
- constructor(adapter: OpenAiResponsesAdapter, state: ResponsesSessionState, cacheLease: boolean, allowExternalContinuation: boolean);
48
+ constructor(adapter: OpenAiResponsesAdapter, state: ResponsesSessionState, cacheLease: boolean);
50
49
  start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
51
50
  close(): Promise<void>;
52
51
  resetContinuation(): void;
@@ -35,14 +35,16 @@ export class OpenAiResponsesAdapter {
35
35
  if (this.#cacheLeased ||
36
36
  this.#cachedState.lastRequest !== undefined ||
37
37
  this.#cachedState.lastResponse !== undefined ||
38
+ !responsesContinuationEnabled(this.#configuration) ||
39
+ checkpoint.version !== 2 ||
38
40
  checkpoint.protocol !== 'OPENAI_RESPONSES' ||
39
- checkpoint.configurationHash !== responsesConfigurationHash(this.#configuration))
41
+ checkpoint.configurationHash !== responsesContinuationIdentityHash(this.#configuration))
40
42
  return false;
41
43
  this.#cachedState.restoredContinuation = checkpoint;
42
44
  return true;
43
45
  }
44
46
  async *start(request, signal) {
45
- const turn = new OpenAiResponsesTurnSession(this, {}, false, true);
47
+ const turn = new OpenAiResponsesTurnSession(this, {}, false);
46
48
  try {
47
49
  yield* turn.start(request, signal);
48
50
  }
@@ -59,7 +61,7 @@ export class OpenAiResponsesAdapter {
59
61
  this.#cachedState = {};
60
62
  this.#cacheLeased = true;
61
63
  }
62
- return new OpenAiResponsesTurnSession(this, state, cacheLease, false);
64
+ return new OpenAiResponsesTurnSession(this, state, cacheLease);
63
65
  }
64
66
  async close() {
65
67
  if (this.#closed)
@@ -546,17 +548,15 @@ class OpenAiResponsesTurnSession {
546
548
  adapter;
547
549
  state;
548
550
  cacheLease;
549
- allowExternalContinuation;
550
551
  #active = false;
551
552
  #closed = false;
552
553
  #turnState;
553
554
  #fullBody;
554
555
  #connectionDiagnostic;
555
- constructor(adapter, state, cacheLease, allowExternalContinuation) {
556
+ constructor(adapter, state, cacheLease) {
556
557
  this.adapter = adapter;
557
558
  this.state = state;
558
559
  this.cacheLease = cacheLease;
559
- this.allowExternalContinuation = allowExternalContinuation;
560
560
  }
561
561
  async *start(request, signal) {
562
562
  if (this.#closed)
@@ -573,7 +573,7 @@ class OpenAiResponsesTurnSession {
573
573
  this.#fullBody = prepared.fullBody;
574
574
  if (this.adapter.configuration.responsesEncoding === 'LITE' &&
575
575
  this.adapter.configuration.responsesTransport?.transport === 'WEBSOCKET' &&
576
- this.adapter.configuration.responsesPreviousResponseId &&
576
+ responsesContinuationEnabled(this.adapter.configuration) &&
577
577
  this.state.lastRequest === undefined &&
578
578
  request.allowTools !== false &&
579
579
  request.toolChoice !== 'none' &&
@@ -626,7 +626,7 @@ class OpenAiResponsesTurnSession {
626
626
  completed = event.reason === 'completed' || event.reason === 'tool_use';
627
627
  yield event;
628
628
  }
629
- if (this.adapter.configuration.responsesPreviousResponseId &&
629
+ if (responsesContinuationEnabled(this.adapter.configuration) &&
630
630
  completed &&
631
631
  responseId !== undefined &&
632
632
  (outputItems.length > 0 || !sawUnrepresentedOutput)) {
@@ -656,17 +656,18 @@ class OpenAiResponsesTurnSession {
656
656
  clearResponsesContinuation(this.state);
657
657
  }
658
658
  continuationCheckpoint() {
659
+ if (!responsesContinuationEnabled(this.adapter.configuration))
660
+ return undefined;
659
661
  const request = this.state.lastRequest;
660
662
  const response = this.state.lastResponse;
661
663
  if (request === undefined || response === undefined)
662
664
  return this.state.restoredContinuation;
663
665
  const baseline = [...request.input, ...response.outputItems];
664
666
  return {
665
- version: 1,
667
+ version: 2,
666
668
  protocol: 'OPENAI_RESPONSES',
667
- configurationHash: responsesConfigurationHash(this.adapter.configuration),
669
+ configurationHash: responsesContinuationIdentityHash(this.adapter.configuration),
668
670
  responseId: response.responseId,
669
- requestPropertiesHash: responsesRequestPropertiesHash(request),
670
671
  inputPrefixLength: baseline.length,
671
672
  inputPrefixHash: hashResponsesValue(baseline)
672
673
  };
@@ -677,7 +678,7 @@ class OpenAiResponsesTurnSession {
677
678
  this.state.socket.readyState !== WebSocket.OPEN)
678
679
  invalidateResponsesWebSocket(this.state, false);
679
680
  const fullBody = responsesRequestBody(this.adapter.configuration, request, request.messages, this.adapter.prefixIdentity);
680
- if (this.adapter.configuration.responsesPreviousResponseId) {
681
+ if (responsesContinuationEnabled(this.adapter.configuration)) {
681
682
  const incremental = incrementalResponsesInput(this.state, fullBody);
682
683
  if (incremental !== undefined) {
683
684
  const responseId = this.state.lastResponse.responseId;
@@ -692,20 +693,6 @@ class OpenAiResponsesTurnSession {
692
693
  continuation: true
693
694
  };
694
695
  }
695
- if (this.allowExternalContinuation &&
696
- request.continuation !== undefined &&
697
- this.adapter.configuration.responsesTransport?.transport !== 'WEBSOCKET') {
698
- const deltaBody = responsesRequestBody(this.adapter.configuration, request, request.continuation.deltaMessages);
699
- return {
700
- fullBody,
701
- body: {
702
- ...fullBody,
703
- input: deltaBody.input,
704
- previous_response_id: request.continuation.previousResponseId
705
- },
706
- continuation: true
707
- };
708
- }
709
696
  }
710
697
  return {
711
698
  fullBody,
@@ -899,7 +886,6 @@ function incrementalResponsesInput(state, current) {
899
886
  if (previous === undefined || completion === undefined) {
900
887
  const restored = state.restoredContinuation;
901
888
  if (restored === undefined ||
902
- restored.requestPropertiesHash !== responsesRequestPropertiesHash(current) ||
903
889
  current.input.length < restored.inputPrefixLength ||
904
890
  restored.inputPrefixHash !==
905
891
  hashResponsesValue(current.input.slice(0, restored.inputPrefixLength)))
@@ -907,8 +893,6 @@ function incrementalResponsesInput(state, current) {
907
893
  state.lastResponse = { responseId: restored.responseId, outputItems: [] };
908
894
  return current.input.slice(restored.inputPrefixLength);
909
895
  }
910
- if (!responsesRequestPropertiesMatch(previous, current))
911
- return undefined;
912
896
  const baseline = [...previous.input, ...completion.outputItems];
913
897
  if (current.input.length < baseline.length)
914
898
  return undefined;
@@ -917,37 +901,24 @@ function incrementalResponsesInput(state, current) {
917
901
  return undefined;
918
902
  return current.input.slice(baseline.length);
919
903
  }
920
- function responsesRequestPropertiesMatch(previous, current) {
921
- return RESPONSES_REQUEST_PROPERTY_KEYS.every((key) => isDeepStrictEqual(previous[key], current[key]));
922
- }
923
904
  function clearResponsesContinuation(state) {
924
905
  delete state.lastRequest;
925
906
  delete state.lastResponse;
926
907
  delete state.restoredContinuation;
927
908
  }
928
- const RESPONSES_REQUEST_PROPERTY_KEYS = [
929
- 'model',
930
- 'instructions',
931
- 'tools',
932
- 'tool_choice',
933
- 'parallel_tool_calls',
934
- 'reasoning',
935
- 'store',
936
- 'stream',
937
- 'include',
938
- 'prompt_cache_key',
939
- 'text',
940
- 'client_metadata'
941
- ];
942
- function responsesRequestPropertiesHash(request) {
943
- return hashResponsesValue(Object.fromEntries(RESPONSES_REQUEST_PROPERTY_KEYS.map((key) => [key, request[key]])));
944
- }
945
909
  function hashResponsesValue(value) {
946
910
  return createHash('sha256').update(JSON.stringify(value)).digest('hex');
947
911
  }
948
- function responsesConfigurationHash(configuration) {
949
- const { apiKey: _apiKey, credentialProvider: _credentialProvider, imageGeneration: _imageGeneration, ...identity } = configuration;
950
- return hashResponsesValue(identity);
912
+ function responsesContinuationIdentityHash(configuration) {
913
+ return hashResponsesValue({
914
+ endpoint: new URL(responsesEndpoint(configuration.baseUrl)).toString(),
915
+ modelId: configuration.modelId,
916
+ responsesEncoding: configuration.responsesEncoding ?? 'STANDARD'
917
+ });
918
+ }
919
+ function responsesContinuationEnabled(configuration) {
920
+ return (configuration.responsesPreviousResponseId === true &&
921
+ configuration.responsesTransport?.transport === 'WEBSOCKET');
951
922
  }
952
923
  function responsesCredentialFingerprint(credential) {
953
924
  return hashResponsesValue({
package/dist/sdk/agent.js CHANGED
@@ -1600,11 +1600,10 @@ function isModelContinuationCheckpoint(value) {
1600
1600
  if (value === null || typeof value !== 'object' || Array.isArray(value))
1601
1601
  return false;
1602
1602
  const checkpoint = value;
1603
- return (checkpoint.version === 1 &&
1603
+ return (checkpoint.version === 2 &&
1604
1604
  checkpoint.protocol === 'OPENAI_RESPONSES' &&
1605
1605
  isBoundedString(checkpoint.configurationHash, 64) &&
1606
1606
  isBoundedString(checkpoint.responseId, 1_024) &&
1607
- isBoundedString(checkpoint.requestPropertiesHash, 64) &&
1608
1607
  Number.isSafeInteger(checkpoint.inputPrefixLength) &&
1609
1608
  checkpoint.inputPrefixLength >= 0 &&
1610
1609
  isBoundedString(checkpoint.inputPrefixHash, 64));
@@ -33,7 +33,10 @@ function parseAgentMessageInput(value) {
33
33
  return invalid();
34
34
  const input = value;
35
35
  if (Object.keys(input).some((key) => !['agentId', 'message', 'delivery'].includes(key)) ||
36
- (input.delivery !== undefined && input.delivery !== 'append' && input.delivery !== 'replace') ||
36
+ (input.delivery !== undefined &&
37
+ input.delivery !== null &&
38
+ input.delivery !== 'append' &&
39
+ input.delivery !== 'replace') ||
37
40
  typeof input.agentId !== 'string' ||
38
41
  input.agentId.trim().length === 0 ||
39
42
  typeof input.message !== 'string' ||
@@ -43,7 +46,9 @@ function parseAgentMessageInput(value) {
43
46
  return {
44
47
  agentId: input.agentId.trim(),
45
48
  message: input.message,
46
- ...(input.delivery === undefined ? {} : { delivery: input.delivery })
49
+ ...(input.delivery === 'append' || input.delivery === 'replace'
50
+ ? { delivery: input.delivery }
51
+ : {})
47
52
  };
48
53
  }
49
54
  function invalid() {
@@ -62,6 +62,7 @@ function parseAgentStartInput(value) {
62
62
  input.modelId !== null &&
63
63
  (typeof input.modelId !== 'string' || input.modelId.trim().length === 0)) ||
64
64
  (input.reasoningEffort !== undefined &&
65
+ input.reasoningEffort !== null &&
65
66
  (typeof input.reasoningEffort !== 'string' ||
66
67
  !marAgentReasoningEffortSchema.safeParse(input.reasoningEffort.trim()).success)) ||
67
68
  (input.background !== undefined &&
@@ -46,11 +46,14 @@ function parseAgentWaitInput(value) {
46
46
  const input = value;
47
47
  if (Object.keys(input).some((key) => !['agentId', 'waitFor', 'waitMs'].includes(key)) ||
48
48
  (input.agentId !== undefined &&
49
+ input.agentId !== null &&
49
50
  (typeof input.agentId !== 'string' || input.agentId.trim().length === 0)) ||
50
51
  (input.waitFor !== undefined &&
52
+ input.waitFor !== null &&
51
53
  input.waitFor !== 'completion' &&
52
54
  input.waitFor !== 'message') ||
53
55
  (input.waitMs !== undefined &&
56
+ input.waitMs !== null &&
54
57
  (!Number.isInteger(input.waitMs) ||
55
58
  input.waitMs < TOOL_EXECUTION_LIMITS.agentWaitMinWaitMs ||
56
59
  input.waitMs > TOOL_EXECUTION_LIMITS.agentWaitMaxWaitMs)))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.9.84",
3
+ "version": "0.9.85",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -42,6 +42,7 @@
42
42
  "build": "node ../scripts/clean-build-output.mjs dist && tsc -p tsconfig.json",
43
43
  "typecheck": "tsc -p tsconfig.json --noEmit",
44
44
  "test:unit": "vitest run --config vitest.config.ts test/unit",
45
+ "analyze:session": "node test/tools/session-analyzer.mjs",
45
46
  "benchmark:compaction": "node test/e2e/compaction-benchmark.mjs",
46
47
  "test:performance": "vitest run --config vitest.performance.config.ts",
47
48
  "test:memory": "node --expose-gc ./node_modules/vitest/vitest.mjs run --config vitest.memory.config.ts",