maka-agent 0.2.0-dev.12.20260901 → 0.2.0-dev.14.20260902

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 (105) hide show
  1. package/THIRD_PARTY_NOTICES.txt +2 -2
  2. package/dist/acp/maka-acp-agent.js +6 -3
  3. package/dist/acp/session-registry.js +307 -0
  4. package/dist/acp/stdio-server.js +20 -1
  5. package/dist/cli-core.js +15 -2
  6. package/dist/pi-transcript.js +1 -1
  7. package/dist/pi-tui-layout.js +1 -1
  8. package/dist/pi-tui-mcp-status.js +1 -1
  9. package/dist/pi-tui-runner.js +1 -1
  10. package/dist/runtime-host-access-command.js +37 -4
  11. package/dist/runtime-host-capability-provider-command.js +16 -2
  12. package/dist/runtime-host-cli-context.js +73 -3
  13. package/dist/runtime-host-cli.js +37 -6
  14. package/dist/runtime-host-run-command.js +3 -1
  15. package/dist/runtime-host-update-command.js +59 -31
  16. package/dist/tui-copy-catalog.js +4 -2
  17. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  18. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  19. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  20. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  21. package/node_modules/@maka/core/dist/attachments.js +12 -0
  22. package/node_modules/@maka/core/dist/connection-error-copy.js +17 -34
  23. package/node_modules/@maka/core/dist/events.js +0 -13
  24. package/node_modules/@maka/core/dist/maka-wordmark.js +23 -0
  25. package/node_modules/@maka/core/dist/model-catalog.js +22 -1
  26. package/node_modules/@maka/core/dist/runtime-event.js +1 -0
  27. package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +50 -1
  28. package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +9 -1
  29. package/node_modules/@maka/core/dist/runtime-policy/model-catalog-entry-codec.js +10 -1
  30. package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +75 -0
  31. package/node_modules/@maka/core/dist/runtime-policy.js +4 -2
  32. package/node_modules/@maka/core/dist/session.js +1 -0
  33. package/node_modules/@maka/core/dist/settings.js +10 -2
  34. package/node_modules/@maka/core/dist/usage-record-schema.js +11 -1
  35. package/node_modules/@maka/core/package.json +2 -1
  36. package/node_modules/@maka/mcp/dist/index.js +1 -0
  37. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +52 -80
  38. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +178 -140
  39. package/node_modules/@maka/runtime/dist/archive-read-tool.js +26 -5
  40. package/node_modules/@maka/runtime/dist/builtin-tools.js +27 -2
  41. package/node_modules/@maka/runtime/dist/context-budget-policy.js +0 -12
  42. package/node_modules/@maka/runtime/dist/conversation-copy.js +66 -43
  43. package/node_modules/@maka/runtime/dist/durable-tool-result-projection.js +31 -0
  44. package/node_modules/@maka/runtime/dist/history-compact-error.js +10 -2
  45. package/node_modules/@maka/runtime/dist/history-compact-input-fit.js +7 -1
  46. package/node_modules/@maka/runtime/dist/history-compaction.js +13 -22
  47. package/node_modules/@maka/runtime/dist/model-history.js +41 -16
  48. package/node_modules/@maka/runtime/dist/provider-image-overflow-recovery.js +12 -31
  49. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +3 -0
  50. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +13 -2
  51. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -6
  52. package/node_modules/@maka/runtime/dist/shell-tools.js +77 -1
  53. package/node_modules/@maka/runtime/dist/tool-availability.js +74 -5
  54. package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +333 -52
  55. package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +34 -3
  56. package/node_modules/@maka/runtime/dist/tool-runtime.js +12 -0
  57. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +0 -3
  58. package/node_modules/@maka/runtime-host/dist/client/catalog-reader.js +40 -13
  59. package/node_modules/@maka/runtime-host/dist/client/connection.js +50 -31
  60. package/node_modules/@maka/runtime-host/dist/client/host-profile.js +48 -4
  61. package/node_modules/@maka/runtime-host/dist/client/index.js +2 -2
  62. package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +32 -1
  63. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +22 -4
  64. package/node_modules/@maka/runtime-host/dist/operator/access-management-frame.js +10 -1
  65. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +56 -12
  66. package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +3 -0
  67. package/node_modules/@maka/runtime-host/dist/protocol/configuration.js +55 -9
  68. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +37 -0
  69. package/node_modules/@maka/runtime-host/dist/protocol/index.js +17 -1
  70. package/node_modules/@maka/runtime-host/dist/protocol/network-proxy.js +1 -7
  71. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +2 -0
  72. package/node_modules/@maka/runtime-host/dist/protocol/runtime-policy.js +48 -1
  73. package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +4 -2
  74. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +2 -12
  75. package/node_modules/@maka/runtime-host/dist/server/access-authority.js +2 -1
  76. package/node_modules/@maka/runtime-host/dist/server/agent-graph-execution-coordinator.js +1 -2
  77. package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +0 -9
  78. package/node_modules/@maka/runtime-host/dist/server/configuration-coordinator.js +20 -1
  79. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +46 -24
  80. package/node_modules/@maka/runtime-host/dist/server/execution-service.js +4 -1
  81. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +15 -0
  82. package/node_modules/@maka/runtime-host/dist/server/listener-set.js +8 -4
  83. package/node_modules/@maka/runtime-host/dist/server/network-proxy-coordinator.js +0 -1
  84. package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +3 -0
  85. package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +28 -1
  86. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +3 -3
  87. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +35 -3
  88. package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +16 -0
  89. package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +189 -6
  90. package/node_modules/@maka/storage/dist/runtime-policy-stores.js +4 -1
  91. package/node_modules/@maka/storage/dist/settings-store.js +16 -25
  92. package/node_modules/fast-uri/index.js +215 -47
  93. package/node_modules/fast-uri/lib/schemes.js +9 -4
  94. package/node_modules/fast-uri/lib/utils.js +396 -92
  95. package/node_modules/fast-uri/package.json +1 -1
  96. package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
  97. package/node_modules/qs/CHANGELOG.md +15 -0
  98. package/node_modules/qs/README.md +19 -1
  99. package/node_modules/qs/dist/qs.js +21 -21
  100. package/node_modules/qs/eslint.config.mjs +1 -1
  101. package/node_modules/qs/lib/parse.js +3 -4
  102. package/node_modules/qs/lib/stringify.js +23 -8
  103. package/node_modules/qs/lib/utils.js +10 -3
  104. package/node_modules/qs/package.json +4 -4
  105. package/package.json +1 -1
