maka-agent 0.2.0-dev.46.20260922 → 0.2.0-dev.48.20260923
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/DISCLAIMER-WIP +6 -1
- package/README.md +6 -1
- package/dist/acp/maka-acp-agent.js +1 -1
- package/dist/acp/session-mcp.js +212 -0
- package/dist/acp/session-registry.js +52 -8
- package/dist/acp/stdio-server.js +3 -0
- package/dist/cli-core.js +1 -1
- package/dist/mcp-capability-provider.js +6 -1
- package/dist/mcp-capability-publication.js +139 -0
- package/dist/pi-tui-layout.js +46 -17
- package/dist/pi-tui-pickers.js +25 -14
- package/dist/pi-tui-runner.js +24 -15
- package/dist/runtime-host-capability-provider-command.js +25 -6
- package/dist/runtime-host-service-manager.js +26 -2
- package/dist/tui-context-refresh.js +15 -26
- package/dist/tui-mcp-control.js +66 -112
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-errors.js +26 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-filesystem.js +103 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-process.js +119 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-projection.js +146 -0
- package/node_modules/@maka/acp-executor-plugin/dist/index.js +653 -0
- package/node_modules/@maka/acp-executor-plugin/dist/plugin.mjs +24446 -0
- package/node_modules/@maka/acp-executor-plugin/package.json +15 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/index.js +88 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/model-presentation.js +53 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/plugin.mjs +115 -0
- package/node_modules/@maka/antigravity-acp-plugin/package.json +15 -0
- package/node_modules/@maka/core/dist/client-capability-grant.js +2 -2
- package/node_modules/@maka/core/dist/executor-catalog.js +106 -0
- package/node_modules/@maka/core/dist/mcp.js +4 -1
- package/node_modules/@maka/core/dist/model-metadata.js +28 -1
- package/node_modules/@maka/core/dist/model-thinking.js +36 -1
- package/node_modules/@maka/core/dist/provider-registry.js +0 -1
- package/node_modules/@maka/core/dist/refresh-read-coordinator.js +62 -0
- package/node_modules/@maka/core/dist/runtime-event-store.js +25 -0
- package/node_modules/@maka/core/dist/runtime-invocation.js +5 -0
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +4 -0
- package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +6 -0
- package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +21 -0
- package/node_modules/@maka/core/dist/session.js +19 -1
- package/node_modules/@maka/core/dist/settings.js +3 -0
- package/node_modules/@maka/core/dist/tool-ledger-scanner.js +365 -104
- package/node_modules/@maka/core/dist/turn-origin.js +14 -0
- package/node_modules/@maka/core/package.json +3 -0
- package/node_modules/@maka/mcp/dist/credential-coordinator.js +6 -1
- package/node_modules/@maka/mcp/dist/index.js +31 -5
- package/node_modules/@maka/mcp/dist/oauth.js +54 -6
- package/node_modules/@maka/runtime/dist/agent-run-inspect.js +1 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +15 -60
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +18 -0
- package/node_modules/@maka/runtime/dist/apply-patch-profile.js +64 -14
- package/node_modules/@maka/runtime/dist/builtin-tools.js +3 -2
- package/node_modules/@maka/runtime/dist/codex-v4a-patch.js +18 -0
- package/node_modules/@maka/runtime/dist/conversation-copy.js +56 -1
- package/node_modules/@maka/runtime/dist/mcp-tools.js +9 -2
- package/node_modules/@maka/runtime/dist/model-adapter.js +80 -3
- package/node_modules/@maka/runtime/dist/model-runtime.js +6 -0
- package/node_modules/@maka/runtime/dist/openai-apply-patch.js +5 -0
- package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +122 -3
- package/node_modules/@maka/runtime/dist/plugin-executor-service.js +212 -16
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +2 -2
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +184 -126
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +3 -0
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +29 -4
- package/node_modules/@maka/runtime-host/dist/candidate-entry.js +8 -5
- package/node_modules/@maka/runtime-host/dist/client/capability-provider-service.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +59 -20
- package/node_modules/@maka/runtime-host/dist/client/connection.js +6 -4
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +8 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnecting-connection.js +4 -4
- package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -1
- package/node_modules/@maka/runtime-host/dist/composition-identity.js +19 -0
- package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +20 -3
- package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +8 -5
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +20 -2
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +34 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +75 -5
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +1 -5
- package/node_modules/@maka/runtime-host/dist/server/builtin-external-agent-plugins.js +188 -0
- package/node_modules/@maka/runtime-host/dist/server/candidate.js +12 -7
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +234 -70
- package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +14 -8
- package/node_modules/@maka/runtime-host/dist/server/client-capability-recovery.js +44 -46
- package/node_modules/@maka/runtime-host/dist/server/context-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-composition-factory.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +188 -21
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/extension-bundle.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +29 -7
- package/node_modules/@maka/runtime-host/dist/server/host-composition-source.js +47 -0
- package/node_modules/@maka/runtime-host/dist/server/host-composition.js +1 -29
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +2 -9
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +75 -9
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +5 -1
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +45 -0
- package/node_modules/@maka/runtime-host/dist/server/jev-routing-model.js +157 -0
- package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +11 -1
- package/node_modules/@maka/runtime-host/dist/server/process-lifecycle.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/process-termination-error.js +27 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +91 -12
- package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/session-bundle-coordinator.js +4 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +128 -13
- package/node_modules/@maka/runtime-host/dist/server/session-effect-coordinator.js +12 -6
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +33 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +173 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +293 -0
- package/node_modules/@maka/runtime-host/package.json +2 -0
- package/node_modules/@maka/storage/dist/agent-run-store-contract.js +8 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +15 -3
- package/node_modules/@maka/storage/dist/mcp-config-store.js +91 -2
- package/node_modules/@maka/storage/dist/operational-state-store.js +15 -1
- package/node_modules/@maka/storage/dist/plan-authority.js +2 -0
- package/node_modules/@maka/storage/dist/plan-store.js +30 -0
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-policy/credential-vault-document.js +4 -1
- package/node_modules/@maka/storage/dist/runtime-policy/policy-document.js +2 -0
- package/node_modules/@maka/storage/dist/session-store-values.js +16 -4
- package/node_modules/@maka/storage/dist/shell-run-authority.js +1 -0
- package/node_modules/@maka/storage/dist/shell-run-store.js +15 -0
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +74 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +724 -84
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +16 -0
- package/package.json +1 -1
|
@@ -29,9 +29,18 @@ import { PluginPackageStoreError } from './plugin-package-store.js';
|
|
|
29
29
|
import { HostPluginPlatformError } from './plugin-platform.js';
|
|
30
30
|
export class HostPluginPlatformCoordinator {
|
|
31
31
|
platform;
|
|
32
|
+
queryExecutors;
|
|
32
33
|
#streams = new Map();
|
|
33
34
|
#pendingStreamOpens = new Map();
|
|
34
35
|
handlers = {
|
|
36
|
+
'plugin.executor.query': async (input) => {
|
|
37
|
+
try {
|
|
38
|
+
return { ok: true, result: { items: await this.queryExecutors(input) } };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return failure(error);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
35
44
|
'plugin.client.query': (input) => this.#client(input),
|
|
36
45
|
'plugin.client.remote.call': (input, context) => this.#remoteCall(input, context),
|
|
37
46
|
'plugin.client.remote.stream.open': (input, context) => this.#remoteStreamOpen(input, context),
|
|
@@ -45,8 +54,9 @@ export class HostPluginPlatformCoordinator {
|
|
|
45
54
|
'plugin.composition.apply': (input) => this.#apply(input),
|
|
46
55
|
'plugin.platform.reconcile': () => this.#reconcile(),
|
|
47
56
|
};
|
|
48
|
-
constructor(platform) {
|
|
57
|
+
constructor(platform, queryExecutors = async () => []) {
|
|
49
58
|
this.platform = platform;
|
|
59
|
+
this.queryExecutors = queryExecutors;
|
|
50
60
|
}
|
|
51
61
|
releaseConnection(connectionId) {
|
|
52
62
|
for (const [streamId, stream] of this.#streams) {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
import { RuntimeHostProcessTerminationRequiredError
|
|
19
|
+
import { RuntimeHostProcessTerminationRequiredError } from './process-termination-error.js';
|
|
20
20
|
export async function runRuntimeHostProcessLifecycle(host, options = {}) {
|
|
21
21
|
let closing = false;
|
|
22
22
|
const close = () => {
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
export class RuntimeHostProcessTerminationRequiredError extends Error {
|
|
20
|
+
shutdownGraceMs;
|
|
21
|
+
code = 'process_termination_required';
|
|
22
|
+
constructor(shutdownGraceMs) {
|
|
23
|
+
super(`Runtime Host did not shut down within ${shutdownGraceMs} ms`);
|
|
24
|
+
this.shutdownGraceMs = shutdownGraceMs;
|
|
25
|
+
this.name = 'RuntimeHostProcessTerminationRequiredError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -966,10 +966,10 @@ export class RootTurnCoordinator {
|
|
|
966
966
|
};
|
|
967
967
|
if (!(await this.bindRecoveryCapabilities(input.sessionId, execution)))
|
|
968
968
|
return { deferred: true };
|
|
969
|
-
execution = await this.prepareFreshWorkHubExecution(header, turnId, input.content, execution);
|
|
970
969
|
const unavailableReason = runtimeHostExecutionUnavailableReason(header, execution);
|
|
971
970
|
if (unavailableReason)
|
|
972
971
|
return { error: unavailableReason };
|
|
972
|
+
execution = await this.prepareFreshWorkHubExecution(header, turnId, input.content, execution);
|
|
973
973
|
const reservation = this.reserveRootTurn(input.sessionId);
|
|
974
974
|
if (!reservation)
|
|
975
975
|
return { error: 'Another root Turn is being admitted' };
|
|
@@ -1011,18 +1011,19 @@ export class RootTurnCoordinator {
|
|
|
1011
1011
|
});
|
|
1012
1012
|
}
|
|
1013
1013
|
async prepareFreshWorkHubExecution(header, turnId, content, execution, inputClosedSignal) {
|
|
1014
|
-
if (execution.kind !== 'workhub_coordination' ||
|
|
1014
|
+
if (execution.kind !== 'workhub_coordination' ||
|
|
1015
|
+
execution.feedback ||
|
|
1016
|
+
execution.operation === 'action' ||
|
|
1017
|
+
!this.prepareWorkHubRoutingDecision) {
|
|
1015
1018
|
return execution;
|
|
1016
1019
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}),
|
|
1025
|
-
};
|
|
1020
|
+
const routingDecision = await this.prepareWorkHubRoutingDecision({
|
|
1021
|
+
header,
|
|
1022
|
+
turnId,
|
|
1023
|
+
content,
|
|
1024
|
+
...(inputClosedSignal ? { inputClosedSignal } : {}),
|
|
1025
|
+
});
|
|
1026
|
+
return routingDecision === undefined ? execution : { ...execution, routingDecision };
|
|
1026
1027
|
}
|
|
1027
1028
|
prepareMessage(input) {
|
|
1028
1029
|
return this.runCommand(async () => {
|
|
@@ -1160,6 +1161,70 @@ export class RootTurnCoordinator {
|
|
|
1160
1161
|
}
|
|
1161
1162
|
return this.startRootMessage(request, context);
|
|
1162
1163
|
}
|
|
1164
|
+
/** Result delivery uses the same durable root admission as ordinary WorkHub turns.
|
|
1165
|
+
* The deterministic Turn identity is the delivery receipt, including after a crash.
|
|
1166
|
+
* Content is re-read under both Session admissions immediately before committing.
|
|
1167
|
+
*/
|
|
1168
|
+
startWorkHubResult(origin, prepareContent) {
|
|
1169
|
+
return this.runCommand(async () => {
|
|
1170
|
+
const sessionId = WORKHUB_COORDINATION_SESSION_ID;
|
|
1171
|
+
const turnId = origin.eventId;
|
|
1172
|
+
let disposition;
|
|
1173
|
+
const result = await this.sessionAdmission.runMany([sessionId, origin.targetSessionId], async (lease) => {
|
|
1174
|
+
const previousDelivery = await this.stores.agentRunStore.readRootTurnAdmission(sessionId, turnId);
|
|
1175
|
+
if (previousDelivery) {
|
|
1176
|
+
if (previousDelivery.execution.kind !== 'workhub_coordination' ||
|
|
1177
|
+
!isDeepStrictEqual(previousDelivery.execution.feedback, origin))
|
|
1178
|
+
throw new Error('WorkHub feedback identity conflict');
|
|
1179
|
+
return 'delivered';
|
|
1180
|
+
}
|
|
1181
|
+
if (!this.isSessionExecutionIdle(sessionId))
|
|
1182
|
+
return 'pending';
|
|
1183
|
+
const content = await prepareContent(lease);
|
|
1184
|
+
if (!content)
|
|
1185
|
+
return 'obsolete';
|
|
1186
|
+
const previous = this.rootAdmissionOwner.latestAdmission(sessionId)?.execution;
|
|
1187
|
+
const execution = {
|
|
1188
|
+
kind: 'workhub_coordination',
|
|
1189
|
+
inputDigest: messageContentDigest(content),
|
|
1190
|
+
feedback: origin,
|
|
1191
|
+
...(previous?.kind === 'workhub_coordination' && previous.capabilityBinding
|
|
1192
|
+
? { capabilityBinding: previous.capabilityBinding }
|
|
1193
|
+
: {}),
|
|
1194
|
+
};
|
|
1195
|
+
if (!(await this.bindRecoveryCapabilities(sessionId, execution)))
|
|
1196
|
+
return 'pending';
|
|
1197
|
+
const header = await this.stores.sessionStore.readHeaderSnapshot(sessionId);
|
|
1198
|
+
if (runtimeHostExecutionUnavailableReason(header, execution))
|
|
1199
|
+
return 'pending';
|
|
1200
|
+
const reservation = this.reserveRootTurn(sessionId);
|
|
1201
|
+
if (!reservation)
|
|
1202
|
+
return 'pending';
|
|
1203
|
+
try {
|
|
1204
|
+
if (!this.beginRootAdmission(reservation))
|
|
1205
|
+
return 'pending';
|
|
1206
|
+
const admitted = await this.rootAdmissionOwner.admitRootTurn({
|
|
1207
|
+
sessionId,
|
|
1208
|
+
turnId,
|
|
1209
|
+
proposedRunId: randomUUID(),
|
|
1210
|
+
proposedUserMessageId: turnId,
|
|
1211
|
+
execution,
|
|
1212
|
+
normalizedInput: content,
|
|
1213
|
+
sourceMessages: [],
|
|
1214
|
+
admittedAt: Date.now(),
|
|
1215
|
+
});
|
|
1216
|
+
disposition = await this.prepareAdmittedTurn(activationInputForAdmission(admitted.admission), admitted.admission, this.acquireRecoveryResidency, lease, undefined, undefined, reservation);
|
|
1217
|
+
return 'delivered';
|
|
1218
|
+
}
|
|
1219
|
+
finally {
|
|
1220
|
+
this.releaseRootReservation(reservation);
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
if (disposition)
|
|
1224
|
+
await this.resolveStartDisposition({ sessionId, turnId }, disposition);
|
|
1225
|
+
return result;
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1163
1228
|
async runWorkHubCoordinationOperation(request, context) {
|
|
1164
1229
|
if (request.execution.operation !== 'action' || !request.operation) {
|
|
1165
1230
|
return operationUnavailable('Coordination action requires a Host operation');
|
|
@@ -1243,9 +1308,23 @@ export class RootTurnCoordinator {
|
|
|
1243
1308
|
admission.execution.kind !== 'workhub_coordination' ||
|
|
1244
1309
|
header.toolProfile !== 'workhub-coordination-v2')
|
|
1245
1310
|
return undefined;
|
|
1311
|
+
let content = requireHostedExecutionMessageContent(admission);
|
|
1312
|
+
if (admission.execution.feedback) {
|
|
1313
|
+
const feedback = admission.execution.feedback;
|
|
1314
|
+
const assignments = await this.stores.sessionStore.readActiveWorkHubAssignmentsByTarget([
|
|
1315
|
+
feedback.targetSessionId,
|
|
1316
|
+
]);
|
|
1317
|
+
const assignment = assignments.find((a) => a.actionId === feedback.actionId && a.delegationId === feedback.delegationId);
|
|
1318
|
+
if (!assignment)
|
|
1319
|
+
return undefined;
|
|
1320
|
+
content = normalizeMessageContent({
|
|
1321
|
+
text: assignment.userText,
|
|
1322
|
+
...(assignment.attachments ? { attachments: assignment.attachments } : {}),
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1246
1325
|
return {
|
|
1247
1326
|
runId: active.runId,
|
|
1248
|
-
content
|
|
1327
|
+
content,
|
|
1249
1328
|
...(admission.execution.routingDecision
|
|
1250
1329
|
? { decision: admission.execution.routingDecision }
|
|
1251
1330
|
: {}),
|
|
@@ -453,6 +453,8 @@ function sameLocator(left, right) {
|
|
|
453
453
|
return right.scope === 'connection' && left.connectionId === right.connectionId;
|
|
454
454
|
case 'web_search':
|
|
455
455
|
return right.scope === 'web_search' && left.provider === right.provider;
|
|
456
|
+
case 'jev':
|
|
457
|
+
return right.scope === 'jev';
|
|
456
458
|
case 'network_proxy':
|
|
457
459
|
return right.scope === 'network_proxy';
|
|
458
460
|
}
|
|
@@ -71,7 +71,10 @@ export class HostSessionBundleCoordinator {
|
|
|
71
71
|
}
|
|
72
72
|
async #fenced(sessionId, operation) {
|
|
73
73
|
try {
|
|
74
|
-
return await this.#options.fenceSubtree(sessionId,
|
|
74
|
+
return await this.#options.fenceSubtree(sessionId, async (fencedSessionIds) => {
|
|
75
|
+
await this.#options.recoverInterruptedSessions(fencedSessionIds);
|
|
76
|
+
return operation(fencedSessionIds);
|
|
77
|
+
});
|
|
75
78
|
}
|
|
76
79
|
catch (error) {
|
|
77
80
|
// Two types, because the Session manager translates on the way out: the
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
+
import { isExecutorConfiguration } from '@maka/core/executor-catalog';
|
|
19
20
|
import { JsonArrayPageBudget } from './json-array-page-budget.js';
|
|
20
21
|
import { RuntimeHostProtocolError } from '../protocol/errors.js';
|
|
21
22
|
import { createHash } from 'node:crypto';
|
|
@@ -150,6 +151,8 @@ export class HostSessionCatalogCoordinator {
|
|
|
150
151
|
#continuity;
|
|
151
152
|
#workspaceResolver;
|
|
152
153
|
#requestDrain;
|
|
154
|
+
#configureExecutor;
|
|
155
|
+
#retireExecutor;
|
|
153
156
|
#assertExecutorAvailable;
|
|
154
157
|
#sessionAccessAuthority;
|
|
155
158
|
constructor(options) {
|
|
@@ -161,6 +164,8 @@ export class HostSessionCatalogCoordinator {
|
|
|
161
164
|
this.#continuity = options.continuity;
|
|
162
165
|
this.#workspaceResolver = options.workspaceResolver;
|
|
163
166
|
this.#requestDrain = options.requestDrain;
|
|
167
|
+
this.#configureExecutor = options.configureExecutor;
|
|
168
|
+
this.#retireExecutor = options.retireExecutor;
|
|
164
169
|
this.#assertExecutorAvailable = options.assertExecutorAvailable;
|
|
165
170
|
this.#sessionAccessAuthority = options.sessionAccessAuthority;
|
|
166
171
|
}
|
|
@@ -221,7 +226,7 @@ export class HostSessionCatalogCoordinator {
|
|
|
221
226
|
const prepared = await prepareCreate(input);
|
|
222
227
|
return this.#workspaceResolver.runWithUsageRecorded(input.workspace, async (workspace) => {
|
|
223
228
|
const [model, policy] = await Promise.all([
|
|
224
|
-
this.#resolveCreateExecution(input),
|
|
229
|
+
this.#resolveCreateExecution(input, workspace.cwd),
|
|
225
230
|
this.#readRuntimePolicy(),
|
|
226
231
|
]);
|
|
227
232
|
return {
|
|
@@ -232,7 +237,12 @@ export class HostSessionCatalogCoordinator {
|
|
|
232
237
|
...(workspace.projectId === null ? {} : { projectId: workspace.projectId }),
|
|
233
238
|
name: prepared.name,
|
|
234
239
|
labels: [...prepared.labels],
|
|
235
|
-
...(model.executorId
|
|
240
|
+
...(model.executorId
|
|
241
|
+
? {
|
|
242
|
+
executorId: model.executorId,
|
|
243
|
+
...(model.executorConfig ? { executorConfig: model.executorConfig } : {}),
|
|
244
|
+
}
|
|
245
|
+
: {}),
|
|
236
246
|
...(model.connectionId ? { llmConnectionId: model.connectionId } : {}),
|
|
237
247
|
llmConnectionSlug: model.connectionSlug,
|
|
238
248
|
model: model.model,
|
|
@@ -394,7 +404,7 @@ export class HostSessionCatalogCoordinator {
|
|
|
394
404
|
}
|
|
395
405
|
return await this.#workspaceResolver.runWithUsageRecorded(input.workspace, async (workspace) => {
|
|
396
406
|
const [model, policy] = await Promise.all([
|
|
397
|
-
this.#resolveCreateExecution(input),
|
|
407
|
+
this.#resolveCreateExecution(input, workspace.cwd),
|
|
398
408
|
this.#readRuntimePolicy(),
|
|
399
409
|
]);
|
|
400
410
|
const createInput = {
|
|
@@ -402,7 +412,12 @@ export class HostSessionCatalogCoordinator {
|
|
|
402
412
|
...(workspace.projectId === null ? {} : { projectId: workspace.projectId }),
|
|
403
413
|
name: prepared.name,
|
|
404
414
|
labels: [...prepared.labels],
|
|
405
|
-
...(model.executorId
|
|
415
|
+
...(model.executorId
|
|
416
|
+
? {
|
|
417
|
+
executorId: model.executorId,
|
|
418
|
+
...(model.executorConfig ? { executorConfig: model.executorConfig } : {}),
|
|
419
|
+
}
|
|
420
|
+
: {}),
|
|
406
421
|
...(model.connectionId ? { llmConnectionId: model.connectionId } : {}),
|
|
407
422
|
llmConnectionSlug: model.connectionSlug,
|
|
408
423
|
model: model.model,
|
|
@@ -480,6 +495,7 @@ export class HostSessionCatalogCoordinator {
|
|
|
480
495
|
}
|
|
481
496
|
return this.#admission.run(input.sessionId, async (lease) => {
|
|
482
497
|
let commitAttempted = false;
|
|
498
|
+
let confirmedExecutor;
|
|
483
499
|
try {
|
|
484
500
|
const current = await this.#stores.readHeaderRecordSnapshot(input.sessionId);
|
|
485
501
|
if (authority === 'workhub' &&
|
|
@@ -499,12 +515,28 @@ export class HostSessionCatalogCoordinator {
|
|
|
499
515
|
const configuration = await this.#mergeConfigurationPatch(current.header, input.patch);
|
|
500
516
|
const clearsConnectionBlock = input.patch.modelTarget !== undefined &&
|
|
501
517
|
current.header.blockedReason === 'NO_REAL_CONNECTION';
|
|
502
|
-
if (!clearsConnectionBlock &&
|
|
518
|
+
if (!clearsConnectionBlock &&
|
|
519
|
+
!input.patch.executorConfig &&
|
|
520
|
+
sessionConfigurationMatches(current.header, configuration)) {
|
|
503
521
|
return configurationSuccess({
|
|
504
522
|
kind: 'committed',
|
|
505
523
|
session: projectSessionCatalogRecord(await this.#stores.readCatalogRecord(input.sessionId, authority === 'workhub' ? 'recoverable' : 'ordinary')),
|
|
506
524
|
});
|
|
507
525
|
}
|
|
526
|
+
if (input.patch.executorConfig) {
|
|
527
|
+
if (this.#manager.runningTurnIds(input.sessionId).length ||
|
|
528
|
+
current.header.status === 'waiting_for_user')
|
|
529
|
+
throw new SessionOperationFailure('operation_conflict', 'Executor model can only change while idle');
|
|
530
|
+
if (!this.#configureExecutor)
|
|
531
|
+
throw new SessionOperationFailure('operation_unavailable', 'Executor configuration is unavailable');
|
|
532
|
+
try {
|
|
533
|
+
await this.#configureExecutor(current.header, input.patch.executorConfig);
|
|
534
|
+
confirmedExecutor = current.header;
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
throw new SessionOperationFailure('operation_unavailable', 'The external agent did not confirm the model change. Retry while idle or start a new task.');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
508
540
|
commitAttempted = true;
|
|
509
541
|
await this.#manager.transitionSessionConfiguration(input.sessionId, {
|
|
510
542
|
expectedRevision: input.expectedRevision,
|
|
@@ -515,6 +547,8 @@ export class HostSessionCatalogCoordinator {
|
|
|
515
547
|
return configurationSuccess(await this.#committedUpdate(input.sessionId, lease, authority === 'workhub' ? 'recoverable' : 'ordinary'));
|
|
516
548
|
}
|
|
517
549
|
catch (error) {
|
|
550
|
+
if (confirmedExecutor)
|
|
551
|
+
await this.#reconcileExecutorAfterFailedCommit(confirmedExecutor);
|
|
518
552
|
if (!commitAttempted &&
|
|
519
553
|
!isNotFound(error) &&
|
|
520
554
|
!(error instanceof SessionMetadataVersionConflictError) &&
|
|
@@ -529,6 +563,28 @@ export class HostSessionCatalogCoordinator {
|
|
|
529
563
|
}
|
|
530
564
|
});
|
|
531
565
|
}
|
|
566
|
+
async #reconcileExecutorAfterFailedCommit(previous) {
|
|
567
|
+
try {
|
|
568
|
+
const actual = (await this.#stores.readHeaderRecordSnapshot(previous.id)).header;
|
|
569
|
+
if (actual.executorId !== previous.executorId ||
|
|
570
|
+
!actual.executorConfig?.model ||
|
|
571
|
+
!this.#configureExecutor)
|
|
572
|
+
throw new Error('Confirmed executor model is unavailable');
|
|
573
|
+
await this.#configureExecutor(actual, actual.executorConfig);
|
|
574
|
+
}
|
|
575
|
+
catch {
|
|
576
|
+
// When the durable value or rollback cannot be confirmed, never leave a
|
|
577
|
+
// live external Session that might answer on a different model.
|
|
578
|
+
try {
|
|
579
|
+
if (!this.#retireExecutor)
|
|
580
|
+
throw new Error('Executor retirement is unavailable');
|
|
581
|
+
await this.#retireExecutor(previous.id);
|
|
582
|
+
}
|
|
583
|
+
catch {
|
|
584
|
+
this.#requestDrain();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
532
588
|
async #relocateWorkspace(input) {
|
|
533
589
|
if (isWorkHubCoordinationSessionId(input.sessionId)) {
|
|
534
590
|
return workspaceFailure('operation_conflict', 'WorkHub Coordination Session workspace requires WorkHub authority');
|
|
@@ -839,17 +895,50 @@ export class HostSessionCatalogCoordinator {
|
|
|
839
895
|
};
|
|
840
896
|
}
|
|
841
897
|
async #mergeConfigurationPatch(current, patch) {
|
|
842
|
-
if (current.
|
|
898
|
+
if (current.executorConfig && Object.keys(patch).some((key) => key !== 'executorConfig'))
|
|
899
|
+
throw new SessionOperationFailure('operation_unavailable', 'Native execution settings require a new Maka task');
|
|
900
|
+
if (patch.executorConfig &&
|
|
901
|
+
(!current.executorId ||
|
|
902
|
+
patch.executorTarget !== undefined ||
|
|
903
|
+
patch.modelTarget !== undefined ||
|
|
904
|
+
!isExecutorConfiguration(patch.executorConfig)))
|
|
905
|
+
throw new SessionOperationFailure('invalid_request', 'Invalid executor configuration');
|
|
906
|
+
if (current.backend === 'fake' &&
|
|
907
|
+
patch.modelTarget === undefined &&
|
|
908
|
+
patch.executorTarget === undefined) {
|
|
843
909
|
throw new SessionOperationFailure('operation_conflict', 'Legacy test backend configuration requires an explicit account selection');
|
|
844
910
|
}
|
|
845
911
|
if (current.backend !== 'plugin-executor' &&
|
|
846
912
|
current.llmConnectionId === undefined &&
|
|
847
|
-
patch.modelTarget === undefined
|
|
913
|
+
patch.modelTarget === undefined &&
|
|
914
|
+
patch.executorTarget === undefined) {
|
|
848
915
|
throw new SessionOperationFailure('operation_conflict', 'Legacy Session configuration requires an explicit account selection');
|
|
849
916
|
}
|
|
850
917
|
const thinkingLevel = patch.thinkingLevel === undefined
|
|
851
918
|
? current.thinkingLevel
|
|
852
919
|
: (patch.thinkingLevel ?? undefined);
|
|
920
|
+
if (patch.executorTarget !== undefined) {
|
|
921
|
+
let executorConfig;
|
|
922
|
+
try {
|
|
923
|
+
executorConfig = await this.#assertExecutorAvailable?.(current.id, patch.executorTarget.executorId, patch.executorTarget.model ? { model: patch.executorTarget.model } : undefined, current.cwd);
|
|
924
|
+
}
|
|
925
|
+
catch {
|
|
926
|
+
throw new SessionOperationFailure('operation_unavailable', `Plugin executor is unavailable: ${patch.executorTarget.executorId}`);
|
|
927
|
+
}
|
|
928
|
+
return {
|
|
929
|
+
backend: 'plugin-executor',
|
|
930
|
+
executorId: patch.executorTarget.executorId,
|
|
931
|
+
...(executorConfig ? { executorConfig } : {}),
|
|
932
|
+
llmConnectionId: undefined,
|
|
933
|
+
llmConnectionSlug: `executor:${patch.executorTarget.executorId}`,
|
|
934
|
+
model: patch.executorTarget.model ?? patch.executorTarget.executorId,
|
|
935
|
+
thinkingLevel,
|
|
936
|
+
connectionLocked: current.connectionLocked,
|
|
937
|
+
permissionMode: patch.permissionMode ?? current.permissionMode,
|
|
938
|
+
collaborationMode: patch.collaborationMode ?? current.collaborationMode ?? 'agent',
|
|
939
|
+
orchestrationMode: patch.orchestrationMode ?? current.orchestrationMode ?? 'default',
|
|
940
|
+
};
|
|
941
|
+
}
|
|
853
942
|
let model = {
|
|
854
943
|
...(current.llmConnectionId === undefined ? {} : { connectionId: current.llmConnectionId }),
|
|
855
944
|
connectionSlug: current.llmConnectionSlug,
|
|
@@ -870,9 +959,10 @@ export class HostSessionCatalogCoordinator {
|
|
|
870
959
|
return {
|
|
871
960
|
backend: patch.modelTarget || current.backend === 'ai-sdk' ? 'ai-sdk' : 'plugin-executor',
|
|
872
961
|
executorId: patch.modelTarget ? undefined : current.executorId,
|
|
962
|
+
executorConfig: patch.executorConfig ?? current.executorConfig,
|
|
873
963
|
llmConnectionId: model.connectionId,
|
|
874
964
|
llmConnectionSlug: model.connectionSlug,
|
|
875
|
-
model: model.model,
|
|
965
|
+
model: patch.executorConfig?.model ?? model.model,
|
|
876
966
|
thinkingLevel,
|
|
877
967
|
connectionLocked: patch.modelTarget === undefined ? current.connectionLocked : true,
|
|
878
968
|
permissionMode: patch.permissionMode ?? current.permissionMode,
|
|
@@ -880,18 +970,24 @@ export class HostSessionCatalogCoordinator {
|
|
|
880
970
|
orchestrationMode: patch.orchestrationMode ?? current.orchestrationMode ?? 'default',
|
|
881
971
|
};
|
|
882
972
|
}
|
|
883
|
-
async #resolveCreateExecution(input) {
|
|
973
|
+
async #resolveCreateExecution(input, cwd) {
|
|
884
974
|
if (input.executorId) {
|
|
975
|
+
const requestedModel = input.executorConfig?.model ?? input.executorModel;
|
|
976
|
+
let executorConfig;
|
|
885
977
|
try {
|
|
886
|
-
this.#assertExecutorAvailable?.(input.sessionId, input.executorId);
|
|
978
|
+
executorConfig = await this.#assertExecutorAvailable?.(input.sessionId, input.executorId, requestedModel ? { model: requestedModel } : input.executorConfig, cwd);
|
|
887
979
|
}
|
|
888
980
|
catch {
|
|
889
981
|
throw new SessionOperationFailure('operation_unavailable', `Plugin executor is unavailable: ${input.executorId}`);
|
|
890
982
|
}
|
|
891
983
|
return {
|
|
892
984
|
executorId: input.executorId,
|
|
985
|
+
...((executorConfig ?? input.executorConfig)
|
|
986
|
+
? { executorConfig: executorConfig ?? input.executorConfig }
|
|
987
|
+
: {}),
|
|
893
988
|
connectionSlug: `executor:${input.executorId}`,
|
|
894
|
-
model: input.executorId,
|
|
989
|
+
model: input.executorConfig?.model ?? input.executorModel ?? input.executorId,
|
|
990
|
+
thinkingLevel: input.thinkingLevel ?? undefined,
|
|
895
991
|
};
|
|
896
992
|
}
|
|
897
993
|
if (!input.modelTarget) {
|
|
@@ -911,6 +1007,7 @@ export class HostSessionCatalogCoordinator {
|
|
|
911
1007
|
function sessionConfigurationMatches(header, configuration) {
|
|
912
1008
|
return (header.backend === configuration.backend &&
|
|
913
1009
|
header.executorId === configuration.executorId &&
|
|
1010
|
+
header.executorConfig?.model === configuration.executorConfig?.model &&
|
|
914
1011
|
header.llmConnectionId === configuration.llmConnectionId &&
|
|
915
1012
|
header.llmConnectionSlug === configuration.llmConnectionSlug &&
|
|
916
1013
|
header.model === configuration.model &&
|
|
@@ -923,17 +1020,30 @@ function sessionConfigurationMatches(header, configuration) {
|
|
|
923
1020
|
function isPermissionModeOnlyPatch(patch) {
|
|
924
1021
|
return (patch.permissionMode !== undefined &&
|
|
925
1022
|
patch.modelTarget === undefined &&
|
|
1023
|
+
patch.executorTarget === undefined &&
|
|
1024
|
+
patch.executorConfig === undefined &&
|
|
926
1025
|
patch.thinkingLevel === undefined &&
|
|
927
1026
|
patch.collaborationMode === undefined &&
|
|
928
1027
|
patch.orchestrationMode === undefined);
|
|
929
1028
|
}
|
|
930
1029
|
async function prepareCreate(input) {
|
|
1030
|
+
if (input.executorConfig !== undefined &&
|
|
1031
|
+
(!input.executorId || !isExecutorConfiguration(input.executorConfig)))
|
|
1032
|
+
throw new SessionOperationFailure('invalid_request', 'Invalid executor configuration');
|
|
931
1033
|
if ((input.executorId === undefined) === (input.modelTarget === undefined)) {
|
|
932
1034
|
throw new SessionOperationFailure('invalid_request', 'Session creation requires exactly one model target or executor id');
|
|
933
1035
|
}
|
|
934
1036
|
if (input.executorId !== undefined && !isExecutorId(input.executorId)) {
|
|
935
1037
|
throw new SessionOperationFailure('invalid_request', 'Session executor id is invalid');
|
|
936
1038
|
}
|
|
1039
|
+
if (input.executorConfig?.model &&
|
|
1040
|
+
input.executorModel !== undefined &&
|
|
1041
|
+
input.executorConfig.model !== input.executorModel) {
|
|
1042
|
+
throw new SessionOperationFailure('invalid_request', 'Conflicting executor models');
|
|
1043
|
+
}
|
|
1044
|
+
if (input.executorModel !== undefined && input.executorId === undefined) {
|
|
1045
|
+
throw new SessionOperationFailure('invalid_request', 'Executor model requires an executor id');
|
|
1046
|
+
}
|
|
937
1047
|
if (input.labels?.some(isExecutionSemanticLabel)) {
|
|
938
1048
|
throw new SessionOperationFailure('invalid_request', 'Session creation cannot set reserved execution labels');
|
|
939
1049
|
}
|
|
@@ -960,7 +1070,7 @@ function createRequestFingerprint(input, prepared) {
|
|
|
960
1070
|
prepared.name,
|
|
961
1071
|
prepared.labels,
|
|
962
1072
|
input.executorId
|
|
963
|
-
? ['executor', input.executorId]
|
|
1073
|
+
? ['executor', input.executorId, input.executorConfig?.model ?? input.executorModel ?? null]
|
|
964
1074
|
: input.modelTarget?.kind === 'default'
|
|
965
1075
|
? ['default']
|
|
966
1076
|
: [
|
|
@@ -1036,7 +1146,12 @@ export function projectSessionCatalogRecord(record, liveRunState) {
|
|
|
1036
1146
|
...(header.revisionIndex === undefined ? {} : { revisionIndex: header.revisionIndex }),
|
|
1037
1147
|
...(header.revisionState === undefined ? {} : { revisionState: header.revisionState }),
|
|
1038
1148
|
backend: header.backend,
|
|
1039
|
-
...(header.executorId
|
|
1149
|
+
...(header.executorId
|
|
1150
|
+
? {
|
|
1151
|
+
executorId: header.executorId,
|
|
1152
|
+
...(header.executorConfig ? { executorConfig: header.executorConfig } : {}),
|
|
1153
|
+
}
|
|
1154
|
+
: {}),
|
|
1040
1155
|
llmConnectionId: header.llmConnectionId ?? null,
|
|
1041
1156
|
llmConnectionSlug: header.llmConnectionSlug,
|
|
1042
1157
|
connectionLocked: header.connectionLocked,
|
|
@@ -90,12 +90,15 @@ export class HostSessionEffectCoordinator {
|
|
|
90
90
|
const header = await this.#readSessionHeader(sessionId);
|
|
91
91
|
if (header.titleIsManual || header.name !== DEFAULT_SESSION_NAME)
|
|
92
92
|
return;
|
|
93
|
-
generated =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
generated =
|
|
94
|
+
header.backend === 'plugin-executor'
|
|
95
|
+
? undefined
|
|
96
|
+
: await this.#model.generateTitle({
|
|
97
|
+
sessionId,
|
|
98
|
+
header,
|
|
99
|
+
sourceText,
|
|
100
|
+
abortSignal,
|
|
101
|
+
});
|
|
99
102
|
}
|
|
100
103
|
catch {
|
|
101
104
|
// An unreachable title model is not a Session failure; the fallback name
|
|
@@ -195,6 +198,9 @@ export class HostSessionEffectCoordinator {
|
|
|
195
198
|
if (header.isArchived) {
|
|
196
199
|
return settledRecap(recapFailure('session_archived', 'Archived Session cannot generate a recap'));
|
|
197
200
|
}
|
|
201
|
+
if (header.backend === 'plugin-executor') {
|
|
202
|
+
return settledRecap(recapFailure('operation_unavailable', 'Plugin executor Sessions do not support native recap generation'));
|
|
203
|
+
}
|
|
198
204
|
const view = await this.#readModel.getSessionView(input.sessionId);
|
|
199
205
|
if (abort.signal.aborted) {
|
|
200
206
|
return settledRecap(recapFailure('host_draining', 'Runtime Host is draining'));
|
|
@@ -146,7 +146,7 @@ export class HostSessionRetirementCoordinator {
|
|
|
146
146
|
handles.goal.commit();
|
|
147
147
|
handles.scheduledTasks.commit();
|
|
148
148
|
await this.#graphWake.retireSessions(family.sessionIds);
|
|
149
|
-
this.#capabilities.retireSessions(family.sessionIds);
|
|
149
|
+
await this.#capabilities.retireSessions(family.sessionIds);
|
|
150
150
|
this.#messages.retireSessions(family.sessionIds);
|
|
151
151
|
await this.#refreshFamily(family);
|
|
152
152
|
return lifecycleSuccess(projectSessionCatalogRecord(await this.#stores.readCatalogRecord(target.header.id)));
|
|
@@ -215,7 +215,7 @@ export class HostSessionRetirementCoordinator {
|
|
|
215
215
|
await this.#graphWake.retireSessions(allSessionIds);
|
|
216
216
|
this.#rememberRetiredWorktrees(committableRemove, removedSessionIds);
|
|
217
217
|
this.#scheduleCleanup(removedSessionIds);
|
|
218
|
-
this.#capabilities.retireSessions(allSessionIds);
|
|
218
|
+
await this.#capabilities.retireSessions(allSessionIds);
|
|
219
219
|
this.#messages.retireSessions(allSessionIds);
|
|
220
220
|
await this.#continuity.retireSessions(plan.remove.sessionIds, plan.remove.admission);
|
|
221
221
|
await this.#refreshFamily(plan.archive);
|
|
@@ -47,6 +47,16 @@ export function createSessionTranscriptReader(input) {
|
|
|
47
47
|
readDurableTurnLandmarks: async (sessionId, request) => (await prepared(sessionId)).readTurnLandmarks(sessionId, request),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
/** Reads only the delegated Turn's projected assistant output. The indexed
|
|
51
|
+
* Turn extent avoids materializing every earlier message in its Session.
|
|
52
|
+
*/
|
|
53
|
+
export function createTurnResultReader(input) {
|
|
54
|
+
const ledger = createDurableLedgerTranscriptReader(input);
|
|
55
|
+
return async (sessionId, turnId) => {
|
|
56
|
+
await input.ensureTranscriptLedger?.(sessionId);
|
|
57
|
+
return ledger.readLatestAssistantForTurn(sessionId, turnId);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
50
60
|
/**
|
|
51
61
|
* Pages seek immutable Session event ordinals before decoding payloads. One
|
|
52
62
|
* Turn is projected at a time, so a page costs one Turn rather than the
|
|
@@ -149,6 +159,29 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
149
159
|
source,
|
|
150
160
|
readHighWater: highWater,
|
|
151
161
|
...pagedTranscriptReads(source),
|
|
162
|
+
async readLatestAssistantForTurn(sessionId, turnId) {
|
|
163
|
+
const extents = await store.readTranscriptTurns(sessionId, { turnId });
|
|
164
|
+
let result = '';
|
|
165
|
+
for (const extent of extents) {
|
|
166
|
+
for (let ordinal = extent.firstOrdinal; ordinal <= extent.lastOrdinal;) {
|
|
167
|
+
const run = await readRun(sessionId, {
|
|
168
|
+
direction: 'newer',
|
|
169
|
+
throughOrdinal: extent.lastOrdinal,
|
|
170
|
+
position: ordinal,
|
|
171
|
+
});
|
|
172
|
+
if (!run)
|
|
173
|
+
break;
|
|
174
|
+
if (run.invocation.turnId === turnId) {
|
|
175
|
+
for (const { message } of await projectTurn(run)) {
|
|
176
|
+
if (message.type === 'assistant' && message.text.trim())
|
|
177
|
+
result = message.text;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
ordinal = run.lastOrdinal + 1;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
},
|
|
152
185
|
/** One row per Turn, folded from the Turn's own projected messages. */
|
|
153
186
|
async readTurnContributions(sessionId, throughSequence, position, maxContributions) {
|
|
154
187
|
const watermark = throughSequence ?? (await highWater(sessionId));
|
|
@@ -731,6 +731,10 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
731
731
|
catch {
|
|
732
732
|
// Invalid output, unavailable candidates, or provider failure cannot
|
|
733
733
|
// silently become creation or bind an arbitrary existing Session.
|
|
734
|
+
// Preparation failures (for example transcript reads) and injected-model
|
|
735
|
+
// throws bind this admission to clarify, preventing actions for this turn.
|
|
736
|
+
// This differs intentionally from Jev's internal provider/candidate errors:
|
|
737
|
+
// that opt-in adapter returns undefined to preserve the legacy unbound path.
|
|
734
738
|
return { kind: 'routing', disposition: 'clarify' };
|
|
735
739
|
}
|
|
736
740
|
}
|