maka-agent 0.2.0-dev.25.20260908 → 0.2.0-dev.26.20260909
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/dist/mcp-capability-provider.js +4 -1
- package/dist/pi-tui-mcp-status.js +2 -2
- package/dist/pi-tui-pickers.js +19 -29
- package/dist/tui-autocomplete-layout.js +3 -1
- package/dist/tui-copy-catalog.js +6 -3
- package/dist/tui-editor-render.js +36 -0
- package/dist/tui-mcp-control.js +41 -6
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/runtime-event.js +3 -0
- package/node_modules/@maka/core/dist/session.js +29 -4
- package/node_modules/@maka/core/dist/workhub-action-result.js +68 -0
- package/node_modules/@maka/core/package.json +2 -3
- package/node_modules/@maka/eval/dist/toolchain-verification.js +1 -1
- package/node_modules/@maka/mcp/dist/form-elicitation.js +213 -0
- package/node_modules/@maka/mcp/dist/index.js +337 -71
- package/node_modules/@maka/runtime/dist/agent-run.js +10 -1
- package/node_modules/@maka/runtime/dist/goal-continuation.js +5 -1
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +20 -1
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +120 -13
- package/node_modules/@maka/runtime/dist/session-manager.js +20 -36
- package/node_modules/@maka/runtime-host/dist/client/connection.js +30 -5
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +17 -1
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +4 -2
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +18 -1
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +3 -2
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +68 -168
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/canonical-session-projection.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +28 -8
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +112 -56
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +45 -20
- package/node_modules/@maka/runtime-host/dist/server/failure-diagnostic.js +24 -0
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +7 -3
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -3
- package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +3 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +34 -4
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +4 -1
- package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +2 -7
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +110 -14
- package/node_modules/@maka/runtime-host/dist/server/runtime-resource-coordinator.js +11 -8
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +31 -21
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +20 -8
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +15 -104
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +3 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -85
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +120 -137
- package/node_modules/@maka/storage/dist/agent-run-store.js +14 -1
- package/node_modules/@maka/storage/dist/artifact-store.js +40 -3
- package/node_modules/@maka/storage/dist/atomic-file-write.js +74 -0
- package/node_modules/@maka/storage/dist/credential-store.js +12 -63
- package/node_modules/@maka/storage/dist/execution-stores.js +4 -1
- package/node_modules/@maka/storage/dist/mcp-config-store.js +10 -25
- package/node_modules/@maka/storage/dist/session-store.js +14 -2
- package/node_modules/@maka/storage/dist/settings-store.js +8 -4
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +11 -0
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +12 -1
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +67 -3
- package/node_modules/@maka/storage/dist/stable-storage.js +14 -1
- package/package.json +1 -1
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +0 -990
- package/node_modules/@maka/core/dist/workhub-session-resolver.js +0 -50
|
@@ -16,14 +16,13 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
import { truncateUtf8 } from '@maka/core/diagnostic-log';
|
|
20
|
-
import { redactSecrets } from '@maka/core/redaction';
|
|
21
19
|
import { HOST_OPERATION_SPECS, decodeOperationOutcome, } from '../protocol/index.js';
|
|
22
20
|
import { HOST_BOOTSTRAP_OPERATION_SPECS } from '../protocol/host-status.js';
|
|
23
21
|
import { HOST_RESOURCE_OPERATION_SPECS } from '../protocol/host-resources.js';
|
|
24
22
|
import { ACCESS_AUTHORITY_OPERATION_SPECS } from '../protocol/access-authority.js';
|
|
25
23
|
import { SESSION_COLLABORATION_OPERATION_SPECS } from '../protocol/session-collaboration.js';
|
|
26
24
|
import { PEER_MESH_OPERATION_SPECS } from '../protocol/peer-mesh.js';
|
|
25
|
+
import { boundedFailureDiagnostic } from './failure-diagnostic.js';
|
|
27
26
|
import { createPeerMeshOperationHandlers } from './peer-mesh-authority.js';
|
|
28
27
|
/**
|
|
29
28
|
* The spec objects a Runtime Host serves from its own core — operations that
|
|
@@ -144,7 +143,7 @@ async function dispatchTypedOperation(request, handlers, context) {
|
|
|
144
143
|
outcome = decodeOperationOutcome(request.operation, await handler(request.input, context));
|
|
145
144
|
}
|
|
146
145
|
catch (error) {
|
|
147
|
-
console.error(`[runtime-host] unexpected ${request.operation} failure: ${
|
|
146
|
+
console.error(`[runtime-host] unexpected ${request.operation} failure: ${boundedFailureDiagnostic(error)}`);
|
|
148
147
|
return operationFailureResponse(request, 'internal_failure', 'Runtime Host operation failed');
|
|
149
148
|
}
|
|
150
149
|
return outcome.ok
|
|
@@ -161,7 +160,3 @@ async function dispatchTypedOperation(request, handlers, context) {
|
|
|
161
160
|
error: outcome.error,
|
|
162
161
|
};
|
|
163
162
|
}
|
|
164
|
-
function boundedUnexpectedFailure(error) {
|
|
165
|
-
const details = error instanceof Error ? error.stack || `${error.name}: ${error.message}` : String(error);
|
|
166
|
-
return truncateUtf8(redactSecrets(details), 8 * 1024, '\n<diagnostic truncated>');
|
|
167
|
-
}
|
|
@@ -21,7 +21,7 @@ import { isDeepStrictEqual } from 'node:util';
|
|
|
21
21
|
import { readLogicalRuntimeExecution, readLogicalRuntimeExecutionForRun, } from '@maka/core/runtime-logical-execution';
|
|
22
22
|
import { runtimeHandoffPause } from '@maka/core/runtime-handoff';
|
|
23
23
|
import { INLINE_REFERENCE_MAX_COUNT, messageContentDigest, messageContentsEqual, normalizeMessageContent, } from '@maka/core/events';
|
|
24
|
-
import { isWorkHubCoordinationSessionId } from '@maka/core/session';
|
|
24
|
+
import { WORKHUB_COORDINATION_SESSION_ID, isWorkHubCoordinationSessionId, } from '@maka/core/session';
|
|
25
25
|
import { resolveEffectiveOrchestration } from '@maka/core/orchestration';
|
|
26
26
|
import { decodeSkillInvocationResult, } from '@maka/core/skill-invocation';
|
|
27
27
|
import { agentGraphIdForRootSession } from '@maka/runtime/stream-graph-coordinator';
|
|
@@ -222,6 +222,10 @@ export class RootTurnCoordinator {
|
|
|
222
222
|
throw error;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
|
+
/** Called under Session admission before changing an execution's authority ceiling. */
|
|
226
|
+
isSessionExecutionIdle(sessionId) {
|
|
227
|
+
return (!this.#recoveryPlansBySession.has(sessionId) && this.readRootState(sessionId).kind === 'idle');
|
|
228
|
+
}
|
|
225
229
|
readRootState(sessionId) {
|
|
226
230
|
const active = this.#executions.get(sessionId);
|
|
227
231
|
if (active) {
|
|
@@ -565,7 +569,10 @@ export class RootTurnCoordinator {
|
|
|
565
569
|
if (isWorkHubCoordinationSessionId(input.sessionId)) {
|
|
566
570
|
return Promise.reject(new RuntimeHostedRootUnavailableError(input.sessionId, WORKHUB_COORDINATION_EXECUTION_UNAVAILABLE_REASON));
|
|
567
571
|
}
|
|
568
|
-
|
|
572
|
+
// Admission is active work even while queued behind a Session gate. Its
|
|
573
|
+
// residency overlaps the execution residency until admission settles.
|
|
574
|
+
const residency = this.acquireRecoveryResidency();
|
|
575
|
+
const admission = this.runCommand(async () => {
|
|
569
576
|
const activeAtEntry = this.#executions.has(input.sessionId);
|
|
570
577
|
let reservation = preparedReservation ?? (activeAtEntry ? undefined : this.reserveRootTurn(input.sessionId));
|
|
571
578
|
if (preparedReservation && this.#admissions.get(input.sessionId) !== preparedReservation) {
|
|
@@ -674,6 +681,7 @@ export class RootTurnCoordinator {
|
|
|
674
681
|
throw error.cause;
|
|
675
682
|
throw error;
|
|
676
683
|
});
|
|
684
|
+
return admission.finally(() => residency.release());
|
|
677
685
|
}
|
|
678
686
|
lookup(sessionId, turnId) {
|
|
679
687
|
return this.runCommand(async () => {
|
|
@@ -1090,15 +1098,92 @@ export class RootTurnCoordinator {
|
|
|
1090
1098
|
if (!isWorkHubCoordinationSessionId(request.sessionId)) {
|
|
1091
1099
|
return Promise.resolve(operationUnavailable('WorkHub Coordination execution requires its reserved Session'));
|
|
1092
1100
|
}
|
|
1101
|
+
if ((request.execution.operation === 'action') !== (request.operation !== undefined)) {
|
|
1102
|
+
return Promise.resolve(operationUnavailable('Coordination execution mode does not match its runner'));
|
|
1103
|
+
}
|
|
1093
1104
|
return this.startRootMessage(request, context);
|
|
1094
1105
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1106
|
+
async runWorkHubCoordinationOperation(request, context) {
|
|
1107
|
+
if (request.execution.operation !== 'action' || !request.operation) {
|
|
1108
|
+
return operationUnavailable('Coordination action requires a Host operation');
|
|
1109
|
+
}
|
|
1110
|
+
let turnId = request.turnId;
|
|
1111
|
+
let recoveredReceipt;
|
|
1112
|
+
// A retry preserves the action identity, but never reopens a terminal Run.
|
|
1113
|
+
// The existing admission chain and terminal facts identify prior attempts.
|
|
1114
|
+
for (;;) {
|
|
1115
|
+
const admission = await this.stores.agentRunStore.readRootTurnAdmission(request.sessionId, turnId);
|
|
1116
|
+
if (!admission)
|
|
1117
|
+
break;
|
|
1118
|
+
if (!rootExecutionMatches(admission.execution, request.execution))
|
|
1119
|
+
return operationConflict('Coordination action identity belongs to different content');
|
|
1120
|
+
const run = await this.readRunIfPresent(request.sessionId, admission.runId);
|
|
1121
|
+
if (!run)
|
|
1122
|
+
break;
|
|
1123
|
+
const snapshot = await this.readCanonicalSnapshot(request.sessionId, turnId, admission.runId, run);
|
|
1124
|
+
if (!isTerminalSnapshot(snapshot) || snapshot.status === 'completed')
|
|
1125
|
+
break;
|
|
1126
|
+
// A crash after the receipt does not erase a completed Host effect.
|
|
1127
|
+
const events = await this.stores.runtimeEventStore.readImmutableRuntimeEvents(request.sessionId, admission.runId);
|
|
1128
|
+
recoveredReceipt ??= events.find((event) => event.actions?.coordination)?.actions
|
|
1129
|
+
?.coordination;
|
|
1130
|
+
turnId = `whretry_${createHash('sha256').update(admission.runId).digest('hex').slice(0, 48)}`;
|
|
1131
|
+
}
|
|
1132
|
+
const receiptToReplay = recoveredReceipt;
|
|
1133
|
+
const started = await this.startWorkHubCoordinationMessage({
|
|
1134
|
+
...request,
|
|
1135
|
+
turnId,
|
|
1136
|
+
...(receiptToReplay ? { operation: async () => receiptToReplay } : {}),
|
|
1137
|
+
}, context);
|
|
1138
|
+
if (!started.ok)
|
|
1139
|
+
return started;
|
|
1140
|
+
const active = this.#executions.get(request.sessionId);
|
|
1141
|
+
if (active?.turnId === turnId)
|
|
1142
|
+
await active.done;
|
|
1143
|
+
const snapshot = await this.readCanonicalSnapshot(request.sessionId, turnId, started.result.runId);
|
|
1144
|
+
if (snapshot.status !== 'completed')
|
|
1145
|
+
return operationUnavailable('Coordination operation did not complete');
|
|
1146
|
+
const events = await this.stores.runtimeEventStore.readImmutableRuntimeEvents(request.sessionId, started.result.runId);
|
|
1147
|
+
const receipt = events.find((event) => event.actions?.coordination)?.actions?.coordination;
|
|
1148
|
+
return receipt
|
|
1149
|
+
? { ok: true, result: receipt }
|
|
1150
|
+
: operationUnavailable('Coordination receipt is unavailable');
|
|
1151
|
+
}
|
|
1152
|
+
coordinationOperation(request, admission) {
|
|
1153
|
+
if (request.execution.kind !== 'workhub_coordination' ||
|
|
1154
|
+
!('operation' in request) ||
|
|
1155
|
+
!request.operation)
|
|
1156
|
+
return undefined;
|
|
1157
|
+
const execute = request.operation;
|
|
1158
|
+
const content = requireHostedExecutionMessageContent(admission);
|
|
1159
|
+
return {
|
|
1160
|
+
sessionId: admission.sessionId,
|
|
1161
|
+
turnId: admission.turnId,
|
|
1162
|
+
runId: admission.runId,
|
|
1163
|
+
userMessageId: admission.userMessageId,
|
|
1164
|
+
execution: admission.execution,
|
|
1165
|
+
content,
|
|
1166
|
+
start: ({ runId, userMessageId, onRunStarted }) => this.manager.runCoordinationOperation(admission.sessionId, { turnId: admission.turnId, ...content }, { runId, userMessageId, onRunStarted }, () => execute(admission.turnId)),
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
/** Accepts a model action against the exact live WorkHub root and its durable user input. */
|
|
1170
|
+
readActiveWorkHubRequest(turnId) {
|
|
1171
|
+
const sessionId = WORKHUB_COORDINATION_SESSION_ID;
|
|
1172
|
+
return this.sessionAdmission.run(sessionId, async () => {
|
|
1173
|
+
const active = this.#executions.get(sessionId);
|
|
1174
|
+
if (!active || active.turnId !== turnId)
|
|
1175
|
+
return undefined;
|
|
1176
|
+
const [admission, header] = await Promise.all([
|
|
1177
|
+
this.stores.agentRunStore.readRootTurnAdmission(sessionId, turnId),
|
|
1178
|
+
this.stores.sessionStore.readHeaderSnapshot(sessionId),
|
|
1179
|
+
]);
|
|
1180
|
+
if (this.#executions.get(sessionId) !== active ||
|
|
1181
|
+
admission?.runId !== active.runId ||
|
|
1182
|
+
admission.execution.kind !== 'workhub_coordination' ||
|
|
1183
|
+
header.toolProfile !== 'workhub-coordination-v2')
|
|
1184
|
+
return undefined;
|
|
1185
|
+
return requireHostedExecutionMessageContent(admission);
|
|
1186
|
+
});
|
|
1102
1187
|
}
|
|
1103
1188
|
startRootMessage(request, context) {
|
|
1104
1189
|
return this.runCommand(async () => {
|
|
@@ -1115,7 +1200,7 @@ export class RootTurnCoordinator {
|
|
|
1115
1200
|
if (existing.execution.kind !== request.execution.kind) {
|
|
1116
1201
|
return completedStart(operationConflict('Turn identity belongs to a different execution kind'));
|
|
1117
1202
|
}
|
|
1118
|
-
if (!
|
|
1203
|
+
if (!rootExecutionMatches(existing.execution, request.execution)) {
|
|
1119
1204
|
return completedStart(operationConflict('Turn identity belongs to a different execution payload'));
|
|
1120
1205
|
}
|
|
1121
1206
|
let content;
|
|
@@ -1141,7 +1226,7 @@ export class RootTurnCoordinator {
|
|
|
1141
1226
|
return completedStart({ ok: true, result: snapshot });
|
|
1142
1227
|
}
|
|
1143
1228
|
}
|
|
1144
|
-
return this.prepareAdmittedTurn(activationInputForAdmission(existing), existing, context.acquireResidency, lease, undefined,
|
|
1229
|
+
return this.prepareAdmittedTurn(activationInputForAdmission(existing), existing, context.acquireResidency, lease, undefined, this.coordinationOperation(request, existing), reservation);
|
|
1145
1230
|
}
|
|
1146
1231
|
reservation ??= this.reserveRootTurn(request.sessionId);
|
|
1147
1232
|
if (!reservation) {
|
|
@@ -1186,7 +1271,8 @@ export class RootTurnCoordinator {
|
|
|
1186
1271
|
const attachmentError = await this.attachmentValidator?.validateTurnAttachments(request.sessionId, attachments);
|
|
1187
1272
|
if (attachmentError)
|
|
1188
1273
|
return completedStart(operationConflict(attachmentError));
|
|
1189
|
-
const binding = request.execution.kind === 'workhub_coordination'
|
|
1274
|
+
const binding = request.execution.kind === 'workhub_coordination' &&
|
|
1275
|
+
header.toolProfile === 'workhub-coordination-v1'
|
|
1190
1276
|
? undefined
|
|
1191
1277
|
: prepared.commitCapabilityBinding
|
|
1192
1278
|
? await prepared.commitCapabilityBinding()
|
|
@@ -1226,7 +1312,7 @@ export class RootTurnCoordinator {
|
|
|
1226
1312
|
if (!rootMessageAdmissionMatches(admitted.admission, request, canonicalContent.content, context.turnAdmissionAuthorization)) {
|
|
1227
1313
|
return completedStart(operationConflict('Turn identity was already admitted with a different payload'));
|
|
1228
1314
|
}
|
|
1229
|
-
return this.prepareAdmittedTurn(activationInputForAdmission(admitted.admission), admitted.admission, context.acquireResidency, lease, undefined,
|
|
1315
|
+
return this.prepareAdmittedTurn(activationInputForAdmission(admitted.admission), admitted.admission, context.acquireResidency, lease, undefined, this.coordinationOperation(request, admitted.admission), reservation);
|
|
1230
1316
|
});
|
|
1231
1317
|
const disposition = await admissionTask.finally(() => {
|
|
1232
1318
|
if (reservation)
|
|
@@ -2198,8 +2284,18 @@ function throwHostedStopError(sessionId, outcome) {
|
|
|
2198
2284
|
throw new RuntimeHostedRootUnavailableError(sessionId, outcome.error.message);
|
|
2199
2285
|
}
|
|
2200
2286
|
}
|
|
2287
|
+
/** Compatibility may omit only the action identity absent from an older admission. */
|
|
2288
|
+
function rootExecutionMatches(stored, incoming) {
|
|
2289
|
+
if (stored.kind === 'workhub_coordination' &&
|
|
2290
|
+
incoming.kind === 'workhub_coordination' &&
|
|
2291
|
+
stored.actionId === undefined) {
|
|
2292
|
+
const { actionId: _actionId, ...legacyIncoming } = incoming;
|
|
2293
|
+
return isDeepStrictEqual(stored, legacyIncoming);
|
|
2294
|
+
}
|
|
2295
|
+
return isDeepStrictEqual(stored, incoming);
|
|
2296
|
+
}
|
|
2201
2297
|
function rootMessageAdmissionMatches(admission, request, content, authorization) {
|
|
2202
|
-
return (
|
|
2298
|
+
return (rootExecutionMatches(admission.execution, request.execution) &&
|
|
2203
2299
|
(request.execution.kind === 'external_message' && request.execution.inputDigest
|
|
2204
2300
|
? true
|
|
2205
2301
|
: messageContentsEqual(requireHostedExecutionMessageContent(admission), content)) &&
|
|
@@ -23,6 +23,7 @@ import { ShellRunPtyControlClosedError, isShellRunResourceRef, } from '@maka/run
|
|
|
23
23
|
import { defaultShellPlan, ShellPreferenceError } from '@maka/runtime/shell-detect';
|
|
24
24
|
import { isSessionNotFoundError } from '@maka/storage/execution-stores';
|
|
25
25
|
import { decodeRuntimeResourceControllerAcquireResult, decodeRuntimeResourceControllerControlResult, decodeRuntimeResourceQueryResult, decodeRuntimeResourceStopResult, decodeRuntimeResourceStartResult, RUNTIME_RESOURCE_CONTROLLER_ACQUIRE_RESULT_MAX_BYTES, RUNTIME_RESOURCE_MAX_CONTROL_SEQUENCE, } from '../protocol/index.js';
|
|
26
|
+
import { boundedFailureDiagnostic } from './failure-diagnostic.js';
|
|
26
27
|
import { boundedRuntimeResourceSnapshot, canonicalRuntimeResources, createRuntimeResourcePage, runtimeResourceRevision, runtimeResourceSnapshotFromResult, } from './runtime-resource-projection.js';
|
|
27
28
|
const MAX_CONTROL_REPLAYS = 128;
|
|
28
29
|
/** Owns Host Shell/PTY tools plus the connection-scoped Client controller fence. */
|
|
@@ -186,8 +187,7 @@ export class HostRuntimeResourceCoordinator {
|
|
|
186
187
|
if (isSessionNotFoundError(error)) {
|
|
187
188
|
return queryFailure('not_found', 'Session was not found');
|
|
188
189
|
}
|
|
189
|
-
this.#
|
|
190
|
-
return queryFailure('internal_failure', 'Session state is unavailable');
|
|
190
|
+
return this.#canonicalReadFailure(error, 'Session state is unavailable');
|
|
191
191
|
}
|
|
192
192
|
if (input.kind === 'get') {
|
|
193
193
|
try {
|
|
@@ -206,9 +206,8 @@ export class HostRuntimeResourceCoordinator {
|
|
|
206
206
|
}),
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
|
-
catch {
|
|
210
|
-
this.#
|
|
211
|
-
return queryFailure('internal_failure', 'Runtime Resource state is unavailable');
|
|
209
|
+
catch (error) {
|
|
210
|
+
return this.#canonicalReadFailure(error, 'Runtime Resource state is unavailable');
|
|
212
211
|
}
|
|
213
212
|
}
|
|
214
213
|
let updates;
|
|
@@ -218,9 +217,8 @@ export class HostRuntimeResourceCoordinator {
|
|
|
218
217
|
updates = updates.filter((update) => update.sessionId === input.sessionId);
|
|
219
218
|
}
|
|
220
219
|
}
|
|
221
|
-
catch {
|
|
222
|
-
this.#
|
|
223
|
-
return queryFailure('internal_failure', 'Runtime Resource state is unavailable');
|
|
220
|
+
catch (error) {
|
|
221
|
+
return this.#canonicalReadFailure(error, 'Runtime Resource state is unavailable');
|
|
224
222
|
}
|
|
225
223
|
try {
|
|
226
224
|
const resources = canonicalRuntimeResources(updates);
|
|
@@ -251,6 +249,11 @@ export class HostRuntimeResourceCoordinator {
|
|
|
251
249
|
? queryFailure('not_found', 'Session was not found')
|
|
252
250
|
: outcome;
|
|
253
251
|
}
|
|
252
|
+
#canonicalReadFailure(error, message) {
|
|
253
|
+
console.error(`[runtime-host] canonical Runtime Resource read failed: ${boundedFailureDiagnostic(error)}`);
|
|
254
|
+
this.#requestDrain();
|
|
255
|
+
return queryFailure('internal_failure', message);
|
|
256
|
+
}
|
|
254
257
|
#guestObservationGrantId(context, sessionId) {
|
|
255
258
|
if (context.principalKind !== 'session_guest')
|
|
256
259
|
return;
|
|
@@ -418,28 +418,38 @@ export class HostScheduledTaskCoordinator {
|
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
async #refresh() {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
421
|
+
if (this.#handoffHeld || this.#draining)
|
|
422
|
+
return;
|
|
423
|
+
// Cover claim admission, native delivery, and persistence, while an idle
|
|
424
|
+
// schedule or a read-only catalog query does not claim active work.
|
|
425
|
+
const residency = this.#acquireResidency();
|
|
426
|
+
try {
|
|
427
|
+
await this.#exclusive(async () => {
|
|
425
428
|
if (this.#handoffHeld || this.#draining)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
+
return;
|
|
430
|
+
for (const claim of await this.#store.listPendingFires()) {
|
|
431
|
+
if (this.#handoffHeld || this.#draining)
|
|
432
|
+
break;
|
|
433
|
+
if (claim.nativeState === 'waiting_for_provider') {
|
|
434
|
+
await this.#fulfill(claim, true);
|
|
435
|
+
}
|
|
429
436
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
437
|
+
while (!this.#draining && !this.#handoffHeld) {
|
|
438
|
+
const scan = await this.#store.claimNextDue(this.#now());
|
|
439
|
+
for (const expired of scan.expired)
|
|
440
|
+
this.#publish('updated', expired.id);
|
|
441
|
+
const claim = scan.claim;
|
|
442
|
+
if (!claim)
|
|
443
|
+
break;
|
|
444
|
+
await this.#refreshResidency();
|
|
445
|
+
await this.#fulfill(claim, false);
|
|
446
|
+
}
|
|
447
|
+
await this.#refreshSchedule();
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
finally {
|
|
451
|
+
residency.release();
|
|
452
|
+
}
|
|
443
453
|
}
|
|
444
454
|
async #fulfill(claim, recovering) {
|
|
445
455
|
const incognito = (await this.#runtimePolicy.runtimePolicy.getSnapshot()).policy.privacy
|
|
@@ -680,7 +690,7 @@ export class HostScheduledTaskCoordinator {
|
|
|
680
690
|
(claims.length > 0 ||
|
|
681
691
|
tasks.some((task) => task.status === 'active' && task.nextFireAt !== null));
|
|
682
692
|
if (shouldHold && !this.#residency)
|
|
683
|
-
this.#residency = this.#acquireResidency();
|
|
693
|
+
this.#residency = this.#acquireResidency('idle');
|
|
684
694
|
if (!shouldHold)
|
|
685
695
|
this.#releaseResidency();
|
|
686
696
|
}
|
|
@@ -24,7 +24,7 @@ import { thinkingVariantsForConnection } from '@maka/core/model-thinking';
|
|
|
24
24
|
import { executionBoundaryDisplayMode, } from '@maka/core/sandbox-boundary';
|
|
25
25
|
import { DEFAULT_SESSION_NAME, normalizeUserSessionName } from '@maka/core/session-name';
|
|
26
26
|
import { isSessionStartModeLabel as isExecutionSemanticLabel, sessionStartModeSpec, } from '@maka/core/session-start-mode';
|
|
27
|
-
import { isWorkHubCoordinationSessionId, isWorkHubCoordinationSessionTarget, } from '@maka/core/session';
|
|
27
|
+
import { WORKHUB_COORDINATION_SESSION_ID, isWorkHubCoordinationSession, isWorkHubCoordinationSessionId, isWorkHubCoordinationSessionTarget, } from '@maka/core/session';
|
|
28
28
|
import { isSessionNotFoundError, SessionMetadataConflictError, SessionMetadataVersionConflictError, } from '@maka/storage/execution-stores';
|
|
29
29
|
import { isVisibleSessionMessage } from '@maka/storage/session-message-projection';
|
|
30
30
|
import { SessionConfigurationRevisionConflictError, SessionConfigurationTransitionError, } from '@maka/runtime/session-manager';
|
|
@@ -438,15 +438,27 @@ export class HostSessionCatalogCoordinator {
|
|
|
438
438
|
}
|
|
439
439
|
});
|
|
440
440
|
}
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
configureWorkHubModel(input) {
|
|
442
|
+
return this.#updateConfiguration({
|
|
443
|
+
sessionId: WORKHUB_COORDINATION_SESSION_ID,
|
|
444
|
+
expectedRevision: input.expectedRevision,
|
|
445
|
+
patch: { modelTarget: input.modelTarget },
|
|
446
|
+
}, 'workhub');
|
|
447
|
+
}
|
|
448
|
+
async #updateConfiguration(input, authority = 'ordinary') {
|
|
449
|
+
if (authority === 'ordinary' && isWorkHubCoordinationSessionId(input.sessionId)) {
|
|
443
450
|
return configurationFailure('operation_conflict', 'WorkHub Coordination Session configuration requires WorkHub authority');
|
|
444
451
|
}
|
|
445
452
|
return this.#admission.run(input.sessionId, async (lease) => {
|
|
446
453
|
let commitAttempted = false;
|
|
447
454
|
try {
|
|
448
455
|
const current = await this.#stores.readHeaderRecordSnapshot(input.sessionId);
|
|
449
|
-
if (
|
|
456
|
+
if (authority === 'workhub' &&
|
|
457
|
+
(!isWorkHubCoordinationSessionId(current.header.id) ||
|
|
458
|
+
!isWorkHubCoordinationSession(current.header))) {
|
|
459
|
+
return configurationFailure('operation_conflict', 'WorkHub Coordination Session identity is unavailable');
|
|
460
|
+
}
|
|
461
|
+
if (authority === 'ordinary' && isWorkHubCoordinationSessionTarget(current.header)) {
|
|
450
462
|
return configurationFailure('operation_conflict', 'WorkHub Coordination Session configuration requires WorkHub authority');
|
|
451
463
|
}
|
|
452
464
|
if (current.revision !== input.expectedRevision) {
|
|
@@ -461,7 +473,7 @@ export class HostSessionCatalogCoordinator {
|
|
|
461
473
|
if (!clearsConnectionBlock && sessionConfigurationMatches(current.header, configuration)) {
|
|
462
474
|
return configurationSuccess({
|
|
463
475
|
kind: 'committed',
|
|
464
|
-
session: projectSessionCatalogRecord(await this.#stores.readCatalogRecord(input.sessionId)),
|
|
476
|
+
session: projectSessionCatalogRecord(await this.#stores.readCatalogRecord(input.sessionId, authority === 'workhub' ? 'recoverable' : 'ordinary')),
|
|
465
477
|
});
|
|
466
478
|
}
|
|
467
479
|
commitAttempted = true;
|
|
@@ -470,7 +482,7 @@ export class HostSessionCatalogCoordinator {
|
|
|
470
482
|
clearConnectionBlock: input.patch.modelTarget !== undefined,
|
|
471
483
|
configuration,
|
|
472
484
|
});
|
|
473
|
-
return configurationSuccess(await this.#committedUpdate(input.sessionId, lease));
|
|
485
|
+
return configurationSuccess(await this.#committedUpdate(input.sessionId, lease, authority === 'workhub' ? 'recoverable' : 'ordinary'));
|
|
474
486
|
}
|
|
475
487
|
catch (error) {
|
|
476
488
|
if (!commitAttempted &&
|
|
@@ -597,11 +609,11 @@ export class HostSessionCatalogCoordinator {
|
|
|
597
609
|
position = page.nextPosition;
|
|
598
610
|
}
|
|
599
611
|
}
|
|
600
|
-
async #committedUpdate(sessionId, lease) {
|
|
612
|
+
async #committedUpdate(sessionId, lease, roleScope = 'ordinary') {
|
|
601
613
|
await this.#continuity.refreshCanonical(sessionId, lease);
|
|
602
614
|
return {
|
|
603
615
|
kind: 'committed',
|
|
604
|
-
session: projectSessionCatalogRecord(await this.#stores.readCatalogRecord(sessionId)),
|
|
616
|
+
session: projectSessionCatalogRecord(await this.#stores.readCatalogRecord(sessionId, roleScope)),
|
|
605
617
|
};
|
|
606
618
|
}
|
|
607
619
|
#metadataMutationFailure(input, error, unknownMessage) {
|
|
@@ -46,18 +46,9 @@ const TRANSCRIPT_TURN_SCAN_LIMIT = 1;
|
|
|
46
46
|
* as absent rather than searched for down the Session.
|
|
47
47
|
*/
|
|
48
48
|
const TRANSCRIPT_LOOKUP_MAX_TURNS = 2;
|
|
49
|
-
/** One storage round trip of Coordination rows, sized like one ledger Turn. */
|
|
50
|
-
const COORDINATION_TRANSCRIPT_SCAN_LIMIT = 64;
|
|
51
|
-
const COORDINATION_TRANSCRIPT_SCAN_MAX_BYTES = DURABLE_TRANSCRIPT_TURN_MAX_BYTES;
|
|
52
49
|
export function createSessionTranscriptReader(input) {
|
|
53
50
|
const ledger = createDurableLedgerTranscriptReader(input);
|
|
54
|
-
const coordination = createCoordinationTranscriptReader(input.stores);
|
|
55
|
-
const isCoordination = (sessionId) => sessionId === WORKHUB_COORDINATION_SESSION_ID;
|
|
56
|
-
// Only a ledger-backed Session has a conversion; the Coordination Session's
|
|
57
|
-
// rows are the transcript, not something a run left behind.
|
|
58
51
|
const prepared = async (sessionId) => {
|
|
59
|
-
if (isCoordination(sessionId))
|
|
60
|
-
return coordination;
|
|
61
52
|
await input.ensureTranscriptLedger?.(sessionId);
|
|
62
53
|
return ledger;
|
|
63
54
|
};
|
|
@@ -148,6 +139,12 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
148
139
|
if (projected.diagnostics.some(isHardRuntimeEventReadModelDiagnostic)) {
|
|
149
140
|
throw new Error('Durable RuntimeEvent transcript projection is incomplete');
|
|
150
141
|
}
|
|
142
|
+
const admission = turn.invocation.sessionId === WORKHUB_COORDINATION_SESSION_ID
|
|
143
|
+
? await input.stores.agentRunStore.readRootTurnAdmission(turn.invocation.sessionId, turn.invocation.turnId)
|
|
144
|
+
: undefined;
|
|
145
|
+
const actionId = admission?.execution.kind === 'workhub_coordination'
|
|
146
|
+
? admission.execution.actionId
|
|
147
|
+
: undefined;
|
|
151
148
|
const ordinals = new Map(turn.events.map((entry) => [entry.event.id, entry.ordinal]));
|
|
152
149
|
const emitted = new Map();
|
|
153
150
|
return projected.messages.map((message, index) => {
|
|
@@ -160,7 +157,12 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
160
157
|
throw new Error('RuntimeEvent exceeds its transcript sequence stride');
|
|
161
158
|
}
|
|
162
159
|
emitted.set(ordinal, offset + 1);
|
|
163
|
-
return {
|
|
160
|
+
return {
|
|
161
|
+
sequence: ordinal * EVENT_SEQUENCE_STRIDE + offset,
|
|
162
|
+
message: message.type === 'user' && actionId
|
|
163
|
+
? { ...message, coordinationActionId: actionId }
|
|
164
|
+
: message,
|
|
165
|
+
};
|
|
164
166
|
});
|
|
165
167
|
};
|
|
166
168
|
const readTurns = async (sessionId, request) => store.readTranscriptInvocations(sessionId, {
|
|
@@ -221,9 +223,11 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
221
223
|
ordinal = older ? low - 1 : high + 1;
|
|
222
224
|
}
|
|
223
225
|
};
|
|
226
|
+
const source = { readHighWater: highWater, scan };
|
|
224
227
|
return {
|
|
228
|
+
source,
|
|
225
229
|
readHighWater: highWater,
|
|
226
|
-
...pagedTranscriptReads(
|
|
230
|
+
...pagedTranscriptReads(source),
|
|
227
231
|
/** One row per Turn, folded from the Turn's own projected messages. */
|
|
228
232
|
async readTurnContributions(sessionId, throughSequence, position, maxContributions) {
|
|
229
233
|
const watermark = throughSequence ?? (await highWater(sessionId));
|
|
@@ -395,99 +399,6 @@ function pagedTranscriptReads(source) {
|
|
|
395
399
|
},
|
|
396
400
|
};
|
|
397
401
|
}
|
|
398
|
-
/**
|
|
399
|
-
* The WorkHub Coordination Session's transcript, read from the rows the WorkHub
|
|
400
|
-
* writes.
|
|
401
|
-
*
|
|
402
|
-
* Every other Session's transcript is what its runs did, so the ledger holds
|
|
403
|
-
* all of it. The Coordination Session's is not: a delegation, a stop and a
|
|
404
|
-
* routing summary are appended under a Turn id that no root Turn admission ever
|
|
405
|
-
* minted, so there is no invocation for the ledger to hang them on and no
|
|
406
|
-
* conversion that could lift them. `workhub.coordination.answer` is the one
|
|
407
|
-
* path that would admit a real Turn and nothing in the renderer calls it.
|
|
408
|
-
*
|
|
409
|
-
* Delete this source once the WorkHub admits a Coordination Turn for every
|
|
410
|
-
* action, which its own ADR already requires (#3492,
|
|
411
|
-
* `docs/architecture/workhub-coordination-session-adr.md`): the Session then
|
|
412
|
-
* reads like any other and this reader has nothing left to do.
|
|
413
|
-
*/
|
|
414
|
-
function createCoordinationTranscriptReader(stores) {
|
|
415
|
-
const store = stores.sessionStore;
|
|
416
|
-
const highWater = (sessionId) => store.readTranscriptHighWaterSnapshot(sessionId);
|
|
417
|
-
const scan = async function* (sessionId, request) {
|
|
418
|
-
const throughSequence = request.throughSequence === undefined ? await highWater(sessionId) : request.throughSequence;
|
|
419
|
-
if (throughSequence === null)
|
|
420
|
-
return;
|
|
421
|
-
const older = request.direction === 'older';
|
|
422
|
-
const position = request.position ?? (older ? throughSequence : 0);
|
|
423
|
-
let cursor = older ? Math.min(position, throughSequence) + 1 : position - 1;
|
|
424
|
-
for (;;) {
|
|
425
|
-
const page = await store.readMessagesAfter(sessionId, {
|
|
426
|
-
...(older ? { beforeSequence: cursor } : { afterSequence: cursor }),
|
|
427
|
-
maxMessages: COORDINATION_TRANSCRIPT_SCAN_LIMIT,
|
|
428
|
-
maxStoredBytes: COORDINATION_TRANSCRIPT_SCAN_MAX_BYTES,
|
|
429
|
-
});
|
|
430
|
-
if (page.records.length === 0)
|
|
431
|
-
return;
|
|
432
|
-
for (const record of page.records) {
|
|
433
|
-
if (!older && record.sequence > throughSequence)
|
|
434
|
-
return;
|
|
435
|
-
yield record;
|
|
436
|
-
}
|
|
437
|
-
cursor = page.records.at(-1).sequence;
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
const source = { readHighWater: highWater, scan };
|
|
441
|
-
return {
|
|
442
|
-
readHighWater: highWater,
|
|
443
|
-
...pagedTranscriptReads(source),
|
|
444
|
-
/** Folded from the rows themselves; these Turns have nothing else. */
|
|
445
|
-
async readTurnContributions(sessionId, throughSequence, position, maxContributions) {
|
|
446
|
-
const watermark = throughSequence ?? (await highWater(sessionId));
|
|
447
|
-
if (watermark === null) {
|
|
448
|
-
return { throughSequence: null, contributions: [], nextPosition: null };
|
|
449
|
-
}
|
|
450
|
-
const byTurn = new Map();
|
|
451
|
-
let nextPosition = null;
|
|
452
|
-
for await (const { sequence, message } of scan(sessionId, {
|
|
453
|
-
direction: 'newer',
|
|
454
|
-
throughSequence: watermark,
|
|
455
|
-
position,
|
|
456
|
-
})) {
|
|
457
|
-
const turnId = message.turnId;
|
|
458
|
-
if (turnId === undefined)
|
|
459
|
-
continue;
|
|
460
|
-
if (!byTurn.has(turnId) && byTurn.size === maxContributions) {
|
|
461
|
-
nextPosition = sequence;
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
byTurn.set(turnId, foldTurnContribution(byTurn.get(turnId), turnId, sequence, message));
|
|
465
|
-
}
|
|
466
|
-
return { throughSequence: watermark, contributions: [...byTurn.values()], nextPosition };
|
|
467
|
-
},
|
|
468
|
-
/** Every prompt, in order: this transcript has no index to sample from. */
|
|
469
|
-
async readTurnLandmarks(sessionId, maxLandmarks) {
|
|
470
|
-
const throughSequence = await highWater(sessionId);
|
|
471
|
-
if (throughSequence === null)
|
|
472
|
-
return { throughSequence: null, landmarks: [] };
|
|
473
|
-
const landmarks = [];
|
|
474
|
-
for await (const { sequence, message } of scan(sessionId, {
|
|
475
|
-
direction: 'newer',
|
|
476
|
-
throughSequence,
|
|
477
|
-
})) {
|
|
478
|
-
if (message.type !== 'user' || message.turnId === undefined)
|
|
479
|
-
continue;
|
|
480
|
-
const label = (message.displayText ?? message.text ?? '').trim();
|
|
481
|
-
if (!label)
|
|
482
|
-
continue;
|
|
483
|
-
landmarks.push({ turnId: message.turnId, sequence, label });
|
|
484
|
-
if (landmarks.length === maxLandmarks)
|
|
485
|
-
break;
|
|
486
|
-
}
|
|
487
|
-
return { throughSequence, landmarks };
|
|
488
|
-
},
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
402
|
function ordinalOf(sequence) {
|
|
492
403
|
return Math.floor(sequence / EVENT_SEQUENCE_STRIDE);
|
|
493
404
|
}
|
|
@@ -44,6 +44,9 @@ export function projectSharedSessionTranscriptMessage(message, sessionId) {
|
|
|
44
44
|
? {}
|
|
45
45
|
: { steeringEventId: message.steeringEventId }),
|
|
46
46
|
...(message.origin === undefined ? {} : { origin: message.origin }),
|
|
47
|
+
...(message.coordinationActionId === undefined
|
|
48
|
+
? {}
|
|
49
|
+
: { coordinationActionId: message.coordinationActionId }),
|
|
47
50
|
};
|
|
48
51
|
}
|
|
49
52
|
case 'assistant':
|