@@ -22,7 +22,8 @@ export const RUNTIME_HOST_ACCESS_MANAGEMENT_ERROR_CODE_MAX_BYTES = 128;
22
22
  export const RUNTIME_HOST_ACCESS_MANAGEMENT_ERROR_MESSAGE_MAX_BYTES = 2 * 1024;
23
23
  const FRAME_MAX_BYTES = 768 * 1024;
24
24
  const CREDENTIAL_MAX_BYTES = 8 * 1024;
25
- const ACCESS_ACTIONS = ['list', 'prepare', 'revoke'];
25
+ const CONNECTION_CODE_MAX_BYTES = 64 * 1024;
26
+ const ACCESS_ACTIONS = ['list', 'prepare', 'revoke', 'connection-code'];
26
27
  const boundedString = (maxBytes) => z
27
28
  .string()
28
29
  .min(1)
@@ -69,6 +70,14 @@ const ACCESS_MANAGEMENT_FRAME_SCHEMA = z.union([
69
70
  credentials: z.array(CREDENTIAL_METADATA_SCHEMA),
70
71
  })
71
72
  .strict(),
73
+ z
74
+ .object({
75
+ schemaVersion: z.literal(1),
76
+ kind: z.literal('result'),
77
+ action: z.literal('connection-code'),
78
+ connectionCode: boundedString(CONNECTION_CODE_MAX_BYTES),
79
+ })
80
+ .strict(),
72
81
  z
