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

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 (160) hide show
  1. package/dist/cli-core.js +0 -1
  2. package/dist/pi-transcript.js +48 -0
  3. package/dist/pi-tui-form-interaction.js +497 -0
  4. package/dist/pi-tui-pickers.js +256 -55
  5. package/dist/pi-tui-runner.js +215 -12
  6. package/dist/runtime-host-cli.js +17 -42
  7. package/dist/runtime-host-lifecycle-transaction.js +31 -13
  8. package/dist/runtime-host-managed-lifecycle-manager.js +1 -1
  9. package/dist/runtime-host-onboarding.js +3 -1
  10. package/dist/runtime-host-openrc-service.js +469 -0
  11. package/dist/runtime-host-run-command.js +6 -2
  12. package/dist/runtime-host-service-command.js +3 -3
  13. package/dist/runtime-host-service-management-command.js +43 -15
  14. package/dist/runtime-host-session-channel.js +3 -0
  15. package/dist/runtime-host-session-driver.js +21 -6
  16. package/dist/runtime-host-setup-command.js +30 -31
  17. package/dist/tui-context-refresh.js +103 -0
  18. package/dist/tui-copy-catalog.js +73 -0
  19. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  20. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  21. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  22. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  23. package/node_modules/@maka/core/dist/chat-model-choice.js +4 -0
  24. package/node_modules/@maka/core/dist/computer-use.js +27 -92
  25. package/node_modules/@maka/core/dist/interaction.js +577 -3
  26. package/node_modules/@maka/core/dist/llm-connections.js +4 -0
  27. package/node_modules/@maka/core/dist/local-memory.js +2 -2
  28. package/node_modules/@maka/core/dist/model-call-usage-projection.js +3 -0
  29. package/node_modules/@maka/core/dist/model-metadata.js +26 -7
  30. package/node_modules/@maka/core/dist/model-thinking.js +17 -0
  31. package/node_modules/@maka/core/dist/provider-registry.js +1 -0
  32. package/node_modules/@maka/core/dist/runtime-event.js +23 -1
  33. package/node_modules/@maka/core/dist/scheduled-task.js +4 -0
  34. package/node_modules/@maka/core/dist/session.js +95 -2
  35. package/node_modules/@maka/core/dist/text-sanitize.js +19 -0
  36. package/node_modules/@maka/core/dist/usage-ledger-merge.js +3 -0
  37. package/node_modules/@maka/core/dist/usage-record-schema.js +11 -5
  38. package/node_modules/@maka/core/dist/workhub-creation-intent.js +75 -9
  39. package/node_modules/@maka/core/dist/workhub-session-resolver.js +50 -0
  40. package/node_modules/@maka/core/package.json +2 -0
  41. package/node_modules/@maka/eval/dist/harbor-external-subject.js +2 -1
  42. package/node_modules/@maka/eval/dist/harness-executor.js +3 -2
  43. package/node_modules/@maka/eval/dist/install-preflight.js +39 -10
  44. package/node_modules/@maka/eval/dist/process-termination.js +83 -0
  45. package/node_modules/@maka/mcp/dist/sep-2243.js +3 -3
  46. package/node_modules/@maka/runtime/dist/agent-run.js +6 -3
  47. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +332 -118
  48. package/node_modules/@maka/runtime/dist/ai-sdk-compaction-contract.js +6 -1
  49. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +171 -341
  50. package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +3 -26
  51. package/node_modules/@maka/runtime/dist/bundled-skill-catalog.generated.js +1 -1
  52. package/node_modules/@maka/runtime/dist/computer-use-codec.js +1 -141
  53. package/node_modules/@maka/runtime/dist/computer-use-tools.js +23 -105
  54. package/node_modules/@maka/runtime/dist/context-budget-policy.js +20 -26
  55. package/node_modules/@maka/runtime/dist/context-budget.js +18 -20
  56. package/node_modules/@maka/runtime/dist/conversation-copy.js +23 -19
  57. package/node_modules/@maka/runtime/dist/cua-frame-state.js +1 -69
  58. package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +267 -0
  59. package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +6 -9
  60. package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +67 -23
  61. package/node_modules/@maka/runtime/dist/history-compact-summary-validation.js +11 -18
  62. package/node_modules/@maka/runtime/dist/history-compaction.js +66 -73
  63. package/node_modules/@maka/runtime/dist/interaction-authority.js +69 -0
  64. package/node_modules/@maka/runtime/dist/mcp-tools.js +13 -1
  65. package/node_modules/@maka/runtime/dist/memory-extraction.js +11 -11
  66. package/node_modules/@maka/runtime/dist/model-adapter.js +24 -6
  67. package/node_modules/@maka/runtime/dist/model-factory.js +14 -5
  68. package/node_modules/@maka/runtime/dist/oauth-provider-contracts.js +28 -1
  69. package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +7 -6
  70. package/node_modules/@maka/runtime/dist/opencode-session-header.js +29 -0
  71. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +11 -1
  72. package/node_modules/@maka/runtime/dist/runtime-kernel.js +12 -11
  73. package/node_modules/@maka/runtime/dist/runtime-read-model.js +51 -13
  74. package/node_modules/@maka/runtime/dist/scheduled-task-tools.js +3 -0
  75. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +26 -0
  76. package/node_modules/@maka/runtime/dist/session-manager.js +1 -0
  77. package/node_modules/@maka/runtime/dist/session-projection-helpers.js +16 -1
  78. package/node_modules/@maka/runtime/dist/stream-graph-projection.js +6 -0
  79. package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +5 -0
  80. package/node_modules/@maka/runtime/dist/stream-usage-fallback-fetch.js +74 -0
  81. package/node_modules/@maka/runtime/dist/subscription-credentials.js +51 -4
  82. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +47 -3
  83. package/node_modules/@maka/runtime/dist/system-prompt/main-session-prompt.js +20 -1
  84. package/node_modules/@maka/runtime/dist/test-connection.js +18 -11
  85. package/node_modules/@maka/runtime/dist/tool-runtime.js +183 -0
  86. package/node_modules/@maka/runtime/package.json +1 -0
  87. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +14 -0
  88. package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +84 -1
  89. package/node_modules/@maka/runtime-host/dist/client/host-profile.js +68 -51
  90. package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
  91. package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +8 -5
  92. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +342 -36
  93. package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +60 -4
  94. package/node_modules/@maka/runtime-host/dist/peer-mesh/index.js +3 -3
  95. package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +1 -1
  96. package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +48 -60
  97. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +847 -431
  98. package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +33 -74
  99. package/node_modules/@maka/runtime-host/dist/peer-mesh/store.js +82 -69
  100. package/node_modules/@maka/runtime-host/dist/peer-reachability/index.js +21 -0
  101. package/node_modules/@maka/runtime-host/dist/peer-reachability/model.js +202 -0
  102. package/node_modules/@maka/runtime-host/dist/peer-reachability/owner.js +119 -0
  103. package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +245 -0
  104. package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +4 -1
  105. package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +68 -0
  106. package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +15 -8
  107. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +7 -23
  108. package/node_modules/@maka/runtime-host/dist/protocol/index.js +47 -1
  109. package/node_modules/@maka/runtime-host/dist/protocol/oauth.js +25 -1
  110. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
  111. package/node_modules/@maka/runtime-host/dist/protocol/peer-mesh.js +14 -31
  112. package/node_modules/@maka/runtime-host/dist/protocol/scheduled-task.js +19 -6
  113. package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +36 -1
  114. package/node_modules/@maka/runtime-host/dist/protocol/session-revision.js +11 -2
  115. package/node_modules/@maka/runtime-host/dist/protocol/usage-pricing.js +16 -2
  116. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +51 -2
  117. package/node_modules/@maka/runtime-host/dist/server/access-authority.js +35 -2
  118. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
  119. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +12 -10
  120. package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +163 -30
  121. package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +38 -14
  122. package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +45 -22
  123. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +78 -20
  124. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +4 -2
  125. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +2 -1
  126. package/node_modules/@maka/runtime-host/dist/server/execution-service.js +49 -28
  127. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -6
  128. package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +130 -27
  129. package/node_modules/@maka/runtime-host/dist/server/interaction-projection.js +30 -1
  130. package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -4
  131. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +16 -3
  132. package/node_modules/@maka/runtime-host/dist/server/oauth-coordinator.js +66 -3
  133. package/node_modules/@maka/runtime-host/dist/server/oauth-execution-authority.js +20 -3
  134. package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +8 -21
  135. package/node_modules/@maka/runtime-host/dist/server/peer-mesh-authority.js +1 -1
  136. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +6 -3
  137. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +72 -12
  138. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +48 -18
  139. package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +43 -26
  140. package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +15 -1
  141. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +229 -4
  142. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +146 -8
  143. package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +32 -6
  144. package/node_modules/@maka/runtime-host/package.json +1 -0
  145. package/node_modules/@maka/storage/dist/agent-run-store.js +18 -5
  146. package/node_modules/@maka/storage/dist/codex-session-adapter.js +14 -0
  147. package/node_modules/@maka/storage/dist/execution-stores.js +5 -0
  148. package/node_modules/@maka/storage/dist/interaction-store.js +13 -1
  149. package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +9 -3
  150. package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +49 -19
  151. package/node_modules/@maka/storage/dist/runtime-policy/oauth-login-receipt-document.js +3 -1
  152. package/node_modules/@maka/storage/dist/runtime-policy/onboarding-transaction.js +13 -5
  153. package/node_modules/@maka/storage/dist/session-copy-cleanup.js +4 -2
  154. package/node_modules/@maka/storage/dist/session-store.js +39 -6
  155. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +16 -9
  156. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +30 -3
  157. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +117 -0
  158. package/node_modules/@maka/storage/dist/sqlite-usage-store.js +29 -6
  159. package/node_modules/@maka/storage/dist/usage-stores.js +3 -1
  160. package/package.json +1 -1
