maka-agent 0.2.0-dev.26.20260909 → 0.2.0-dev.28.20260911
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.
- package/LICENSE +106 -0
- package/dist/cli-core.js +28 -0
- package/dist/runtime-host-cli.js +43 -3
- package/dist/runtime-host-handoff-surface.js +10 -8
- package/dist/runtime-host-lifecycle-transaction.js +28 -3
- package/dist/runtime-host-local-source-retirement.js +1 -1
- package/dist/runtime-host-run-command.js +15 -7
- package/dist/runtime-host-setup-command.js +29 -6
- package/dist/runtime-host-update-command.js +49 -5
- package/dist/session-export-command.js +72 -0
- package/dist/session-import-command.js +69 -0
- package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +9 -4
- package/node_modules/@maka/core/dist/diagnostic-log.js +18 -7
- package/node_modules/@maka/core/dist/text-sanitize.js +8 -2
- package/node_modules/@maka/core/dist/usage-stats/types.js +2 -0
- package/node_modules/@maka/core/dist/workhub-routing.js +126 -0
- package/node_modules/@maka/core/package.json +2 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +15 -11
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +3 -1
- package/node_modules/@maka/runtime/dist/bots/bot-test.js +2 -1
- package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +24 -20
- package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +5 -3
- package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +87 -29
- package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +40 -36
- package/node_modules/@maka/runtime/dist/builtin-tools.js +21 -2
- package/node_modules/@maka/runtime/dist/computer-use-tools.js +4 -1
- package/node_modules/@maka/runtime/dist/deep-research-tools.js +11 -5
- package/node_modules/@maka/runtime/dist/edit-replace.js +2 -2
- package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +5 -6
- package/node_modules/@maka/runtime/dist/memory-extraction-evidence.js +7 -1
- package/node_modules/@maka/runtime/dist/memory-extraction.js +12 -7
- package/node_modules/@maka/runtime/dist/openai-responses-websocket.js +2 -5
- package/node_modules/@maka/runtime/dist/plugin-agent-service.js +131 -0
- package/node_modules/@maka/runtime/dist/plugin-approval-service.js +33 -0
- package/node_modules/@maka/runtime/dist/plugin-attachment-service.js +53 -0
- package/node_modules/@maka/runtime/dist/plugin-command-service.js +100 -0
- package/node_modules/@maka/runtime/dist/plugin-composition-loader.js +11 -2
- package/node_modules/@maka/runtime/dist/plugin-data-services.js +332 -0
- package/node_modules/@maka/runtime/dist/plugin-fs-service.js +62 -0
- package/node_modules/@maka/runtime/dist/plugin-goal-service.js +61 -0
- package/node_modules/@maka/runtime/dist/plugin-invocation-signal.js +24 -0
- package/node_modules/@maka/runtime/dist/plugin-llm-service.js +85 -0
- package/node_modules/@maka/runtime/dist/plugin-lsp-service.js +94 -0
- package/node_modules/@maka/runtime/dist/plugin-scope-registry.js +91 -0
- package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +71 -0
- package/node_modules/@maka/runtime/dist/plugin-shell-env-service.js +87 -0
- package/node_modules/@maka/runtime/dist/plugin-shell-service.js +74 -0
- package/node_modules/@maka/runtime/dist/plugin-skill-service.js +93 -0
- package/node_modules/@maka/runtime/dist/plugin-system-prompt-service.js +346 -0
- package/node_modules/@maka/runtime/dist/plugin-tool-service.js +18 -52
- package/node_modules/@maka/runtime/dist/plugin-user-question-service.js +43 -0
- package/node_modules/@maka/runtime/dist/plugin-web-service.js +69 -0
- package/node_modules/@maka/runtime/dist/request-customization-fetch.js +14 -7
- package/node_modules/@maka/runtime/dist/session-export.js +178 -0
- package/node_modules/@maka/runtime/dist/session-import.js +100 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +6 -1
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +9 -6
- package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +13 -6
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +43 -10
- package/node_modules/@maka/runtime/dist/stream-graph-handoff.js +6 -1
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +1 -4
- package/node_modules/@maka/runtime/dist/system-prompt/workspace-instructions.js +11 -6
- package/node_modules/@maka/runtime/dist/tavily-search.js +8 -2
- package/node_modules/@maka/runtime/dist/text-line-window.js +49 -0
- package/node_modules/@maka/runtime/dist/tool-output.js +35 -12
- package/node_modules/@maka/runtime/dist/unified-diff.js +40 -10
- package/node_modules/@maka/runtime/dist/web-fetch-tool.js +4 -2
- package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +82 -33
- package/node_modules/@maka/runtime/dist/workspace-executor.js +2 -6
- package/node_modules/@maka/runtime/package.json +20 -1
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +73 -24
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +46 -12
- package/node_modules/@maka/runtime-host/dist/operator/setup-frame.js +31 -21
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +7 -4
- package/node_modules/@maka/runtime-host/dist/protocol/artifact.js +2 -2
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +10 -1
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +30 -5
- package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +3 -2
- package/node_modules/@maka/runtime-host/dist/protocol/skill-catalog.js +2 -1
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +25 -23
- package/node_modules/@maka/runtime-host/dist/server/artifact-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +62 -4
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +481 -11
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +74 -0
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +6 -1
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +30 -0
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +114 -20
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +49 -12
- package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +234 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
- package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +17 -8
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +189 -92
- package/node_modules/@maka/runtime-host/dist/server/runtime-resource-projection.js +3 -9
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +20 -21
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +24 -2
- package/node_modules/@maka/runtime-host/dist/server/skill-catalog-coordinator.js +18 -2
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -35
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +92 -10
- package/node_modules/@maka/runtime-host/dist/transport/framed-byte-stream-transport.js +4 -1
- package/node_modules/@maka/runtime-host/dist/transport/local-ipc-framing.js +4 -2
- package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +9 -8
- package/node_modules/@maka/runtime-host/dist/transport/resumable-peer-stream.js +14 -9
- package/node_modules/@maka/storage/dist/agent-run-store.js +54 -10
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +32 -10
- package/node_modules/@maka/storage/dist/context-value-mutation-gate.js +59 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +1 -1
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +909 -34
- package/node_modules/@maka/storage/dist/session-message-projection.js +8 -4
- package/node_modules/@maka/storage/dist/session-store.js +2 -2
- package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +18 -4
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +12 -5
- package/node_modules/@maka/storage/dist/stable-storage.js +2 -1
- package/node_modules/@maka/storage/package.json +2 -0
- package/node_modules/@modelcontextprotocol/client/dist/src-D_zzAWoS.mjs +22 -5
- package/node_modules/@modelcontextprotocol/client/dist/src-NAgB4Mp8.cjs +22 -5
- package/node_modules/zod/v4/core/memoizer.cjs +12 -16
- package/node_modules/zod/v4/core/memoizer.js +12 -16
- package/package.json +1 -1
|
@@ -55,6 +55,8 @@ export async function runHostHandoff(input) {
|
|
|
55
55
|
// (or a broken successor) into an automatic replacement storm.
|
|
56
56
|
const attempted = new Set();
|
|
57
57
|
let automaticAttempts = 0;
|
|
58
|
+
let refusedWork;
|
|
59
|
+
let replacementCompleted = false;
|
|
58
60
|
let recovery;
|
|
59
61
|
const publish = (next) => {
|
|
60
62
|
view = next;
|
|
@@ -102,7 +104,10 @@ export async function runHostHandoff(input) {
|
|
|
102
104
|
target: view.target,
|
|
103
105
|
reason: 'unavailable',
|
|
104
106
|
mayExitNaturally: false,
|
|
105
|
-
|
|
107
|
+
...(view.manualRecheck ? { manualRecheck: true } : {}),
|
|
108
|
+
diagnostic: replacementCompleted
|
|
109
|
+
? `Host replacement completed, but reconnection failed: ${boundedDiagnostic(error)}`
|
|
110
|
+
: boundedDiagnostic(error),
|
|
106
111
|
},
|
|
107
112
|
};
|
|
108
113
|
}
|
|
@@ -124,11 +129,13 @@ export async function runHostHandoff(input) {
|
|
|
124
129
|
const blocker = observed.blocker;
|
|
125
130
|
if (recovery?.identity !== blocker.identity)
|
|
126
131
|
recovery = undefined;
|
|
132
|
+
if (refusedWork !== blocker.identity)
|
|
133
|
+
refusedWork = undefined;
|
|
127
134
|
const nextSignature = blockerSignature(blocker);
|
|
128
135
|
if (signature !== nextSignature) {
|
|
129
136
|
signature = nextSignature;
|
|
130
137
|
choice = undefined;
|
|
131
|
-
publish(projectBlocker(blocker, randomUUID(), recovery?.diagnostic));
|
|
138
|
+
publish(projectBlocker(blocker, randomUUID(), recovery?.diagnostic, refusedWork === blocker.identity));
|
|
132
139
|
}
|
|
133
140
|
const current = view;
|
|
134
141
|
const selected = choice;
|
|
@@ -142,12 +149,19 @@ export async function runHostHandoff(input) {
|
|
|
142
149
|
const automatic = !recovery &&
|
|
143
150
|
automaticAttempts < 3 &&
|
|
144
151
|
!attempted.has(nextSignature) &&
|
|
152
|
+
!blocker.replacement?.requiresExplicitSelection &&
|
|
145
153
|
blocker.replacement?.canReplaceIdle &&
|
|
146
154
|
blocker.activity &&
|
|
147
155
|
(isHostActivityIdle(blocker.activity) || cooperative);
|
|
148
156
|
const interrupt = selected?.revision === current.revision && selected.action === 'interrupt';
|
|
157
|
+
const replace = selected?.revision === current.revision && selected.action === 'replace';
|
|
149
158
|
const retry = selected?.revision === current.revision && selected.action === 'retry';
|
|
150
|
-
if ((automatic ||
|
|
159
|
+
if ((automatic ||
|
|
160
|
+
interrupt ||
|
|
161
|
+
replace ||
|
|
162
|
+
(retry &&
|
|
163
|
+
!blocker.replacement?.requiresExplicitSelection &&
|
|
164
|
+
blocker.replacement?.canReplaceIdle)) &&
|
|
151
165
|
blocker.replacement) {
|
|
152
166
|
if (automatic) {
|
|
153
167
|
attempted.add(nextSignature);
|
|
@@ -179,6 +193,8 @@ export async function runHostHandoff(input) {
|
|
|
179
193
|
finally {
|
|
180
194
|
attemptAbort = undefined;
|
|
181
195
|
}
|
|
196
|
+
replacementCompleted = result.kind === 'completed';
|
|
197
|
+
refusedWork = result.kind === 'active_work' ? blocker.identity : undefined;
|
|
182
198
|
recovery =
|
|
183
199
|
result.kind === 'recovery_required'
|
|
184
200
|
? { identity: blocker.identity, diagnostic: result.diagnostic.slice(0, 8_192) }
|
|
@@ -195,7 +211,7 @@ export async function runHostHandoff(input) {
|
|
|
195
211
|
signature = undefined;
|
|
196
212
|
continue;
|
|
197
213
|
}
|
|
198
|
-
const attention = projectBlocker(blocker, current.revision, recovery?.diagnostic);
|
|
214
|
+
const attention = projectBlocker(blocker, current.revision, recovery?.diagnostic, refusedWork === blocker.identity);
|
|
199
215
|
if (!input.openSurface)
|
|
200
216
|
throw new HostHandoffRequiredError(attention);
|
|
201
217
|
surface ??= input.openSurface(submit);
|
|
@@ -211,7 +227,7 @@ export async function runHostHandoff(input) {
|
|
|
211
227
|
resolve();
|
|
212
228
|
};
|
|
213
229
|
const abort = () => finish(input.signal?.reason);
|
|
214
|
-
const timer = setTimeout(finish, interval);
|
|
230
|
+
const timer = blocker.manualRecheck ? undefined : setTimeout(finish, interval);
|
|
215
231
|
wake = () => finish();
|
|
216
232
|
input.signal?.addEventListener('abort', abort, { once: true });
|
|
217
233
|
if (input.signal?.aborted)
|
|
@@ -225,7 +241,7 @@ export async function runHostHandoff(input) {
|
|
|
225
241
|
surface?.close();
|
|
226
242
|
}
|
|
227
243
|
}
|
|
228
|
-
function projectBlocker(blocker, revision, recovery) {
|
|
244
|
+
function projectBlocker(blocker, revision, recovery, activeWorkRefused = false) {
|
|
229
245
|
const replacement = blocker.replacement;
|
|
230
246
|
return {
|
|
231
247
|
revision,
|
|
@@ -237,15 +253,30 @@ function projectBlocker(blocker, revision, recovery) {
|
|
|
237
253
|
? 'repair_required'
|
|
238
254
|
: !replacement
|
|
239
255
|
? 'operator_required'
|
|
240
|
-
:
|
|
241
|
-
?
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
256
|
+
: replacement.requiresExplicitSelection
|
|
257
|
+
? activeWorkRefused
|
|
258
|
+
? 'busy'
|
|
259
|
+
: 'replacement_required'
|
|
260
|
+
: !blocker.activity
|
|
261
|
+
? 'activity_unknown'
|
|
262
|
+
: isHostActivityIdle(blocker.activity)
|
|
263
|
+
? 'retry_required'
|
|
264
|
+
: 'busy',
|
|
245
265
|
...(blocker.activity ? { activity: blocker.activity } : {}),
|
|
246
266
|
mayExitNaturally: blocker.mayExitNaturally,
|
|
267
|
+
...(blocker.manualRecheck ? { manualRecheck: true } : {}),
|
|
268
|
+
...(blocker.packageChange ? { packageChange: blocker.packageChange } : {}),
|
|
247
269
|
...(replacement ? { operation: replacement.kind } : {}),
|
|
248
|
-
actions: replacement?.
|
|
270
|
+
actions: replacement?.requiresExplicitSelection
|
|
271
|
+
? [
|
|
272
|
+
'cancel',
|
|
273
|
+
'retry',
|
|
274
|
+
'replace',
|
|
275
|
+
...(activeWorkRefused && replacement.canInterrupt ? ['interrupt'] : []),
|
|
276
|
+
]
|
|
277
|
+
: replacement?.canInterrupt
|
|
278
|
+
? ['cancel', 'retry', 'interrupt']
|
|
279
|
+
: ['cancel', 'retry'],
|
|
249
280
|
defaultAction: 'cancel',
|
|
250
281
|
...(blocker.recoveryBlocker ? { recoveryBlocker: blocker.recoveryBlocker } : {}),
|
|
251
282
|
...(blocker.operatorStep ? { operatorStep: blocker.operatorStep } : {}),
|
|
@@ -265,6 +296,9 @@ function blockerSignature(blocker) {
|
|
|
265
296
|
blocker.replacement?.kind,
|
|
266
297
|
blocker.replacement?.canReplaceIdle,
|
|
267
298
|
blocker.replacement?.canInterrupt,
|
|
299
|
+
blocker.replacement?.requiresExplicitSelection,
|
|
300
|
+
blocker.manualRecheck,
|
|
301
|
+
blocker.packageChange,
|
|
268
302
|
blocker.operatorStep,
|
|
269
303
|
blocker.recoveryBlocker,
|
|
270
304
|
blocker.diagnostic,
|
|
@@ -41,6 +41,29 @@ const frameBase = {
|
|
|
41
41
|
schemaVersion: z.literal(1),
|
|
42
42
|
sequence: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
43
43
|
};
|
|
44
|
+
const environmentBindingFields = {
|
|
45
|
+
version: boundedString(128),
|
|
46
|
+
serviceId: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
47
|
+
deploymentId: z.string().uuid(),
|
|
48
|
+
operator: z.unknown().transform((value, context) => {
|
|
49
|
+
try {
|
|
50
|
+
const command = decodeRuntimeHostOperatorCommand(value);
|
|
51
|
+
if (command.kind !== 'node') {
|
|
52
|
+
throw new Error('Runtime Host setup operator must be a Node command');
|
|
53
|
+
}
|
|
54
|
+
return command;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
context.addIssue({
|
|
58
|
+
code: 'custom',
|
|
59
|
+
message: error instanceof Error ? error.message : 'Runtime Host operator is invalid',
|
|
60
|
+
});
|
|
61
|
+
return z.NEVER;
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
rootPath: boundedString(4 * 1024).refine((value) => !/[\u0000-\u001f\u007f]/u.test(value)),
|
|
65
|
+
rootId: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
66
|
+
};
|
|
44
67
|
const SETUP_FRAME_SCHEMA = z.discriminatedUnion('kind', [
|
|
45
68
|
z
|
|
46
69
|
.object({
|
|
@@ -53,27 +76,7 @@ const SETUP_FRAME_SCHEMA = z.discriminatedUnion('kind', [
|
|
|
53
76
|
.object({
|
|
54
77
|
...frameBase,
|
|
55
78
|
kind: z.literal('complete'),
|
|
56
|
-
|
|
57
|
-
serviceId: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
58
|
-
deploymentId: z.string().uuid(),
|
|
59
|
-
operator: z.unknown().transform((value, context) => {
|
|
60
|
-
try {
|
|
61
|
-
const command = decodeRuntimeHostOperatorCommand(value);
|
|
62
|
-
if (command.kind !== 'node') {
|
|
63
|
-
throw new Error('Runtime Host setup operator must be a Node command');
|
|
64
|
-
}
|
|
65
|
-
return command;
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
context.addIssue({
|
|
69
|
-
code: 'custom',
|
|
70
|
-
message: error instanceof Error ? error.message : 'Runtime Host operator is invalid',
|
|
71
|
-
});
|
|
72
|
-
return z.NEVER;
|
|
73
|
-
}
|
|
74
|
-
}),
|
|
75
|
-
rootPath: boundedString(4 * 1024).refine((value) => !/[\u0000-\u001f\u007f]/u.test(value)),
|
|
76
|
-
rootId: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
79
|
+
...environmentBindingFields,
|
|
77
80
|
endpoint: boundedString(SETUP_FIELD_MAX_BYTES).refine((value) => parseRuntimeHostSetupEndpoint(value) !== undefined),
|
|
78
81
|
credentialId: boundedString(SETUP_FIELD_MAX_BYTES),
|
|
79
82
|
credential: boundedString(SETUP_CREDENTIAL_MAX_BYTES),
|
|
@@ -87,6 +90,13 @@ const SETUP_FRAME_SCHEMA = z.discriminatedUnion('kind', [
|
|
|
87
90
|
.optional(),
|
|
88
91
|
})
|
|
89
92
|
.strict(),
|
|
93
|
+
z
|
|
94
|
+
.object({
|
|
95
|
+
...frameBase,
|
|
96
|
+
kind: z.literal('existing_environment'),
|
|
97
|
+
...environmentBindingFields,
|
|
98
|
+
})
|
|
99
|
+
.strict(),
|
|
90
100
|
z
|
|
91
101
|
.object({
|
|
92
102
|
...frameBase,
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { createHash } from 'node:crypto';
|
|
20
|
-
import { setTimeout as delay } from 'node:timers/promises';
|
|
21
20
|
import { performance } from 'node:perf_hooks';
|
|
22
21
|
import { canonicalPeerMeshMemberAdvertisement, canonicalPeerMeshRoster, createPeerMeshInvitationSecret, validatePeerMeshInvitation, decodeSignedPeerMeshMemberAdvertisement, decodeSignedPeerMeshRoster, generatePeerMeshAuthorityKeyPair, matchesPeerMeshInvitationSecret, PEER_MESH_MAX_MEMBERS, PEER_MESH_MAX_MESHES, PEER_MESH_MAX_INVITATION_RECORDS, PEER_MESH_MAX_PENDING_INVITATIONS, PEER_MESH_MAX_TRANSIT_ADDRESSES_PER_RELAY, PEER_MESH_MAX_TRANSIT_RELAY_ADDRESSES, peerMeshMemberAdvertisementSigningBytes, peerMeshId, peerMeshInvitationSecretDigest, signPeerMeshRoster, } from './model.js';
|
|
23
22
|
import { canonicalPeerMeshDisplayName } from './display-name.js';
|
|
@@ -793,19 +792,23 @@ class PeerMeshNodeImpl {
|
|
|
793
792
|
}
|
|
794
793
|
}
|
|
795
794
|
async #waitForReconciliationTrigger(observedGeneration, signal) {
|
|
796
|
-
if (this.#reconcileGeneration !== observedGeneration)
|
|
795
|
+
if (this.#reconcileGeneration !== observedGeneration || signal.aborted)
|
|
797
796
|
return;
|
|
798
797
|
let wake;
|
|
799
798
|
const triggered = new Promise((resolve) => {
|
|
800
799
|
wake = resolve;
|
|
801
800
|
this.#reconcileWaiters.add(wake);
|
|
802
801
|
});
|
|
803
|
-
|
|
802
|
+
const timeout = setTimeout(wake, RECONCILE_INTERVAL_MS);
|
|
803
|
+
signal.addEventListener('abort', wake, { once: true });
|
|
804
|
+
if (this.#reconcileGeneration !== observedGeneration || signal.aborted)
|
|
804
805
|
wake();
|
|
805
806
|
try {
|
|
806
|
-
await
|
|
807
|
+
await triggered;
|
|
807
808
|
}
|
|
808
809
|
finally {
|
|
810
|
+
clearTimeout(timeout);
|
|
811
|
+
signal.removeEventListener('abort', wake);
|
|
809
812
|
this.#reconcileWaiters.delete(wake);
|
|
810
813
|
}
|
|
811
814
|
}
|
|
@@ -345,7 +345,7 @@ export function decodeArtifactQueryResult(value) {
|
|
|
345
345
|
if (!isCanonicalBase64(chunkBase64)) {
|
|
346
346
|
throw invalidProtocolFrame('Invalid artifact chunk');
|
|
347
347
|
}
|
|
348
|
-
const chunkBytes = Buffer.
|
|
348
|
+
const chunkBytes = Buffer.byteLength(chunkBase64, 'base64');
|
|
349
349
|
if (chunkBytes > ARTIFACT_READ_CHUNK_MAX_BYTES || offset + chunkBytes > totalBytes) {
|
|
350
350
|
throw invalidProtocolFrame('Invalid artifact chunk bounds');
|
|
351
351
|
}
|
|
@@ -466,7 +466,7 @@ function decodeBinaryPreview(value) {
|
|
|
466
466
|
]);
|
|
467
467
|
const base64 = boundedText(exact.base64, 'artifact binary base64', base64MaxBytes(), true);
|
|
468
468
|
if (!isCanonicalBase64(base64) ||
|
|
469
|
-
Buffer.
|
|
469
|
+
Buffer.byteLength(base64, 'base64') > ARTIFACT_PREVIEW_MAX_BYTES) {
|
|
470
470
|
throw invalidProtocolFrame('Invalid artifact binary base64');
|
|
471
471
|
}
|
|
472
472
|
return {
|
|
@@ -62,7 +62,16 @@ export const RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION = 1;
|
|
|
62
62
|
export const RUNTIME_HOST_PROTOCOL_VERSION = 0;
|
|
63
63
|
// Increment when the same protocol version no longer guarantees safe Client-Host
|
|
64
64
|
// interoperability. Mismatches are rejected before domain commands are admitted.
|
|
65
|
-
export const RUNTIME_HOST_COMPATIBILITY_EPOCH =
|
|
65
|
+
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 142;
|
|
66
|
+
// 142: Invocable Skill queries expose missing and archived Session refusals explicitly.
|
|
67
|
+
// 141: WorkHub root admissions bind model Intent/Recall decisions before actions.
|
|
68
|
+
// 140: Plugin Platform queries expose scoped Command contribution projections.
|
|
69
|
+
// Epoch-139 peers reject the added query view and result shape.
|
|
70
|
+
// 139: WorkHub recovery preserves the Host-authenticated Desktop capability binding.
|
|
71
|
+
// 138: Removes the unused steering display anchor from canonical MessageContent.
|
|
72
|
+
// 137: Reserved by the former display anchor contract.
|
|
73
|
+
// 136: WorkHub transient proposals distinguish routing dispositions from linked
|
|
74
|
+
// operations. Older peers expect replace/stop_work/resume_work dispositions.
|
|
66
75
|
// 135: WorkHub model Turns replace direct action proposals with active-Turn task tools.
|
|
67
76
|
// 134: Coordination actions own real Runtime Turns. Removes the synthetic record
|
|
68
77
|
// operation, projects typed action receipts and admitted action identities, and
|
|
@@ -108,7 +108,7 @@ export const PLUGIN_PLATFORM_OPERATION_SPECS = {
|
|
|
108
108
|
};
|
|
109
109
|
function decodePluginPlatformQueryInput(value) {
|
|
110
110
|
const input = requireShapedRecord(value, 'Plugin Platform query input', ['view'], ['rootId', 'cursor', 'limit']);
|
|
111
|
-
if (!['status', 'packages', 'entries', 'tools', 'failures'].includes(input.view)) {
|
|
111
|
+
if (!['status', 'packages', 'entries', 'tools', 'commands', 'failures'].includes(input.view)) {
|
|
112
112
|
throw invalidProtocolFrame('Invalid Plugin Platform query view');
|
|
113
113
|
}
|
|
114
114
|
const view = input.view;
|
|
@@ -125,8 +125,8 @@ function decodePluginPlatformQueryInput(value) {
|
|
|
125
125
|
(input.rootId !== undefined || cursor !== undefined || limit !== undefined)) {
|
|
126
126
|
throw invalidProtocolFrame('Plugin Platform status query does not accept paging');
|
|
127
127
|
}
|
|
128
|
-
if (input.rootId !== undefined && view !== 'entries' && view !== 'tools') {
|
|
129
|
-
throw invalidProtocolFrame('Plugin root identity is only valid for Entry and
|
|
128
|
+
if (input.rootId !== undefined && view !== 'entries' && view !== 'tools' && view !== 'commands') {
|
|
129
|
+
throw invalidProtocolFrame('Plugin root identity is only valid for Entry, Tool, and Command queries');
|
|
130
130
|
}
|
|
131
131
|
let rootId;
|
|
132
132
|
if (input.rootId !== undefined) {
|
|
@@ -190,7 +190,7 @@ function decodePluginPlatformQueryResult(value) {
|
|
|
190
190
|
]);
|
|
191
191
|
if (!Array.isArray(output.items) ||
|
|
192
192
|
output.items.length > 64 ||
|
|
193
|
-
!['packages', 'entries', 'tools', 'failures'].includes(view)) {
|
|
193
|
+
!['packages', 'entries', 'tools', 'commands', 'failures'].includes(view)) {
|
|
194
194
|
throw invalidProtocolFrame('Invalid Plugin Platform page');
|
|
195
195
|
}
|
|
196
196
|
const nextCursor = output.nextCursor === null
|
|
@@ -203,11 +203,36 @@ function decodePluginPlatformQueryResult(value) {
|
|
|
203
203
|
? { view, items: decodeInspections(output.items), nextCursor }
|
|
204
204
|
: view === 'tools'
|
|
205
205
|
? { view, items: output.items.map(decodeToolInspection), nextCursor }
|
|
206
|
-
:
|
|
206
|
+
: view === 'commands'
|
|
207
|
+
? { view, items: output.items.map(decodeCommandInspection), nextCursor }
|
|
208
|
+
: { view: 'failures', items: output.items.map(decodePlatformFailure), nextCursor };
|
|
207
209
|
}
|
|
208
210
|
requireEncodedByteLimit(decoded, 'Plugin Platform query result', PLUGIN_PLATFORM_QUERY_RESULT_MAX_BYTES);
|
|
209
211
|
return decoded;
|
|
210
212
|
}
|
|
213
|
+
function decodeCommandInspection(value) {
|
|
214
|
+
const item = requireExactRecord(value, 'Plugin Command inspection', [
|
|
215
|
+
'entryId',
|
|
216
|
+
'scopeId',
|
|
217
|
+
'extensionId',
|
|
218
|
+
'generation',
|
|
219
|
+
'name',
|
|
220
|
+
'description',
|
|
221
|
+
'aliases',
|
|
222
|
+
]);
|
|
223
|
+
if (!Array.isArray(item.aliases) || item.aliases.length > 64) {
|
|
224
|
+
throw invalidProtocolFrame('Invalid Plugin Command aliases');
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
entryId: requireId(item.entryId, 'Plugin Entry identity'),
|
|
228
|
+
scopeId: requireString(item.scopeId, 'Plugin scope identity', 256),
|
|
229
|
+
extensionId: requireId(item.extensionId, 'Plugin package identity'),
|
|
230
|
+
generation: requireCount(item.generation, 'Plugin generation'),
|
|
231
|
+
name: requireString(item.name, 'Plugin Command name', 128),
|
|
232
|
+
description: requireString(item.description, 'Plugin Command description', 4096),
|
|
233
|
+
aliases: item.aliases.map((alias) => requireString(alias, 'Plugin Command alias', 128)),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
211
236
|
function decodePlatformFailure(value) {
|
|
212
237
|
const failure = requireShapedRecord(value, 'Plugin Platform failure', ['diagnostic'], ['entryId', 'extensionId']);
|
|
213
238
|
if (failure.entryId === undefined && failure.extensionId === undefined) {
|
|
@@ -165,7 +165,8 @@ export function decodeSessionTranscriptPage(value) {
|
|
|
165
165
|
assertFragmentOrder(fragments, direction);
|
|
166
166
|
const rawBytes = requireCount(result.rawBytes, 'Session transcript page bytes');
|
|
167
167
|
if (rawBytes > SESSION_TRANSCRIPT_PAGE_MAX_BYTES ||
|
|
168
|
-
fragments.reduce((total, fragment) => total + Buffer.
|
|
168
|
+
fragments.reduce((total, fragment) => total + Buffer.byteLength(fragment.data, 'base64'), 0) !==
|
|
169
|
+
rawBytes) {
|
|
169
170
|
throw invalidProtocolFrame('Invalid Session transcript page byte count');
|
|
170
171
|
}
|
|
171
172
|
const nextCursor = result.nextCursor === null
|
|
@@ -231,7 +232,7 @@ function decodeSessionTranscriptFragment(value, source, throughSequence) {
|
|
|
231
232
|
const byteOffset = requireCount(exact.byteOffset, 'Session transcript fragment byte offset');
|
|
232
233
|
const totalBytes = requireCount(exact.totalBytes, 'Session transcript fragment total bytes');
|
|
233
234
|
const data = requireBase64Fragment(exact.data);
|
|
234
|
-
const dataBytes = Buffer.
|
|
235
|
+
const dataBytes = Buffer.byteLength(data, 'base64');
|
|
235
236
|
if (totalBytes === 0 ||
|
|
236
237
|
dataBytes === 0 ||
|
|
237
238
|
byteOffset >= totalBytes ||
|
|
@@ -42,6 +42,7 @@ const QUERY_ERRORS = [
|
|
|
42
42
|
'persistence_failed',
|
|
43
43
|
'internal_failure',
|
|
44
44
|
];
|
|
45
|
+
const INVOCABLE_QUERY_ERRORS = [...QUERY_ERRORS, 'not_found', 'session_archived'];
|
|
45
46
|
const MUTATION_ERRORS = [...QUERY_ERRORS, 'commit_outcome_unknown'];
|
|
46
47
|
export const SKILL_CATALOG_OPERATION_SPECS = {
|
|
47
48
|
'skill.catalog.query': defineHostPathOperation({
|
|
@@ -54,7 +55,7 @@ export const SKILL_CATALOG_OPERATION_SPECS = {
|
|
|
54
55
|
'skill.catalog.invocable.query': defineHostPathOperation({
|
|
55
56
|
mode: 'query',
|
|
56
57
|
availability: 'ready',
|
|
57
|
-
errors:
|
|
58
|
+
errors: INVOCABLE_QUERY_ERRORS,
|
|
58
59
|
decodeInput: decodeInvocableQueryInput,
|
|
59
60
|
decodeOutput: decodeInvocableQueryResult,
|
|
60
61
|
}, (input) => input.target.kind === 'new_session' && input.target.context.workspace.kind === 'host_path'),
|
|
@@ -182,8 +182,10 @@ function decodeWorkHubCoordinationActionFields(input) {
|
|
|
182
182
|
const proposal = decodeWorkHubCoordinationProposal(input.proposal);
|
|
183
183
|
if (input.newWorkDefaults !== undefined &&
|
|
184
184
|
(!isWorkHubCreateDefaults(input.newWorkDefaults) ||
|
|
185
|
-
!(proposal.disposition === 'create_new' ||
|
|
186
|
-
(
|
|
185
|
+
!(('disposition' in proposal && proposal.disposition === 'create_new') ||
|
|
186
|
+
('operation' in proposal &&
|
|
187
|
+
proposal.operation === 'correct' &&
|
|
188
|
+
proposal.target.disposition === 'create_new')))) {
|
|
187
189
|
throw invalidProtocolFrame('Invalid WorkHub creation defaults');
|
|
188
190
|
}
|
|
189
191
|
const delegationText = input.delegationText === undefined
|
|
@@ -191,9 +193,7 @@ function decodeWorkHubCoordinationActionFields(input) {
|
|
|
191
193
|
: requireUtf8String(input.delegationText, 'WorkHub delegation text', WORKHUB_COORDINATION_TEXT_MAX_BYTES);
|
|
192
194
|
if (delegationText !== undefined &&
|
|
193
195
|
(!delegationText.trim() ||
|
|
194
|
-
(proposal.
|
|
195
|
-
proposal.disposition !== 'create_new' &&
|
|
196
|
-
proposal.disposition !== 'replace'))) {
|
|
196
|
+
!('disposition' in proposal || ('operation' in proposal && proposal.operation === 'correct')))) {
|
|
197
197
|
throw invalidProtocolFrame('Invalid WorkHub delegation text');
|
|
198
198
|
}
|
|
199
199
|
const base = {
|
|
@@ -204,19 +204,19 @@ function decodeWorkHubCoordinationActionFields(input) {
|
|
|
204
204
|
: {}),
|
|
205
205
|
...(delegationText === undefined ? {} : { delegationText }),
|
|
206
206
|
};
|
|
207
|
-
if (proposal.disposition === 'delegate_existing') {
|
|
207
|
+
if ('disposition' in proposal && proposal.disposition === 'delegate_existing') {
|
|
208
208
|
if (input.create !== undefined || input.candidateSetId === undefined) {
|
|
209
209
|
throw invalidProtocolFrame('Invalid WorkHub delegation context');
|
|
210
210
|
}
|
|
211
211
|
return { ...base, candidateSetId: candidateSetId(input.candidateSetId) };
|
|
212
212
|
}
|
|
213
|
-
if (proposal.disposition === 'create_new') {
|
|
213
|
+
if ('disposition' in proposal && proposal.disposition === 'create_new') {
|
|
214
214
|
if (input.candidateSetId !== undefined || input.create === undefined) {
|
|
215
215
|
throw invalidProtocolFrame('Invalid WorkHub creation context');
|
|
216
216
|
}
|
|
217
217
|
return { ...base, create: decodeWorkHubCoordinationCreateContext(input.create) };
|
|
218
218
|
}
|
|
219
|
-
if (proposal.
|
|
219
|
+
if ('operation' in proposal && proposal.operation === 'correct') {
|
|
220
220
|
if (proposal.target.disposition === 'delegate_existing') {
|
|
221
221
|
if (input.candidateSetId === undefined || input.create !== undefined) {
|
|
222
222
|
throw invalidProtocolFrame('Invalid WorkHub replacement context');
|
|
@@ -285,9 +285,9 @@ function decodeWorkHubCoordinationProposal(value) {
|
|
|
285
285
|
title: requireUtf8String(exact.title, 'WorkHub Session title', COORDINATION_TITLE_MAX_BYTES),
|
|
286
286
|
};
|
|
287
287
|
}
|
|
288
|
-
if (proposal.
|
|
288
|
+
if (proposal.operation === 'correct') {
|
|
289
289
|
const exact = requireExactRecord(proposal, 'WorkHub replacement proposal', [
|
|
290
|
-
'
|
|
290
|
+
'operation',
|
|
291
291
|
'replacesActionId',
|
|
292
292
|
'target',
|
|
293
293
|
]);
|
|
@@ -298,7 +298,7 @@ function decodeWorkHubCoordinationProposal(value) {
|
|
|
298
298
|
'candidateRef',
|
|
299
299
|
]);
|
|
300
300
|
return {
|
|
301
|
-
|
|
301
|
+
operation: 'correct',
|
|
302
302
|
replacesActionId: requireEntityId(exact.replacesActionId, 'WorkHub replaced action id'),
|
|
303
303
|
target: {
|
|
304
304
|
disposition: 'delegate_existing',
|
|
@@ -312,7 +312,7 @@ function decodeWorkHubCoordinationProposal(value) {
|
|
|
312
312
|
'title',
|
|
313
313
|
]);
|
|
314
314
|
return {
|
|
315
|
-
|
|
315
|
+
operation: 'correct',
|
|
316
316
|
replacesActionId: requireEntityId(exact.replacesActionId, 'WorkHub replaced action id'),
|
|
317
317
|
target: {
|
|
318
318
|
disposition: 'create_new',
|
|
@@ -322,29 +322,31 @@ function decodeWorkHubCoordinationProposal(value) {
|
|
|
322
322
|
}
|
|
323
323
|
throw invalidProtocolFrame('Invalid WorkHub replacement target');
|
|
324
324
|
}
|
|
325
|
-
if (proposal.
|
|
326
|
-
const exact = requireExactRecord(proposal, 'WorkHub stop proposal', ['
|
|
325
|
+
if (proposal.operation === 'stop') {
|
|
326
|
+
const exact = requireExactRecord(proposal, 'WorkHub stop proposal', ['operation', 'expects']);
|
|
327
327
|
return {
|
|
328
|
-
|
|
329
|
-
expects:
|
|
328
|
+
operation: 'stop',
|
|
329
|
+
expects: decodeWorkHubCoordinationLinkedTargetPreconditions(exact.expects),
|
|
330
330
|
};
|
|
331
331
|
}
|
|
332
|
-
if (proposal.
|
|
332
|
+
if (proposal.operation === 'resume') {
|
|
333
333
|
const exact = requireExactRecord(proposal, 'WorkHub resume proposal', [
|
|
334
|
-
'
|
|
334
|
+
'operation',
|
|
335
335
|
'expects',
|
|
336
336
|
'resumesActionId',
|
|
337
337
|
]);
|
|
338
338
|
return {
|
|
339
|
-
|
|
339
|
+
operation: 'resume',
|
|
340
340
|
resumesActionId: requireEntityId(exact.resumesActionId, 'WorkHub resume assignment'),
|
|
341
|
-
expects:
|
|
341
|
+
expects: decodeWorkHubCoordinationLinkedTargetPreconditions(exact.expects),
|
|
342
342
|
};
|
|
343
343
|
}
|
|
344
|
-
throw invalidProtocolFrame('Invalid WorkHub Coordination proposal
|
|
344
|
+
throw invalidProtocolFrame('Invalid WorkHub Coordination proposal');
|
|
345
345
|
}
|
|
346
|
-
function
|
|
347
|
-
const expects = requireExactRecord(value, 'WorkHub
|
|
346
|
+
function decodeWorkHubCoordinationLinkedTargetPreconditions(value) {
|
|
347
|
+
const expects = requireExactRecord(value, 'WorkHub linked-target preconditions', [
|
|
348
|
+
'targetSessionId',
|
|
349
|
+
]);
|
|
348
350
|
return {
|
|
349
351
|
targetSessionId: requireEntityId(expects.targetSessionId, 'WorkHub target Session id'),
|
|
350
352
|
};
|
|
@@ -85,6 +85,8 @@ export class HostArtifactCoordinator {
|
|
|
85
85
|
async #ingest(input, context) {
|
|
86
86
|
try {
|
|
87
87
|
if ((await this.#sessions.probeSessionRemoval(input.sessionId)).kind !== 'present') {
|
|
88
|
+
// Session removal can race an upload; release only this owner's staged bytes.
|
|
89
|
+
this.#uploads.abort(uploadKey(input.sessionId, input.uploadId), context);
|
|
88
90
|
return ingestFailure('not_found', 'Session was not found');
|
|
89
91
|
}
|
|
90
92
|
switch (input.kind) {
|
|
@@ -101,8 +101,8 @@ export class HostClientCapabilityCoordinator {
|
|
|
101
101
|
},
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
-
async bindSession(sessionId, initiatingConnectionId) {
|
|
105
|
-
return this.#bindSession(sessionId, initiatingConnectionId, 'strict');
|
|
104
|
+
async bindSession(sessionId, initiatingConnectionId, requiredToolNames) {
|
|
105
|
+
return this.#bindSession(sessionId, initiatingConnectionId, 'strict', requiredToolNames);
|
|
106
106
|
}
|
|
107
107
|
async bindSessionSuccessor(sessionId) {
|
|
108
108
|
const result = await this.#bindSession(sessionId, '', 'degrade');
|
|
@@ -129,17 +129,61 @@ export class HostClientCapabilityCoordinator {
|
|
|
129
129
|
this.#onModelToolsChanged();
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
|
-
async #bindSession(sessionId, initiatingConnectionId, mode) {
|
|
132
|
+
async #bindSession(sessionId, initiatingConnectionId, mode, requiredToolNames) {
|
|
133
133
|
return this.#activation.runMutation(async () => {
|
|
134
134
|
const selection = this.#selectSessionState(sessionId, initiatingConnectionId, mode);
|
|
135
135
|
if (!selection.ok)
|
|
136
136
|
return selection;
|
|
137
|
+
const capabilityBinding = requiredToolNames
|
|
138
|
+
? this.#toolProviderBinding(selection.state, requiredToolNames)
|
|
139
|
+
: undefined;
|
|
140
|
+
if (requiredToolNames && !capabilityBinding) {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
message: 'Required Client Capability tools do not have one available provider',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
137
146
|
this.#storeSessionState(sessionId, selection.state);
|
|
138
147
|
if (selection.modelToolsChanged)
|
|
139
148
|
this.#onModelToolsChanged();
|
|
140
|
-
return { ok: true };
|
|
149
|
+
return { ok: true, ...(capabilityBinding ? { capabilityBinding } : {}) };
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async bindRecoveredSession(sessionId, binding, toolNames) {
|
|
153
|
+
return this.#activation.runMutation(() => {
|
|
154
|
+
const provider = [...this.#providers.values()].find((candidate) => providerAuthorityDigest(candidate) === binding && this.#activeConnection(candidate));
|
|
155
|
+
if (!provider)
|
|
156
|
+
return false;
|
|
157
|
+
const connection = this.#activeConnection(provider);
|
|
158
|
+
const selection = this.#selectSessionState(sessionId, connection.connectionId, 'strict');
|
|
159
|
+
if (!selection.ok || this.#toolProviderBinding(selection.state, toolNames) !== binding)
|
|
160
|
+
return false;
|
|
161
|
+
this.#storeSessionState(sessionId, selection.state);
|
|
162
|
+
if (selection.modelToolsChanged)
|
|
163
|
+
this.#onModelToolsChanged();
|
|
164
|
+
return true;
|
|
141
165
|
});
|
|
142
166
|
}
|
|
167
|
+
#toolProviderBinding(state, toolNames) {
|
|
168
|
+
const missing = new Set(toolNames);
|
|
169
|
+
let selected;
|
|
170
|
+
for (const [contractId, binding] of state?.sessionBindings ?? []) {
|
|
171
|
+
if (binding.kind !== 'bound')
|
|
172
|
+
continue;
|
|
173
|
+
const provider = this.#providers.get(binding.providerId);
|
|
174
|
+
const offer = provider?.current?.offersByContract.get(contractId);
|
|
175
|
+
if (!provider || !this.#activeConnection(provider) || !offer)
|
|
176
|
+
continue;
|
|
177
|
+
for (const tool of offer.offer.tools) {
|
|
178
|
+
if (!missing.delete(mcpProxyToolName(tool.serverId, tool.name)))
|
|
179
|
+
continue;
|
|
180
|
+
if (selected && selected !== provider)
|
|
181
|
+
return undefined;
|
|
182
|
+
selected = provider;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return selected && missing.size === 0 ? providerAuthorityDigest(selected) : undefined;
|
|
186
|
+
}
|
|
143
187
|
async runWithSessionBindingPreview(sessionId, initiatingConnectionId, operation) {
|
|
144
188
|
return this.#activation.runReadActivation(async () => {
|
|
145
189
|
const registryRevision = this.#revision;
|
|
@@ -1274,3 +1318,17 @@ function sessionCapabilityStatesEqual(left, right) {
|
|
|
1274
1318
|
function asError(value) {
|
|
1275
1319
|
return value instanceof Error ? value : new Error(String(value));
|
|
1276
1320
|
}
|
|
1321
|
+
/** Registration IDs and connections rotate; authenticated provider authority must not. */
|
|
1322
|
+
function providerAuthorityDigest(provider) {
|
|
1323
|
+
return `sha256:${createHash('sha256')
|
|
1324
|
+
.update(JSON.stringify([
|
|
1325
|
+
'maka.client-capability-authority.v1',
|
|
1326
|
+
provider.principalKind,
|
|
1327
|
+
provider.principalId,
|
|
1328
|
+
provider.clientInstanceId,
|
|
1329
|
+
provider.credentialBoundClientInstanceId ?? null,
|
|
1330
|
+
provider.capabilityOwner?.principalId ?? null,
|
|
1331
|
+
provider.capabilityOwner?.clientInstanceId ?? null,
|
|
1332
|
+
]))
|
|
1333
|
+
.digest('hex')}`;
|
|
1334
|
+
}
|