73
82
  .object({
74
83
  schemaVersion: z.literal(1),
@@ -33,6 +33,7 @@ const ROUTE_PAGE_SIZE = 8;
33
33
  const RECONCILE_CONCURRENCY = 4;
34
34
  const RECONCILE_DEADLINE_MS = 60 * 1_000;
35
35
  const RECONCILE_INTERVAL_MS = 30 * 1_000;
36
+ const LOCAL_ROUTE_OBSERVATION_INTERVAL_MS = 1_000;
36
37
  export async function openPeerMeshNode(input) {
37
38
  const store = await openPeerMeshStateStore(input.dataRoot, input.peer.identity().peerId);
38
39
  const node = new PeerMeshNodeImpl({ ...input, store });
@@ -50,6 +51,7 @@ class PeerMeshNodeImpl {
50
51
  #peer;
51
52
  #endpointKind;
52
53
  #now;
54
+ #onBackgroundReconcileError;
53
55
  #activeControlStreams = new Set();
54
56
  #lifetime = new AbortController();
55
57
  #admissionTail = Promise.resolve();
@@ -64,6 +66,7 @@ class PeerMeshNodeImpl {
64
66
  this.#peer = input.peer;
65
67
  this.#endpointKind = input.endpointKind;
66
68
  this.#now = input.now ?? Date.now;
69
+ this.#onBackgroundReconcileError = input.onBackgroundReconcileError;
67
70
  }
68
71
  async initialize() {
69
72
  for (const route of this.#store.read().routes)
@@ -559,9 +562,11 @@ class PeerMeshNodeImpl {
559
562
  const visible = stored.meshes.some((mesh) => isActiveMembership(mesh, localPeerId) && mesh.roster.roster.members.includes(peerId));
560
563
  if (!visible)
561
564
  return;
562
- if (stored.routes.some(({ route }) => route.peerId === peerId && route.expiresAt > this.#now())) {
563
- return;
564
- }
565
+ // A signed route can remain within its TTL after a peer restarted or
566
+ // rotated Relay reservations. Every connection establishment therefore
567
+ // asks the Mesh control plane for its newest record. Callers with a
568
+ // self-contained invitation run this reconciliation in parallel with the
569
+ // first dial; callers without usable routes wait for it.
565
570
  await this.reconcile(signal);
566
571
  }
567
572
  reconcile(signal) {
@@ -621,11 +626,47 @@ class PeerMeshNodeImpl {
621
626
  return this.#store.close();
622
627
  }
623
628
  async #runReconciliation(signal) {
629
+ let failureReported = false;
624
630
  while (!signal.aborted) {
625
- await this.reconcile(signal).catch(() => undefined);
626
- await delay(RECONCILE_INTERVAL_MS, undefined, { signal }).catch(() => undefined);
631
+ try {
632
+ await this.reconcile(signal);
633
+ failureReported = false;
634
+ }
635
+ catch (error) {
636
+ if (!signal.aborted && !failureReported) {
637
+ failureReported = true;
638
+ try {
639
+ this.#onBackgroundReconcileError?.(error);
640
+ }
641
+ catch {
642
+ // Diagnostics cannot control Peer Mesh reconciliation.
643
+ }
644
+ }
645
+ }
646
+ await this.#waitForReconciliationTrigger(signal).catch(() => undefined);
627
647
  }
628
648
  }
649
+ async #waitForReconciliationTrigger(signal) {
650
+ let remainingMs = RECONCILE_INTERVAL_MS;
651
+ while (!signal.aborted && remainingMs > 0) {
652
+ const waitMs = Math.min(LOCAL_ROUTE_OBSERVATION_INTERVAL_MS, remainingMs);
653
+ await delay(waitMs, undefined, { signal });
654
+ if (this.#localRouteRequiresRefresh())
655
+ return;
656
+ remainingMs -= waitMs;
657
+ }
658
+ }
659
+ #localRouteRequiresRefresh() {
660
+ const identity = this.#peer.identity();
661
+ const current = this.#store.read();
662
+ if (!current.meshes.some((state) => isActiveMembership(state, identity.peerId))) {
663
+ return false;
664
+ }
665
+ const existing = current.routes
666
+ .filter(({ route }) => route.peerId === identity.peerId)
667
+ .sort((left, right) => right.route.sequence - left.route.sequence)[0];
668
+ return !isCurrentLocalRoute(existing, identity, current, this.#endpointKind, this.#now());
669
+ }
629
670
  async #reconcile(signal) {
630
671
  const lifetimeSignal = signal
631
672
  ? AbortSignal.any([signal, this.#lifetime.signal])
@@ -811,13 +852,7 @@ class PeerMeshNodeImpl {
811
852
  const existing = current.routes
812
853
  .filter(({ route }) => route.peerId === identity.peerId)
813
854
  .sort((left, right) => right.route.sequence - left.route.sequence)[0];
814
- if (existing &&
815
- existing.route.expiresAt > now + ROUTE_REFRESH_LEAD_MS &&
816
- sameAddresses(existing.route.routeHints, identity.listenAddresses) &&
817
- sameAddresses(existing.route.coordinationRelays, identity.coordinationRelays) &&
818
- existing.route.endpointKind === this.#endpointKind &&
819
- existing.route.displayName === (current.displayName ?? undefined) &&
820
- existing.route.transitMeshId === current.transitMeshId) {
855
+ if (isCurrentLocalRoute(existing, identity, current, this.#endpointKind, now)) {
821
856
  return { state: current, result: existing };
822
857
  }
823
858
  const route = await this.#signLocalRoute(current);
@@ -1519,6 +1554,15 @@ function responseRoutes(state, routes, knownRoutes, now) {
1519
1554
  function sameAddresses(left, right) {
1520
1555
  return left.length === right.length && left.every((address, index) => address === right[index]);
1521
1556
  }
1557
+ function isCurrentLocalRoute(existing, identity, current, endpointKind, now) {
1558
+ return Boolean(existing &&
1559
+ existing.route.expiresAt > now + ROUTE_REFRESH_LEAD_MS &&
1560
+ sameAddresses(existing.route.routeHints, identity.listenAddresses) &&
1561
+ sameAddresses(existing.route.coordinationRelays, identity.coordinationRelays) &&
1562
+ existing.route.endpointKind === endpointKind &&
1563
+ existing.route.displayName === (current.displayName ?? undefined) &&
1564
+ existing.route.transitMeshId === current.transitMeshId);
1565
+ }
1522
1566
  function mergeAddresses(primary, fallback) {
1523
1567
  return Object.freeze([...new Set([...primary, ...fallback])].slice(0, 32));
1524
1568
  }
@@ -57,6 +57,9 @@ export async function openRuntimeHostPeerMeshOwner(input) {
57
57
  dataRoot: join(input.dataRoot, client.identity().peerId),
58
58
  peer: client,
59
59
  endpointKind: input.endpointKind,
60
+ ...(input.onBackgroundReconcileError
61
+ ? { onBackgroundReconcileError: input.onBackgroundReconcileError }
62
+ : {}),
60
63
  });
61
64
  resolverMesh = mesh;
62
65
  }
@@ -16,7 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- import { decodeCredentialLocator, REQUEST_HEADERS_MAX_BYTES, } from '@maka/core/runtime-policy';
19
+ import { decodeCredentialLocator, decodeConnectionCredentialTarget, decodeConnectionVersionBasis, normalizeNetworkProxyCredentialTarget, REQUEST_HEADERS_MAX_BYTES, } from '@maka/core/runtime-policy';
20
20
  import { requireEncodedByteLimit, requireExactRecord, requireShapedRecord, requireUtf8String, } from './codec.js';
21
21
  import { invalidProtocolFrame } from './errors.js';
22
22
  import { defineOperation } from './operation-spec.js';
@@ -39,17 +39,41 @@ export const CONFIGURATION_OPERATION_SPECS = {
39
39
  }),
40
40
  };
41
41
  function decodeConfigurationCredentialExportInput(value) {
42
- const input = requireExactRecord(value, 'configuration credential export input', ['locator']);
43
- return { locator: decodeLocator(input.locator) };
42
+ const input = requireShapedRecord(value, 'configuration credential export input', ['locator'], ['expectedConnection']);
43
+ const locator = decodeLocator(input.locator);
44
+ const expectedConnection = input.expectedConnection === undefined
45
+ ? undefined
46
+ : decodeDomainValue(() => decodeConnectionCredentialTarget(input.expectedConnection));
47
+ if (expectedConnection && locator.scope !== 'connection') {
48
+ throw invalidProtocolFrame('Only connection credential exports accept a target basis');
49
+ }
50
+ return {
51
+ locator,
52
+ ...(expectedConnection === undefined ? {} : { expectedConnection }),
53
+ };
44
54
  }
45
55
  function decodeConfigurationCredentialExportResult(value) {
46
- const result = requireExactRecord(value, 'configuration credential export result', [
47
- 'credential',
48
- ]);
49
- if (result.credential === null)
50
- return { credential: null };
51
- const entry = requireShapedRecord(result.credential, 'exported configuration credential', ['locator', 'secretBase64'], []);
56
+ const result = requireShapedRecord(value, 'configuration credential export result', ['credential'], ['connectionStale']);
57
+ const connectionStale = result.connectionStale === undefined
58
+ ? undefined
59
+ : decodeConnectionStale(result.connectionStale);
60
+ if (connectionStale && result.credential !== null) {
61
+ throw invalidProtocolFrame('A stale connection export must not include credential material');
62
+ }
63
+ if (result.credential === null) {
64
+ return {
65
+ credential: null,
66
+ ...(connectionStale === undefined ? {} : { connectionStale }),
67
+ };
68
+ }
69
+ const entry = requireShapedRecord(result.credential, 'exported configuration credential', ['locator', 'secretBase64'], ['proxyTarget']);
52
70
  const locator = decodeLocator(entry.locator);
71
+ const proxyTarget = entry.proxyTarget === undefined
72
+ ? undefined
73
+ : decodeDomainValue(() => normalizeNetworkProxyCredentialTarget(entry.proxyTarget));
74
+ if (proxyTarget && locator.scope !== 'network_proxy') {
75
+ throw invalidProtocolFrame('Only proxy credentials may carry a proxy target');
76
+ }
53
77
  const maxBytes = locator.scope === 'connection' && locator.kind === 'request_headers'
54
78
  ? REQUEST_HEADERS_MAX_BYTES
55
79
  : CREDENTIAL_SECRET_MAX_BYTES;
@@ -57,11 +81,33 @@ function decodeConfigurationCredentialExportResult(value) {
57
81
  credential: {
58
82
  locator,
59
83
  secretBase64: decodeCredentialSecretBase64(entry.secretBase64, maxBytes),
84
+ ...(proxyTarget === undefined ? {} : { proxyTarget }),
60
85
  },
86
+ ...(connectionStale === undefined ? {} : { connectionStale }),
61
87
  };
62
88
  requireEncodedByteLimit(decoded, 'configuration credential export result', RESULT_MAX_BYTES);
63
89
  return decoded;
64
90
  }
91
+ function decodeConnectionStale(value) {
92
+ const stale = requireExactRecord(value, 'configuration credential stale connection', [
93
+ 'expected',
94
+ 'actual',
95
+ ]);
96
+ return {
97
+ expected: decodeDomainValue(() => decodeConnectionVersionBasis(stale.expected)),
98
+ actual: stale.actual === null
99
+ ? null
100
+ : decodeDomainValue(() => decodeConnectionVersionBasis(stale.actual)),
101
+ };
102
+ }
103
+ function decodeDomainValue(decode) {
104
+ try {
105
+ return decode();
106
+ }
107
+ catch {
108
+ throw invalidProtocolFrame('Invalid configuration credential connection basis');
109
+ }
110
+ }
65
111
  function decodeCredentialSecretBase64(value, maxBytes) {
66
112
  const encoded = requireUtf8String(value, 'exported configuration credential secret', Math.ceil(maxBytes / 3) * 4);
67
113
  const decoded = Buffer.from(encoded, 'base64');
@@ -19,9 +19,17 @@
19
19
  import { invalidProtocolFrame } from './errors.js';
20
20
  import { requireCount, requireEncodedByteLimit, requireExactRecord, requireId, requireRecord, requireString, requireUtf8String, } from './codec.js';
21
21
  import { defineOperation } from './operation-spec.js';
22
+ export function isHostActivityIdle(activity) {
23
+ return (activity.connections === 0 &&
24
+ activity.activeOperations === 0 &&
25
+ activity.residencies.length === 0);
26
+ }
22
27
  export const HOST_DIAGNOSTICS_RESULT_MAX_BYTES = 72 * 1024;
23
28
  export const HOST_DIAGNOSTIC_LOG_MAX_ENTRIES = 256;
24
29
  export const HOST_DIAGNOSTIC_LOG_MAX_ENTRY_BYTES = 10 * 1024;
30
+ const HOST_PEER_ID_MAX_BYTES = 160;
31
+ const HOST_PEER_ADDRESS_MAX_BYTES = 2 * 1024;
32
+ const HOST_PEER_ROUTE_MAX = 16;
25
33
  export const HOST_BOOTSTRAP_OPERATION_SPECS = {
26
34
  'host.status': defineOperation({
27
35
  mode: 'query',
@@ -45,11 +53,22 @@ export const HOST_BOOTSTRAP_OPERATION_SPECS = {
45
53
  decodeOutput: decodeHostUpgradePrepareResult,
46
54
  }),
47
55
  };
56
+ function decodePeerAddresses(value, label) {
57
+ if (!Array.isArray(value) || value.length > HOST_PEER_ROUTE_MAX) {
58
+ throw invalidProtocolFrame(`Invalid ${label}`);
59
+ }
60
+ const addresses = value.map((address) => requireString(address, label, HOST_PEER_ADDRESS_MAX_BYTES));
61
+ if (new Set(addresses).size !== addresses.length) {
62
+ throw invalidProtocolFrame(`Duplicate ${label}`);
63
+ }
64
+ return Object.freeze(addresses);
65
+ }
48
66
  function decodeEmptyHostInput(value, label) {
49
67
  requireExactRecord(value, label, []);
50
68
  return {};
51
69
  }
52
70
  function decodeHostStatusResult(value) {
71
+ const valueRecord = requireRecord(value, 'host.status result');
53
72
  const record = requireExactRecord(value, 'host.status result', [
54
73
  'hostEpoch',
55
74
  'compositionId',
@@ -58,11 +77,13 @@ function decodeHostStatusResult(value) {
58
77
  'connections',
59
78
  'activeOperations',
60
79
  'activeResidencies',
80
+ ...(valueRecord.peerEndpoint === undefined ? [] : ['peerEndpoint']),
61
81
  ]);
62
82
  return decodeHostStatusFields(record);
63
83
  }
64
84
  function decodeHostDiagnosticsResult(value) {
65
85
  requireEncodedByteLimit(value, 'host.diagnostics.query result', HOST_DIAGNOSTICS_RESULT_MAX_BYTES);
86
+ const valueRecord = requireRecord(value, 'host.diagnostics.query result');
66
87
  const record = requireExactRecord(value, 'host.diagnostics.query result', [
67
88
  'hostEpoch',
68
89
  'compositionId',
@@ -71,6 +92,7 @@ function decodeHostDiagnosticsResult(value) {
71
92
  'connections',
72
93
  'activeOperations',
73
94
  'activeResidencies',
95
+ ...(valueRecord.peerEndpoint === undefined ? [] : ['peerEndpoint']),
74
96
  'compositionModules',
75
97
  'residencies',
76
98
  'protocolVersion',
@@ -177,6 +199,21 @@ function decodeHostStatusFields(record) {
177
199
  connections: requireCount(record.connections, 'connections'),
178
200
  activeOperations: requireCount(record.activeOperations, 'activeOperations'),
179
201
  activeResidencies: requireCount(record.activeResidencies, 'activeResidencies'),
202
+ ...(record.peerEndpoint === undefined
203
+ ? {}
204
+ : { peerEndpoint: decodeHostPeerEndpoint(record.peerEndpoint) }),
205
+ };
206
+ }
207
+ function decodeHostPeerEndpoint(value) {
208
+ const record = requireExactRecord(value, 'Runtime Host peer endpoint', [
209
+ 'peerId',
210
+ 'routeHints',
211
+ 'coordinationRelays',
212
+ ]);
213
+ return {
214
+ peerId: requireString(record.peerId, 'Runtime Host peer id', HOST_PEER_ID_MAX_BYTES),
215
+ routeHints: decodePeerAddresses(record.routeHints, 'Runtime Host peer route hints'),
216
+ coordinationRelays: decodePeerAddresses(record.coordinationRelays, 'Runtime Host peer coordination relays'),
180
217
  };
181
218
  }
182
219
  function requirePlatform(value) {
@@ -61,7 +61,23 @@ export const RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION = 1;
61
61
  export const RUNTIME_HOST_PROTOCOL_VERSION = 0;
62
62
  // Increment when the same protocol version no longer guarantees safe Client-Host
63
63
  // interoperability. Mismatches are rejected before domain commands are admitted.
64
- export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 90;
64
+ export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 96;
65
+ // 96: Read image tool results may carry durable `session_context` refs.
66
+ // 95: Catalog entries carry `describedByMetadata`, so a client asks the
67
+ // Host-resolved entry — not its own bundled table — whether a model needs a
68
+ // hand-written capability declaration. The field is required, so a newer Host's
69
+ // entry fails an older client's strict decoder, and an older Host's entry
70
+ // (lacking it) fails a newer client's.
71
+ // 94: A failed Turn snapshot no longer carries contextBudgetExhaustedDetail; the
72
+ // retired outcome reads as context_overflow at the ledger boundary, and an older
73
+ // Host still sending the field fails a newer client's closed snapshot decode.
74
+ // 93: Configuration credential transfer binds proxy destinations and
75
+ // Connection credentials to exact Host-owned targets before secret access.
76
+ // Proxy policy and credentials commit through one recoverable Host command;
77
+ // older peers can split the writes and violate the shared credential basis.
78
+ // 92: Owners can query their complete pending Session Turn-request inbox.
79
+ // 91: Host status publishes the live Direct peer endpoint so newly issued
80
+ // connection invitations do not preserve stale startup routes.
65
81
  // 90: `session.create.mode` accepts the Bot session mode. A Host that predates
66
82
  // it rejects the value as an invalid Session start mode.
67
83
  // 89: The Host refreshes its models.dev catalog at startup and announces the
@@ -19,7 +19,6 @@
19
19
  import { requireEncodedByteLimit, requireExactRecord, requireShapedRecord, requireUtf8String, } from './codec.js';
20
20
  import { invalidProtocolFrame } from './errors.js';
21
21
  import { defineOperation } from './operation-spec.js';
22
- const SECRET_MAX_BYTES = 16 * 1024;
23
22
  const RESULT_MAX_BYTES = 8 * 1024;
24
23
  const ERRORS = [
25
24
  'host_not_ready',
@@ -38,16 +37,11 @@ export const NETWORK_PROXY_OPERATION_SPECS = {
38
37
  }),
39
38
  };
40
39
  function decodeNetworkProxyTestInput(value) {
41
- const input = requireShapedRecord(value, 'network proxy test input', [], ['networkProxy', 'password', 'url', 'timeoutMs']);
40
+ const input = requireShapedRecord(value, 'network proxy test input', [], ['networkProxy', 'url', 'timeoutMs']);
42
41
  return {
43
42
  ...(input.networkProxy === undefined
44
43
  ? {}
45
44
  : { networkProxy: decodeNetworkProxy(input.networkProxy) }),
46
- ...(input.password === undefined
47
- ? {}
48
- : {
49
- password: requireUtf8String(input.password, 'network proxy password', SECRET_MAX_BYTES),
50
- }),
51
45
  ...(input.url === undefined ? {} : { url: decodeProbeUrl(input.url) }),
52
46
  ...(input.timeoutMs === undefined
53
47
  ? {}
@@ -59,6 +59,7 @@ import { TURN_OPERATION_SPECS } from './turn.js';
59
59
  import { USAGE_PRICING_OPERATION_SPECS } from './usage-pricing.js';
60
60
  import { WEB_SEARCH_OPERATION_SPECS } from './web-search.js';
61
61
  import { WORKHUB_COORDINATION_OPERATION_SPECS } from './workhub-coordination.js';
62
+ export { isHostActivityIdle } from './host-status.js';
62
63
  export { ARTIFACT_CURSOR_MAX_BYTES, ARTIFACT_INGEST_CHUNK_MAX_BYTES, ARTIFACT_INGEST_MIME_TYPE_MAX_BYTES, ARTIFACT_MIME_TYPE_MAX_BYTES, ARTIFACT_NAME_MAX_BYTES, ARTIFACT_PAGE_MAX_ITEMS, ARTIFACT_PREVIEW_MAX_BYTES, ARTIFACT_READ_CHUNK_MAX_BYTES, ARTIFACT_RESULT_MAX_BYTES, ARTIFACT_SUMMARY_MAX_BYTES, decodeArtifactDeleteInput, decodeArtifactDeleteResult, decodeArtifactIngestInput, decodeArtifactIngestResult, decodeArtifactQueryInput, decodeArtifactQueryResult, encodeArtifactDeleteResult, encodeArtifactQueryResult, } from './artifact.js';
63
64
  export { TURN_FAILURE_MESSAGE_MAX_BYTES, TURN_MESSAGE_CONTENT_MAX_BYTES, TURN_MESSAGE_TEXT_MAX_BYTES, TURN_RESUME_PARK_REASONS, } from './turn.js';
64
65
  export * from './connection-effects.js';
@@ -162,6 +163,7 @@ export const REMOTE_OWNER_OPERATION_GRANTS = Object.freeze([
162
163
  'queue.entry.update',
163
164
  'queue.retract',
164
165
  'runtime.policy.mutate',
166
+ 'runtime.policy.network-proxy.update',
165
167
  'runtime.policy.query',
166
168
  'runtime.resource.controller.acquire',
167
169
  'runtime.resource.controller.control',
@@ -16,7 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- import { CONNECTION_CATALOG_MAX_CONNECTIONS, CONNECTION_CATALOG_MAX_ENABLED_MODEL_IDS, CONNECTION_CATALOG_MAX_ENTRIES_PER_CONNECTION, CONNECTION_CATALOG_MAX_MODELS_PER_CONNECTION, decodeCanonicalConnectionBaseUrl, decodeModelCatalogEntry, decodeCanonicalRuntimePolicy, decodeConnectionModel, decodeConnectionModelId, decodeConnectionName, decodeConnectionSlug, decodeConnectionTarget, decodeConnectionTestSummary, decodeConnectionVersionBasis, decodeRuntimePolicyEntityId, decodeCredentialLocator, decodeCredentialStatus, decodeCredentialVersionBasis, decodeProviderType, normalizeCreateCatalogConnectionInput, normalizeDeleteCredentialInput, normalizeRemoveCatalogConnectionInput, normalizeOptionalRequestBodyOverlay, normalizeRequestHeaderUpdates, normalizeRuntimePolicyMutation, normalizeSetCredentialInput, normalizeSetDefaultConnectionTargetInput, normalizeUpdateCatalogConnectionInput, REQUEST_HEADERS_MAX_BYTES, RequestCustomizationValidationError, RuntimePolicyDomainDecodeError, } from '@maka/core/runtime-policy';
19
+ import { CONNECTION_CATALOG_MAX_CONNECTIONS, CONNECTION_CATALOG_MAX_ENABLED_MODEL_IDS, CONNECTION_CATALOG_MAX_ENTRIES_PER_CONNECTION, CONNECTION_CATALOG_MAX_MODELS_PER_CONNECTION, decodeCanonicalConnectionBaseUrl, decodeModelCatalogEntry, decodeCanonicalRuntimePolicy, decodeConnectionModel, decodeConnectionModelId, decodeConnectionName, decodeConnectionSlug, decodeConnectionTarget, decodeConnectionTestSummary, decodeConnectionVersionBasis, decodeRuntimePolicyEntityId, decodeCredentialLocator, decodeCredentialStatus, decodeCredentialVersionBasis, decodeProviderType, normalizeCreateCatalogConnectionInput, normalizeDeleteCredentialInput, normalizeRemoveCatalogConnectionInput, normalizeOptionalRequestBodyOverlay, normalizeNetworkProxyUpdate, normalizeNetworkProxyCredentialTarget, normalizeRequestHeaderUpdates, normalizeRuntimePolicyMutation, normalizeSetCredentialInput, normalizeSetDefaultConnectionTargetInput, normalizeUpdateCatalogConnectionInput, REQUEST_HEADERS_MAX_BYTES, RequestCustomizationValidationError, RuntimePolicyDomainDecodeError, } from '@maka/core/runtime-policy';
20
20
  import { normalizeRelayModelProfiles } from '@maka/core/model-thinking';
21
21
  import { requireExactRecord, requireShapedRecord, requireRecord } from './codec.js';
22
22
  import { invalidProtocolFrame } from './errors.js';
@@ -58,6 +58,13 @@ export const RUNTIME_POLICY_OPERATION_SPECS = {
58
58
  decodeInput: decodeRuntimePolicyMutation,
59
59
  decodeOutput: decodeRuntimePolicyMutationResult,
60
60
  }),
61
+ 'runtime.policy.network-proxy.update': defineOperation({
62
+ mode: 'command',
63
+ availability: 'ready',
64
+ errors: MUTATION_ERRORS,
65
+ decodeInput: decodeRuntimePolicyNetworkProxyUpdate,
66
+ decodeOutput: decodeRuntimePolicyNetworkProxyUpdateResult,
67
+ }),
61
68
  'connection.catalog.query': defineOperation({
62
69
  mode: 'query',
63
70
  availability: 'ready',
@@ -158,6 +165,44 @@ function decodeRuntimePolicyMutationResult(value) {
158
165
  }
159
166
  return revisionConflict(item, 'runtime policy mutation result');
160
167
  }
168
+ function decodeRuntimePolicyNetworkProxyUpdate(value) {
169
+ const input = decodeDomain(() => normalizeNetworkProxyUpdate(value));
170
+ if (input.credential.kind === 'replace' &&
171
+ Buffer.byteLength(input.credential.secret, 'utf8') > CREDENTIAL_SECRET_MAX_BYTES) {
172
+ throw invalidProtocolFrame('Invalid network proxy credential secret');
173
+ }
174
+ return input;
175
+ }
176
+ function decodeRuntimePolicyNetworkProxyUpdateResult(value) {
177
+ const item = requireRecord(value, 'network proxy update result');
178
+ if (item.kind === 'committed') {
179
+ const committed = requireExactRecord(item, 'network proxy update committed result', [
180
+ 'kind',
181
+ 'revision',
182
+ 'credentialStatus',
183
+ ]);
184
+ return {
185
+ kind: 'committed',
186
+ revision: revision(committed.revision, 'runtime policy revision'),
187
+ credentialStatus: decodeDomain(() => decodeCredentialStatus(committed.credentialStatus)),
188
+ };
189
+ }
190
+ if (item.kind === 'credential_stale')
191
+ return credentialStale(item);
192
+ if (item.kind === 'proxy_target_mismatch') {
193
+ const mismatch = requireExactRecord(item, 'network proxy target mismatch', [
194
+ 'kind',
195
+ 'expected',
196
+ 'actual',
197
+ ]);
198
+ return {
199
+ kind: 'proxy_target_mismatch',
200
+ expected: decodeDomain(() => normalizeNetworkProxyCredentialTarget(mismatch.expected)),
201
+ actual: decodeDomain(() => normalizeNetworkProxyCredentialTarget(mismatch.actual)),
202
+ };
203
+ }
204
+ return revisionConflict(item, 'network proxy update result');
205
+ }
161
206
  function decodeCatalogQueryInput(value) {
162
207
  const item = requireRecord(value, 'connection catalog query input');
163
208
  if (item.kind === 'start') {
@@ -497,6 +542,8 @@ function decodeSetCredentialResult(value) {
497
542
  requireExactRecord(item, 'credential connection not found result', ['kind']);
498
543
  return { kind: 'connection_not_found' };
499
544
  }
545
+ if (item.kind === 'connection_stale')
546
+ return connectionStale(item);
500
547
  return credentialStale(item);
501
548
  }
502
549
  function decodeDeleteCredentialResult(value) {
@@ -212,8 +212,10 @@ function decodeCollaborationTurnRequestCreateInput(value) {
212
212
  return { intent: decodeSessionTurnRequestIntent(record.intent) };
213
213
  }
214
214
  function decodeCollaborationTurnRequestQueryInput(value) {
215
- const record = requireExactRecord(value, 'collaboration Turn request query', ['sessionId']);
216
- return { sessionId: requireEntityId(record.sessionId, 'sessionId') };
215
+ const record = requireShapedRecord(value, 'collaboration Turn request query', [], ['sessionId']);
216
+ return record.sessionId === undefined
217
+ ? {}
218
+ : { sessionId: requireEntityId(record.sessionId, 'sessionId') };
217
219
  }
218
220
  function decodeCollaborationTurnRequestQueryResult(value) {
219
221
  const record = requireExactRecord(value, 'collaboration Turn request query result', [
@@ -17,7 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { MAX_ATTACHMENT_BYTES, MAX_ATTACHMENT_COUNT } from '@maka/core/attachments';
20
- import { decodeMessageContent as decodeCanonicalMessageContent, isContextBudgetExhaustedDetail, DIRECTORY_REFERENCE_MAX_COUNT, isCanonicalAttachmentRef, } from '@maka/core/events';
20
+ import { decodeMessageContent as decodeCanonicalMessageContent, DIRECTORY_REFERENCE_MAX_COUNT, isCanonicalAttachmentRef, } from '@maka/core/events';
21
21
  import { isOrchestrationMode, isTurnOrchestrationSource, } from '@maka/core/orchestration';
22
22
  import { decodeSkillInvocationResult, } from '@maka/core/skill-invocation';
23
23
  import { invalidProtocolFrame } from './errors.js';
@@ -449,7 +449,7 @@ export function decodeTurnSnapshot(value) {
449
449
  };
450
450
  }
451
451
  if (status === 'failed') {
452
- requireShapedRecord(record, 'failed Turn snapshot', ['sessionId', 'turnId', 'runId', 'status', 'terminalEventId', 'failureClass'], ['failureMessage', 'contextBudgetExhaustedDetail']);
452
+ requireShapedRecord(record, 'failed Turn snapshot', ['sessionId', 'turnId', 'runId', 'status', 'terminalEventId', 'failureClass'], ['failureMessage']);
453
453
  return {
454
454
  ...base,
455
455
  status,
@@ -460,11 +460,6 @@ export function decodeTurnSnapshot(value) {
460
460
  failureMessage: requireUtf8String(record.failureMessage, 'failureMessage', TURN_FAILURE_MESSAGE_MAX_BYTES, false),
461
461
  }
462
462
  : {}),
463
- ...(record.contextBudgetExhaustedDetail !== undefined
464
- ? {
465
- contextBudgetExhaustedDetail: requireContextBudgetExhaustedDetail(record.contextBudgetExhaustedDetail),
466
- }
467
- : {}),
468
463
  };
469
464
  }
470
465
  if (status === 'cancelled') {
@@ -492,11 +487,6 @@ export function decodeTurnSnapshot(value) {
492
487
  : {}),
493
488
  };
494
489
  }
495
- function requireContextBudgetExhaustedDetail(value) {
496
- if (isContextBudgetExhaustedDetail(value))
497
- return value;
498
- throw invalidProtocolFrame('Invalid context budget exhausted detail');
499
- }
500
490
  export function decodeContextCompactionOutcome(value) {
501
491
  const record = requireRecord(value, 'Context compaction outcome');
502
492
  const kind = requireString(record.kind, 'kind', 32);
@@ -215,10 +215,11 @@ class FileRuntimeHostAccessAuthority {
215
215
  const guest = principal.principalKind === 'session_guest';
216
216
  return {
217
217
  canRequestTurns: guest &&
218
+ input.sessionId !== undefined &&
218
219
  this.activeSessionGrant(principal.principalId, input.sessionId, 'session_turn_request') !==
219
220
  undefined,
220
221
  requests: this.#file.turnAccessRequests.filter((request) => (!guest || request.principalId === principal.principalId) &&
221
- request.intent.sessionId === input.sessionId),
222
+ (input.sessionId === undefined || request.intent.sessionId === input.sessionId)),
222
223
  };
223
224
  }
224
225
  acknowledgeTurnAccessRequest(principalId, input) {
@@ -147,8 +147,7 @@ function classifyAgentGraphOutcome(snapshot) {
147
147
  if (snapshot.status === 'cancelled')
148
148
  return { kind: 'aborted', turnId: snapshot.turnId };
149
149
  if (snapshot.status === 'failed') {
150
- if (snapshot.failureClass === 'context_overflow' ||
151
- snapshot.failureClass === 'context_budget_exhausted') {
150
+ if (snapshot.failureClass === 'context_overflow') {
152
151
  return {
153
152
  kind: 'context_overflow',
154
153
  turnId: snapshot.turnId,
@@ -16,7 +16,6 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- import { isContextBudgetExhaustedDetail, } from '@maka/core/events';
20
19
  import { truncateUtf8 } from '@maka/core/diagnostic-log';
21
20
  import { redactSecrets } from '@maka/core/redaction';
22
21
  import { classifyTerminalRuntimeLedger } from '@maka/runtime/terminal-run-commit';
@@ -53,7 +52,6 @@ export async function readCanonicalTurnSnapshot(stores, identity, knownRun) {
53
52
  const failureMessage = fact.terminalEvent.content?.kind === 'error'
54
53
  ? truncateUtf8(redactSecrets(fact.terminalEvent.content.message), TURN_FAILURE_MESSAGE_MAX_BYTES, '…')
55
54
  : undefined;
56
- const contextBudgetExhaustedDetail = readContextBudgetExhaustedDetail(fact.terminalEvent.actions?.stateDelta?.contextBudgetExhaustedDetail);
57
55
  return {
58
56
  sessionId,
59
57
  turnId,
@@ -62,7 +60,6 @@ export async function readCanonicalTurnSnapshot(stores, identity, knownRun) {
62
60
  terminalEventId: fact.terminalEvent.id,
63
61
  failureClass: fact.failureClass,
64
62
  ...(failureMessage ? { failureMessage } : {}),
65
- ...(contextBudgetExhaustedDetail ? { contextBudgetExhaustedDetail } : {}),
66
63
  };
67
64
  }
68
65
  if (!fact.abortSource)
@@ -87,9 +84,6 @@ export async function readCanonicalTurnSnapshot(stores, identity, knownRun) {
87
84
  }
88
85
  return { sessionId, turnId, runId, status: run.status };
89
86
  }
90
- function readContextBudgetExhaustedDetail(value) {
91
- return isContextBudgetExhaustedDetail(value) ? value : undefined;
92
- }
93
87
  function readContextCompactionOutcome(value) {
94
88
  if (!value || typeof value !== 'object')
95
89
  return undefined;
@@ -111,9 +105,6 @@ export function worstCaseFailedTurnSnapshot(identity) {
111
105
  terminalEventId: 'x'.repeat(128),
112
106
  failureClass: '\0'.repeat(128),
113
107
  failureMessage: '\0'.repeat(TURN_FAILURE_MESSAGE_MAX_BYTES),
114
- // Keep capacity preflight conservative for every protocol-valid failure
115
- // detail, including the longest malformed-summary diagnostic.
116
- contextBudgetExhaustedDetail: 'malformed_summary_too_small_for_fold',
117
108
  };
118
109
  }
119
110
  async function readRunIfPresent(stores, sessionId, runId) {
@@ -26,11 +26,30 @@ export class HostConfigurationCoordinator {
26
26
  }
27
27
  async #exportCredentials(input) {
28
28
  try {
29
- const material = await this.policy.exportCredentialMaterial(input.locator);
29
+ const exported = input.expectedConnection
30
+ ? await this.policy.exportCredentialMaterial(input.locator, input.expectedConnection)
31
+ : {
32
+ kind: 'exported',
33
+ material: await this.policy.exportCredentialMaterial(input.locator),
34
+ };
35
+ if (exported.kind === 'connection_stale') {
36
+ return {
37
+ ok: true,
38
+ result: {
39
+ credential: null,
40
+ connectionStale: {
41
+ expected: exported.expected,
42
+ actual: exported.actual,
43
+ },
44
+ },
45
+ };
46
+ }
47
+ const material = exported.material;
30
48
  const credential = material
31
49
  ? {
32
50
  locator: material.locator,
33
51
  secretBase64: Buffer.from(material.secret, 'utf8').toString('base64'),
52
+ ...(material.proxyTarget === undefined ? {} : { proxyTarget: material.proxyTarget }),
34
53
  }
35
54
  : null;
36
55
  return { ok: true, result: { credential } };