@@ -0,0 +1,119 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { chmod, mkdir } from 'node:fs/promises';
20
+ import { join } from 'node:path';
21
+ import { setTimeout as delay } from 'node:timers/promises';
22
+ import { acquireFileLifetimeOwner, } from '@maka/storage/file-lifetime-owner';
23
+ import { createRuntimeHostPeerClient } from '../client/peer-client.js';
24
+ import { RuntimeHostPermanentReconnectError } from '../client/reconnect-lifecycle.js';
25
+ import { openPeerReachabilityPublisher, PeerReachabilityPostCommitError, } from './publisher.js';
26
+ const REACHABILITY_WATCH_TIMEOUT_MS = 60_000;
27
+ const REACHABILITY_RETRY_INTERVAL_MS = 5_000;
28
+ export async function openRuntimeHostPeerEndpointOwner(input) {
29
+ await mkdir(input.dataRoot, { recursive: true, mode: 0o700 });
30
+ if (process.platform !== 'win32')
31
+ await chmod(input.dataRoot, 0o700);
32
+ const rootOwner = await acquireFileLifetimeOwner(join(input.dataRoot, 'peer-endpoint.owner'));
33
+ let client;
34
+ let reachability;
35
+ try {
36
+ client = createRuntimeHostPeerClient({
37
+ nativePath: input.nativePath,
38
+ keyPath: input.keyPath,
39
+ relayAnchorPath: join(input.dataRoot, 'relay-anchors.json'),
40
+ ...(input.expectedPeerId ? { expectedPeerId: input.expectedPeerId } : {}),
41
+ ...(input.listenAddresses ? { listenAddresses: input.listenAddresses } : {}),
42
+ ...(input.coordinationRelays ? { coordinationRelays: input.coordinationRelays } : {}),
43
+ ...(input.automaticRelayDiscovery === undefined
44
+ ? {}
45
+ : { automaticRelayDiscovery: input.automaticRelayDiscovery }),
46
+ ...(input.webRtcStunUrls === undefined ? {} : { webRtcStunUrls: input.webRtcStunUrls }),
47
+ });
48
+ const peerId = client.identity().peerId;
49
+ reachability = await openPeerReachabilityPublisher({
50
+ dataRoot: join(input.dataRoot, peerId),
51
+ peer: client,
52
+ });
53
+ }
54
+ catch (error) {
55
+ await reachability?.close().catch(() => undefined);
56
+ await client?.close().catch(() => undefined);
57
+ await rootOwner.close().catch(() => undefined);
58
+ throw error;
59
+ }
60
+ let closeTask;
61
+ const ownedClient = client;
62
+ const ownedReachability = reachability;
63
+ const lifetime = new AbortController();
64
+ const maintenance = maintainReachability(ownedClient, ownedReachability, lifetime.signal, input.onBackgroundReachabilityError);
65
+ void maintenance.catch(() => undefined);
66
+ return Object.freeze({
67
+ client: ownedClient,
68
+ reachability: ownedReachability,
69
+ closed: maintenance,
70
+ close: () => {
71
+ lifetime.abort();
72
+ closeTask ??= closeEndpointOwner(ownedClient, ownedReachability, rootOwner, maintenance);
73
+ return closeTask;
74
+ },
75
+ });
76
+ }
77
+ async function closeEndpointOwner(client, reachability, rootOwner, maintenance) {
78
+ const errors = [];
79
+ await client.close().catch((error) => errors.push(error));
80
+ await maintenance.catch((error) => errors.push(error));
81
+ await reachability.close().catch((error) => errors.push(error));
82
+ await rootOwner.close().catch((error) => errors.push(error));
83
+ if (errors.length === 1)
84
+ throw errors[0];
85
+ if (errors.length > 1) {
86
+ throw new AggregateError(errors, 'Unable to close Runtime Host peer endpoint owner');
87
+ }
88
+ }
89
+ async function maintainReachability(client, publisher, signal, onError) {
90
+ let generation = client.reachability().generation;
91
+ while (!signal.aborted) {
92
+ try {
93
+ await publisher.refresh();
94
+ const observed = await client.watchReachability(generation, REACHABILITY_WATCH_TIMEOUT_MS);
95
+ generation = observed.generation;
96
+ }
97
+ catch (error) {
98
+ if (signal.aborted)
99
+ return;
100
+ if (error instanceof PeerReachabilityPostCommitError ||
101
+ error instanceof RuntimeHostPermanentReconnectError) {
102
+ throw error;
103
+ }
104
+ try {
105
+ onError?.(error);
106
+ }
107
+ catch {
108
+ // A diagnostic observer cannot control the endpoint lifetime.
109
+ }
110
+ try {
111
+ await delay(REACHABILITY_RETRY_INTERVAL_MS, undefined, { signal });
112
+ }
113
+ catch {
114
+ if (signal.aborted)
115
+ return;
116
+ }
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,245 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { chmod, lstat, mkdir, open, readFile, rename, unlink } from 'node:fs/promises';
20
+ import { dirname, join } from 'node:path';
21
+ import { performance } from 'node:perf_hooks';
22
+ import { authenticateSignedPeerReachabilityLease, canonicalPeerReachabilityLease, decodeSignedPeerReachabilityLease, isPeerReachabilityLeaseCurrent, PEER_REACHABILITY_LEASE_TTL_MS, PEER_REACHABILITY_REFRESH_LEAD_MS, peerReachabilityLeaseReceipt, peerReachabilityLeaseSigningBytes, samePeerReachabilityRoutes, verifySignedPeerReachabilityLease, } from './model.js';
23
+ const STATE_FILE = 'peer-reachability.json';
24
+ const MAX_STATE_BYTES = 64 * 1_024;
25
+ export async function openPeerReachabilityPublisher(input) {
26
+ await mkdir(input.dataRoot, { recursive: true, mode: 0o700 });
27
+ if (process.platform !== 'win32')
28
+ await chmod(input.dataRoot, 0o700);
29
+ const now = input.now ?? Date.now;
30
+ const monotonicNow = input.monotonicNow ?? performance.now.bind(performance);
31
+ const current = await readState(join(input.dataRoot, STATE_FILE), input.peer);
32
+ const publisher = new PeerReachabilityPublisherImpl(join(input.dataRoot, STATE_FILE), input.peer, now, monotonicNow, current);
33
+ await publisher.refresh();
34
+ return publisher;
35
+ }
36
+ class PeerReachabilityPublisherImpl {
37
+ path;
38
+ peer;
39
+ now;
40
+ monotonicNow;
41
+ #current;
42
+ #receipt;
43
+ #tail = Promise.resolve();
44
+ #failure;
45
+ #closed = false;
46
+ #closeTask;
47
+ #listeners = new Set();
48
+ constructor(path, peer, now, monotonicNow, current) {
49
+ this.path = path;
50
+ this.peer = peer;
51
+ this.now = now;
52
+ this.monotonicNow = monotonicNow;
53
+ this.#current = current;
54
+ if (current) {
55
+ this.#receipt = peerReachabilityLeaseReceipt({
56
+ signed: current,
57
+ wallNow: this.now(),
58
+ monotonicNow: this.monotonicNow(),
59
+ });
60
+ }
61
+ }
62
+ current() {
63
+ this.#assertOpen();
64
+ if (!this.#current)
65
+ throw new Error('Peer reachability publisher is not initialized');
66
+ return this.#current;
67
+ }
68
+ subscribe(listener) {
69
+ this.#assertOpen();
70
+ this.#listeners.add(listener);
71
+ return () => this.#listeners.delete(listener);
72
+ }
73
+ refresh() {
74
+ this.#assertOpen();
75
+ const task = this.#tail.then(async () => {
76
+ this.#assertOpen();
77
+ const identity = this.peer.identity();
78
+ const reachability = this.peer.reachability();
79
+ const now = this.now();
80
+ const monotonicNow = this.monotonicNow();
81
+ if (this.#current &&
82
+ this.#current.lease.peerId === identity.peerId &&
83
+ this.#current.lease.issuedAt <= now &&
84
+ this.#current.lease.expiresAt > now + PEER_REACHABILITY_REFRESH_LEAD_MS &&
85
+ isPeerReachabilityLeaseCurrent(this.#current, this.#receipt, monotonicNow + PEER_REACHABILITY_REFRESH_LEAD_MS) &&
86
+ samePeerReachabilityRoutes(this.#current.lease, reachability)) {
87
+ return this.#current;
88
+ }
89
+ const revision = (this.#current?.lease.revision ?? 0) + 1;
90
+ const lease = canonicalPeerReachabilityLease({
91
+ version: 1,
92
+ peerId: identity.peerId,
93
+ revision,
94
+ issuedAt: now,
95
+ expiresAt: now + PEER_REACHABILITY_LEASE_TTL_MS,
96
+ directRoutes: reachability.listenAddresses,
97
+ coordinationRoutes: reachability.activeCoordinationRelays,
98
+ });
99
+ const identityProof = await this.peer.signIdentity(peerReachabilityLeaseSigningBytes(lease));
100
+ const signed = decodeSignedPeerReachabilityLease({
101
+ lease,
102
+ publicKey: identityProof.publicKey.toString('base64url'),
103
+ signature: identityProof.signature.toString('base64url'),
104
+ });
105
+ verifySignedPeerReachabilityLease({
106
+ value: signed,
107
+ expectedPeerId: identity.peerId,
108
+ now,
109
+ verifyIdentity: this.peer.verifyIdentity.bind(this.peer),
110
+ });
111
+ try {
112
+ await writeState(this.path, signed);
113
+ this.#adopt(signed, now, monotonicNow);
114
+ }
115
+ catch (error) {
116
+ if (error instanceof PeerReachabilityPostCommitError) {
117
+ this.#adopt(signed, now, monotonicNow);
118
+ this.#notify(signed);
119
+ this.#failure = error;
120
+ throw error;
121
+ }
122
+ throw new PeerReachabilityPersistenceError(error);
123
+ }
124
+ this.#notify(signed);
125
+ return signed;
126
+ });
127
+ this.#tail = task.then(() => undefined, () => undefined);
128
+ return task;
129
+ }
130
+ close() {
131
+ this.#closeTask ??= this.#close();
132
+ return this.#closeTask;
133
+ }
134
+ async #close() {
135
+ this.#closed = true;
136
+ await this.#tail;
137
+ this.#listeners.clear();
138
+ }
139
+ #notify(lease) {
140
+ for (const listener of this.#listeners) {
141
+ try {
142
+ listener(lease);
143
+ }
144
+ catch {
145
+ // Reachability publication remains authoritative even if an observer fails.
146
+ }
147
+ }
148
+ }
149
+ #assertOpen() {
150
+ if (this.#closed)
151
+ throw new Error('Peer reachability publisher is closed');
152
+ if (this.#failure)
153
+ throw this.#failure;
154
+ }
155
+ #adopt(signed, wallNow, monotonicNow) {
156
+ this.#current = signed;
157
+ this.#receipt = peerReachabilityLeaseReceipt({ signed, wallNow, monotonicNow });
158
+ }
159
+ }
160
+ async function readState(path, peer) {
161
+ try {
162
+ const stat = await lstat(path);
163
+ if (!stat.isFile() || stat.size > MAX_STATE_BYTES) {
164
+ throw new Error('Invalid peer reachability state file');
165
+ }
166
+ const document = JSON.parse(await readFile(path, 'utf8'));
167
+ if (!document || typeof document !== 'object' || Array.isArray(document)) {
168
+ throw new Error('Invalid peer reachability state document');
169
+ }
170
+ const record = document;
171
+ if (record.version !== 1 || Object.keys(record).length !== 2) {
172
+ throw new Error('Invalid peer reachability state document');
173
+ }
174
+ return authenticateSignedPeerReachabilityLease({
175
+ value: record.current,
176
+ expectedPeerId: peer.identity().peerId,
177
+ verifyIdentity: peer.verifyIdentity.bind(peer),
178
+ });
179
+ }
180
+ catch (error) {
181
+ if (isNodeError(error, 'ENOENT'))
182
+ return undefined;
183
+ throw error;
184
+ }
185
+ }
186
+ async function writeState(path, current) {
187
+ const document = `${JSON.stringify({ version: 1, current }, null, 2)}\n`;
188
+ if (Buffer.byteLength(document) > MAX_STATE_BYTES) {
189
+ throw new Error('Peer reachability state is too large');
190
+ }
191
+ const temporary = `${path}.tmp`;
192
+ let replaced = false;
193
+ try {
194
+ await unlink(temporary).catch((error) => {
195
+ if (!isNodeError(error, 'ENOENT'))
196
+ throw error;
197
+ });
198
+ const handle = await open(temporary, 'wx', 0o600);
199
+ try {
200
+ await handle.writeFile(document, 'utf8');
201
+ await handle.sync();
202
+ }
203
+ finally {
204
+ await handle.close();
205
+ }
206
+ if (process.platform !== 'win32')
207
+ await chmod(temporary, 0o600);
208
+ await rename(temporary, path);
209
+ replaced = true;
210
+ try {
211
+ await syncDirectory(dirname(path));
212
+ }
213
+ catch (error) {
214
+ throw new PeerReachabilityPostCommitError(error);
215
+ }
216
+ }
217
+ finally {
218
+ if (!replaced)
219
+ await unlink(temporary).catch(() => undefined);
220
+ }
221
+ }
222
+ export class PeerReachabilityPersistenceError extends Error {
223
+ constructor(cause) {
224
+ super('Peer reachability state could not be persisted', { cause });
225
+ }
226
+ }
227
+ export class PeerReachabilityPostCommitError extends Error {
228
+ constructor(cause) {
229
+ super('Peer reachability state was replaced but its durability could not be confirmed; reopen it', { cause });
230
+ }
231
+ }
232
+ async function syncDirectory(path) {
233
+ if (process.platform === 'win32')
234
+ return;
235
+ const handle = await open(path, 'r');
236
+ try {
237
+ await handle.sync();
238
+ }
239
+ finally {
240
+ await handle.close();
241
+ }
242
+ }
243
+ function isNodeError(error, code) {
244
+ return error instanceof Error && 'code' in error && error.code === code;
245
+ }
@@ -673,7 +673,9 @@ function decodeSignal(value) {
673
673
  const record = requireShapedRecord(value, 'agent graph signal', ['kind'], ['reason', 'status']);
674
674
  if (record.kind === 'attention') {
675
675
  requireExactRecord(record, 'agent graph attention signal', ['kind', 'reason']);
676
- if (record.reason !== 'permission_request' && record.reason !== 'user_question_request') {
676
+ if (record.reason !== 'permission_request' &&
677
+ record.reason !== 'user_question_request' &&
678
+ record.reason !== 'form_request') {
677
679
  throw invalidProtocolFrame('Invalid agent graph attention reason');
678
680
  }
679
681
  return { kind: record.kind, reason: record.reason };
@@ -854,6 +856,7 @@ function requireFacet(value) {
854
856
  value === 'permission_request' ||
855
857
  value === 'permission_decision' ||
856
858
  value === 'user_question_request' ||
859
+ value === 'form_request' ||
857
860
  value === 'transfer' ||
858
861
  value === 'usage' ||
859
862
  value === 'completed' ||
@@ -17,6 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { TOOL_ACTIVITY_KINDS } from '@maka/core/events';
20
+ import { decodeInteractionAnswer, projectInteractionFormRequest, } from '@maka/core/interaction';
20
21
  import { assertExactKeys, requireCount, requireEntityId, requireExactRecord, requireRecord, requireString, } from './codec.js';
21
22
  import { invalidProtocolFrame } from './errors.js';
22
23
  import { defineHostPathOperation, defineOperation } from './operation-spec.js';
@@ -241,6 +242,19 @@ export function decodeClientCapabilityClientFrame(value) {
241
242
  data,
242
243
  };
243
244
  }
245
+ case 'client.capability.interaction_request':
246
+ assertExactKeys(frame, 'Client Capability interaction request frame', [
247
+ 'kind',
248
+ 'invocationId',
249
+ 'interactionId',
250
+ 'request',
251
+ ]);
252
+ return {
253
+ kind: frame.kind,
254
+ invocationId: requireEntityId(frame.invocationId, 'invocationId'),
255
+ interactionId: requireEntityId(frame.interactionId, 'interactionId'),
256
+ request: decodeClientCapabilityFormRequest(frame.request),
257
+ };
244
258
  default:
245
259
  throw invalidProtocolFrame('Invalid Client Capability client frame kind');
246
260
  }
@@ -330,6 +344,19 @@ export function decodeClientCapabilityHostFrame(value) {
330
344
  kind: frame.kind,
331
345
  invocationId: requireEntityId(frame.invocationId, 'invocationId'),
332
346
  };
347
+ case 'client.capability.interaction_result':
348
+ assertExactKeys(frame, 'Client Capability interaction result frame', [
349
+ 'kind',
350
+ 'invocationId',
351
+ 'interactionId',
352
+ 'result',
353
+ ]);
354
+ return {
355
+ kind: frame.kind,
356
+ invocationId: requireEntityId(frame.invocationId, 'invocationId'),
357
+ interactionId: requireEntityId(frame.interactionId, 'interactionId'),
358
+ result: decodeClientCapabilityFormResult(frame.result),
359
+ };
333
360
  case 'client.capability.registration_release':
334
361
  assertExactKeys(frame, 'Client Capability registration release frame', [
335
362
  'kind',
@@ -359,6 +386,45 @@ export function decodeClientCapabilityResult(value) {
359
386
  : {}),
360
387
  };
361
388
  }
389
+ function decodeClientCapabilityFormRequest(value) {
390
+ const record = requireExactRecord(value, 'Client Capability form request', [
391
+ 'message',
392
+ 'requester',
393
+ 'fields',
394
+ ]);
395
+ let request;
396
+ try {
397
+ request = projectInteractionFormRequest({
398
+ toolUseId: 'client-capability-interaction',
399
+ message: record.message,
400
+ requester: record.requester,
401
+ fields: record.fields,
402
+ });
403
+ }
404
+ catch {
405
+ throw invalidProtocolFrame('Invalid Client Capability form request');
406
+ }
407
+ return {
408
+ message: request.message,
409
+ requester: request.requester,
410
+ fields: request.fields,
411
+ };
412
+ }
413
+ function decodeClientCapabilityFormResult(value) {
414
+ const record = requireRecord(value, 'Client Capability form result');
415
+ let answer;
416
+ try {
417
+ answer = decodeInteractionAnswer({ kind: 'form', ...record });
418
+ }
419
+ catch {
420
+ throw invalidProtocolFrame('Invalid Client Capability form result');
421
+ }
422
+ if (answer.kind !== 'form')
423
+ throw invalidProtocolFrame('Invalid Client Capability form result');
424
+ return answer.action === 'accept'
425
+ ? { action: 'accept', values: answer.values }
426
+ : { action: answer.action };
427
+ }
362
428
  function decodeClientCapabilityOffer(value) {
363
429
  const record = requireRecord(value, 'Client Capability offer');
364
430
  assertOptionalExactKeys(record, 'Client Capability offer', ['offerId', 'version', 'affinity', 'hostPathAccess', 'label', 'tools'], ['description']);
@@ -820,6 +886,7 @@ const CLIENT_CAPABILITY_CLIENT_FRAME_KINDS = new Set([
820
886
  'client.capability.result',
821
887
  'client.capability.result_start',
822
888
  'client.capability.result_chunk',
889
+ 'client.capability.interaction_request',
823
890
  ]);
824
891
  const CLIENT_CAPABILITY_HOST_FRAME_KINDS = new Set([
825
892
  'client.capability.call',
@@ -828,4 +895,5 @@ const CLIENT_CAPABILITY_HOST_FRAME_KINDS = new Set([
828
895
  'client.capability.release',
829
896
  'client.capability.registration_release',
830
897
  'client.capability.admitted',
898
+ 'client.capability.interaction_result',
831
899
  ]);
@@ -16,8 +16,8 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- import { CONNECTION_CATALOG_MAX_MODELS_PER_CONNECTION, decodeConnectionModelId, decodeConnectionModel, decodeConnectionSlug, decodeProviderType, decodeConnectionTestSummary, decodeConnectionVersionBasis, RuntimePolicyDomainDecodeError, } from '@maka/core/runtime-policy';
20
- import { requireCount, requireEntityId, requireExactRecord, requireRecord, requireString, } from './codec.js';
19
+ import { CONNECTION_CATALOG_MAX_MODELS_PER_CONNECTION, decodeConnectionModelId, decodeConnectionModel, decodeConnectionName, decodeConnectionSlug, decodeProviderType, decodeConnectionTestSummary, decodeConnectionVersionBasis, RuntimePolicyDomainDecodeError, } from '@maka/core/runtime-policy';
20
+ import { requireCount, requireEntityId, requireExactRecord, requireRecord, requireShapedRecord, requireString, } from './codec.js';
21
21
  import { invalidProtocolFrame } from './errors.js';
22
22
  import { defineOperation } from './operation-spec.js';
23
23
  const EFFECT_ERRORS = [
@@ -91,9 +91,8 @@ export function decodeConnectionOnboardingSaveInput(value) {
91
91
  baseUrl: input.baseUrl,
92
92
  });
93
93
  if (!Array.isArray(input.enabledModelIds) ||
94
- input.enabledModelIds.length === 0 ||
95
94
  input.enabledModelIds.length > CONNECTION_CATALOG_MAX_MODELS_PER_CONNECTION) {
96
- throw invalidProtocolFrame('Connection onboarding requires at least one enabled model');
95
+ throw invalidProtocolFrame('Connection onboarding enabled models exceed the limit');
97
96
  }
98
97
  const enabledModelIds = input.enabledModelIds.map((modelId) => decodeDomain(() => decodeConnectionModelId(modelId)));
99
98
  if (new Set(enabledModelIds).size !== enabledModelIds.length) {
@@ -140,6 +139,7 @@ export function decodeConnectionOnboardingSaveResult(value) {
140
139
  rejected.reason !== 'base_url_not_configured' &&
141
140
  rejected.reason !== 'catalog_full' &&
142
141
  rejected.reason !== 'model_unavailable' &&
142
+ rejected.reason !== 'slug_taken' &&
143
143
  rejected.reason !== 'superseded')) {
144
144
  throw invalidProtocolFrame('Invalid connection onboarding save rejection');
145
145
  }
@@ -164,13 +164,19 @@ export function decodeConnectionOnboardingVerifyInput(value) {
164
164
  function decodeConnectionOnboardingTarget(value) {
165
165
  const target = requireRecord(value, 'connection onboarding target');
166
166
  if (target.kind === 'create') {
167
- const exact = requireExactRecord(target, 'create connection onboarding target', [
168
- 'kind',
169
- 'providerType',
170
- ]);
167
+ // slug/name are optional so a surface that accepts the derived identity
168
+ // can keep talking to any Host vintage; a surface that lets the user name
169
+ // the connection must tolerate an older Host rejecting the extra fields.
170
+ const exact = requireShapedRecord(target, 'create connection onboarding target', ['kind', 'providerType'], ['slug', 'name']);
171
171
  return {
172
172
  kind: 'create',
173
173
  providerType: decodeDomain(() => decodeProviderType(exact.providerType)),
174
+ ...(exact.slug === undefined
175
+ ? {}
176
+ : { slug: decodeDomain(() => decodeConnectionSlug(exact.slug)) }),
177
+ ...(exact.name === undefined
178
+ ? {}
179
+ : { name: decodeDomain(() => decodeConnectionName(exact.name)) }),
174
180
  };
175
181
  }
176
182
  if (target.kind === 'existing') {
@@ -216,6 +222,7 @@ export function decodeConnectionOnboardingVerifyResult(value) {
216
222
  rejected.reason !== 'connection_not_found' &&
217
223
  rejected.reason !== 'credential_not_configured' &&
218
224
  rejected.reason !== 'base_url_not_configured' &&
225
+ rejected.reason !== 'slug_taken' &&
219
226
  rejected.reason !== 'catalog_full')) {
220
227
  throw invalidProtocolFrame('Invalid connection onboarding rejection');
221
228
  }
@@ -19,6 +19,7 @@
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
+ import { decodeSignedPeerReachabilityLease, } from '../peer-reachability/model.js';
22
23
  export function isHostActivityIdle(activity) {
23
24
  return (activity.connections === 0 &&
24
25
  activity.activeOperations === 0 &&
@@ -27,9 +28,6 @@ export function isHostActivityIdle(activity) {
27
28
  export const HOST_DIAGNOSTICS_RESULT_MAX_BYTES = 72 * 1024;
28
29
  export const HOST_DIAGNOSTIC_LOG_MAX_ENTRIES = 256;
29
30
  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;
33
31
  export const HOST_BOOTSTRAP_OPERATION_SPECS = {
34
32
  'host.status': defineOperation({
35
33
  mode: 'query',
@@ -53,16 +51,6 @@ export const HOST_BOOTSTRAP_OPERATION_SPECS = {
53
51
  decodeOutput: decodeHostUpgradePrepareResult,
54
52
  }),
55
53
  };
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
- }
66
54
  function decodeEmptyHostInput(value, label) {
67
55
  requireExactRecord(value, label, []);
68
56
  return {};
@@ -205,16 +193,12 @@ function decodeHostStatusFields(record) {
205
193
  };
206
194
  }
207
195
  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'),
217
- };
196
+ try {
197
+ return decodeSignedPeerReachabilityLease(value);
198
+ }
199
+ catch {
200
+ throw invalidProtocolFrame('Invalid Runtime Host peer reachability lease');
201
+ }
218
202
  }
219
203
  function requirePlatform(value) {
220
204
  if (value === 'aix' ||
@@ -61,7 +61,53 @@ 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 = 96;
64
+ export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 109;
65
+ // 109: accepted Client Capability invocations may carry one bounded nested form
66
+ // Interaction request/result round trip.
67
+ // 108: Session Interaction snapshots, forwarded Runtime events, and Agent Graph
68
+ // activity may carry the provider-neutral `form` request/answer contract.
69
+ // 107: `token_usage` anchors record the model and connection that produced
70
+ // them. The record decodes against a closed allowlist, so an older client
71
+ // rejects the two new keys and, with them, the Session that carries them.
72
+ // 106: Session transcripts gain five `system_note` kinds
73
+ // (`context_provider_dropping`, `context_window_suggestion`,
74
+ // `context_window_overrun`, `context_reported_window_exceeded`,
75
+ // `context_overflow_after_compaction`) and
76
+ // `token_usage` records reshape `lastRequestAnchor` to
77
+ // `{ inputTokens, outputTokens }`, all behind closed allowlists in
78
+ // @maka/core. An older client that handshakes would fail
79
+ // `decodeStoredMessage` on the first transcript carrying them, so the pair
80
+ // must refuse each other at the handshake instead (#4559).
81
+ // 105: Usage summaries may carry the recorded call-time total and per-Session
82
+ // tool-invocation totals. Older Clients reject the unknown fields, so a newer
83
+ // Host's usage summary is unreadable to them.
84
+ // 104: WorkHub Coordination actions add closed direct-stop proposals,
85
+ // confirmations, expected-state preconditions, and outcomes. Older peers
86
+ // reject these strict shapes.
87
+ // 103: `github-copilot` joins `OAUTH_LOGIN_PROVIDERS`, the Host answers the
88
+ // closed `oauth.enrollment.query`, and `connection.onboarding.save` admits
89
+ // canonical OAuth material with an empty enable-all-discovered selection.
90
+ // Older peers reject these wire values, so incompatible pairs must fail the
91
+ // handshake. Re-derived from current `main`; epoch 102 is claimed by open PRs.
92
+ // 101: Session Turn requests can carry regeneration intents and Guests can
93
+ // atomically withdraw pending requests. Older peers do not share this command
94
+ // vocabulary or the expanded Guest operation grant.
95
+ // 100: `session.branch.create` makes `sourceTurnId` optional, so a side
96
+ // conversation can fork with an empty context (no copied messages, no
97
+ // fabricated `branchOfTurnId`) instead of requiring a settled turn. An older
98
+ // Host's required-field check rejects the request that omits `sourceTurnId`;
99
+ // the handshake keeps mixed-version peers apart. `session.revision.create`
100
+ // still requires `sourceTurnId`, and its wire shape and fingerprint are
101
+ // unchanged.
102
+ // 99: ScheduledTask Agent execution templates carry immutable Connection
103
+ // identity. Older peers cannot preserve the ID/slug/model binding and could
104
+ // silently route a deleted Connection to a same-slug replacement.
105
+ // 98: Peer Mesh invitations carry signed reachability leases and member route
106
+ // projections use the convergent recovery state machine. Older peers decode a
107
+ // different strict wire shape.
108
+ // 97: Host status replaces unsigned route arrays with a self-signed, bounded
109
+ // reachability lease. Older peers cannot validate the locator revision or its
110
+ // target identity before retaining it for reconnect.
65
111
  // 96: Read image tool results may carry durable `session_context` refs.
66
112
  // 95: Catalog entries carry `describedByMetadata`, so a client asks the
67
113
  // Host-resolved entry — not its own bundled table — whether a model needs a