maka-agent 0.2.0-dev.23.20260906 → 0.2.0-dev.24.20260907
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/acp/maka-acp-agent.js +2 -1
- package/dist/acp/session-configuration.js +143 -0
- package/dist/acp/session-registry.js +90 -4
- package/dist/cli-core.js +2 -0
- package/dist/pi-transcript.js +5 -12
- package/dist/pi-tui-runner.js +74 -4
- package/dist/runtime-host-cli-context.js +167 -78
- package/dist/runtime-host-cli-installation.js +8 -2
- package/dist/runtime-host-cli.js +31 -1
- package/dist/runtime-host-handoff-surface.js +81 -0
- package/dist/runtime-host-installed-update-bootstrap.js +13 -0
- package/dist/runtime-host-installed-update-coordinator.js +33 -0
- package/dist/runtime-host-lifecycle-transaction.js +9 -1
- package/dist/runtime-host-local-handoff.js +37 -4
- package/dist/runtime-host-local-managed-activation.js +84 -0
- package/dist/runtime-host-local-source-retirement.js +18 -2
- package/dist/runtime-host-managed-lifecycle-manager.js +1 -0
- package/dist/runtime-host-operator-retirement.js +50 -0
- package/dist/runtime-host-service-manager.js +3 -0
- package/dist/runtime-host-session-driver.js +4 -25
- package/dist/runtime-host-session-update.js +62 -0
- package/dist/runtime-host-tui-command.js +38 -73
- package/dist/runtime-host-tui-context.js +1 -0
- package/dist/runtime-host-tui-owner.js +226 -0
- package/dist/tui-copy-catalog.js +140 -3
- package/dist/tui-host-owner-copy.js +24 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
- package/node_modules/@maka/core/dist/model-failure.js +40 -0
- package/node_modules/@maka/core/dist/model-metadata.generated.js +4 -4
- package/node_modules/@maka/core/dist/model-metadata.js +3 -3
- package/node_modules/@maka/core/dist/model-web-search.js +5 -4
- package/node_modules/@maka/core/dist/provider-registry.js +45 -4
- package/node_modules/@maka/core/dist/record-schema.js +9 -2
- package/node_modules/@maka/core/dist/runtime-boundary.js +31 -11
- package/node_modules/@maka/core/dist/runtime-event.js +37 -6
- package/node_modules/@maka/core/dist/runtime-handoff.js +98 -0
- package/node_modules/@maka/core/dist/runtime-invocation.js +1 -1
- package/node_modules/@maka/core/dist/runtime-logical-execution.js +112 -0
- package/node_modules/@maka/core/dist/session.js +54 -52
- package/node_modules/@maka/core/dist/slash-command-catalog.js +2 -0
- package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
- package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
- package/node_modules/@maka/core/package.json +4 -0
- package/node_modules/@maka/mcp/dist/index.js +131 -1
- package/node_modules/@maka/runtime/dist/agent-run.js +272 -105
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +13 -5
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +127 -133
- package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
- package/node_modules/@maka/runtime/dist/conversation-copy.js +5 -4
- package/node_modules/@maka/runtime/dist/goal-continuation.js +36 -4
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +18 -9
- package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +30 -22
- package/node_modules/@maka/runtime/dist/history-compaction.js +29 -4
- package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +106 -0
- package/node_modules/@maka/runtime/dist/message-authority.js +9 -0
- package/node_modules/@maka/runtime/dist/model-adapter.js +19 -98
- package/node_modules/@maka/runtime/dist/model-factory.js +19 -81
- package/node_modules/@maka/runtime/dist/model-fetcher.js +7 -0
- package/node_modules/@maka/runtime/dist/model-runtime.js +85 -111
- package/node_modules/@maka/runtime/dist/native-web-search-tool.js +2 -1
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +2 -2
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +4 -2
- package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
- package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +68 -108
- package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +82 -54
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +117 -159
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +150 -112
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +160 -79
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +30 -89
- package/node_modules/@maka/runtime/dist/runtime-resume.js +80 -4
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +5 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +171 -288
- package/node_modules/@maka/runtime/dist/session-projection-helpers.js +0 -27
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
- package/node_modules/@maka/runtime/dist/test-connection.js +16 -16
- package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
- package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
- package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +9 -1
- package/node_modules/@maka/runtime-host/dist/client/connection.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +156 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +273 -0
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
- package/node_modules/@maka/runtime-host/dist/client/index.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/remote-compatibility-error.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +27 -5
- package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
- package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +21 -12
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +25 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +40 -20
- package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +0 -5
- package/node_modules/@maka/runtime-host/dist/protocol/session-turns.js +29 -58
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +11 -1
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +100 -24
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -5
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +3 -2
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +96 -17
- package/node_modules/@maka/runtime-host/dist/server/host-residency-registry.js +13 -7
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-admission-registry.js +3 -1
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +124 -62
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +76 -6
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +325 -73
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +37 -2
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +45 -3
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +50 -7
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +8 -55
- package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +1 -16
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +0 -1
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +460 -70
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/storage-maintenance.js +109 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +0 -1
- package/node_modules/@maka/storage/dist/artifact-store.js +21 -10
- package/node_modules/@maka/storage/dist/artifact-stores.js +1 -1
- package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -4
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +0 -2
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +9 -7
- package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
- package/node_modules/@maka/storage/dist/opencode-session-adapter.js +0 -4
- package/node_modules/@maka/storage/dist/operational-state-backup.js +9 -5
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +24 -4
- package/node_modules/@maka/storage/dist/session-message-projection.js +15 -0
- package/node_modules/@maka/storage/dist/session-repository.js +767 -0
- package/node_modules/@maka/storage/dist/session-store.js +25 -99
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +10 -3
- package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +24 -8
- package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +7 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +129 -25
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
- package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +111 -0
- package/node_modules/@maka/storage/package.json +2 -0
- package/package.json +1 -1
|
@@ -116,12 +116,12 @@ if (zenmux.api !== 'https://zenmux.ai/api/v1') {
|
|
|
116
116
|
throw new Error('models.dev ZenMux provider facts are missing the official OpenAI-compatible API');
|
|
117
117
|
}
|
|
118
118
|
const zenmuxModelProviderOverrides = GENERATED_MODELS_DEV_MODEL_PROVIDER_OVERRIDES.zenmux;
|
|
119
|
-
if (zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.
|
|
120
|
-
zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.
|
|
119
|
+
if (zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.adapter.kind !== 'anthropic' ||
|
|
120
|
+
zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.baseUrl !==
|
|
121
121
|
'https://zenmux.ai/api/anthropic/v1') {
|
|
122
122
|
throw new Error('models.dev ZenMux snapshot is missing its Anthropic model-level protocol override');
|
|
123
123
|
}
|
|
124
|
-
if (zenmuxModelProviderOverrides['openai/gpt-5.4']?.
|
|
124
|
+
if (zenmuxModelProviderOverrides['openai/gpt-5.4']?.adapter.kind !== 'openai') {
|
|
125
125
|
throw new Error('models.dev ZenMux snapshot is missing its native OpenAI model-level protocol override');
|
|
126
126
|
}
|
|
127
127
|
const zenmuxOpenAICompatibleMetadata = Object.fromEntries(Object.entries(GENERATED_MODELS_DEV_METADATA.zenmux).filter(([id]) => zenmuxModelProviderOverrides[id] === undefined));
|
|
@@ -505,6 +505,14 @@ const providerRegistry = {
|
|
|
505
505
|
fallbackModels: [...kimiCodingPlanModelIds],
|
|
506
506
|
status: 'ready',
|
|
507
507
|
runtimeAdapter: { kind: 'anthropic', auth: 'api-key', normalizeBaseUrl: true },
|
|
508
|
+
protocolAdapters: {
|
|
509
|
+
'openai-chat': {
|
|
510
|
+
kind: 'openai-compatible',
|
|
511
|
+
name: 'provider',
|
|
512
|
+
normalizeUsage: true,
|
|
513
|
+
normalizeBaseUrl: true,
|
|
514
|
+
},
|
|
515
|
+
},
|
|
508
516
|
modelDiscovery: { kind: 'protocol' },
|
|
509
517
|
category: 'domestic',
|
|
510
518
|
catalogGroup: 'plans',
|
|
@@ -891,6 +899,10 @@ const providerRegistry = {
|
|
|
891
899
|
fallbackModels: opencodeModelIds,
|
|
892
900
|
status: 'ready',
|
|
893
901
|
runtimeAdapter: { kind: 'openai-compatible', name: 'provider' },
|
|
902
|
+
protocolAdapters: {
|
|
903
|
+
'anthropic-messages': { kind: 'anthropic', auth: 'api-key', normalizeBaseUrl: true },
|
|
904
|
+
'openai-responses': { kind: 'openai', apiProtocol: 'openai-responses' },
|
|
905
|
+
},
|
|
894
906
|
modelDiscovery: { kind: 'protocol' },
|
|
895
907
|
category: 'overseas',
|
|
896
908
|
catalogGroup: 'plans',
|
|
@@ -904,6 +916,10 @@ const providerRegistry = {
|
|
|
904
916
|
fallbackModels: opencodeGoModelIds,
|
|
905
917
|
status: 'ready',
|
|
906
918
|
runtimeAdapter: { kind: 'openai-compatible', name: 'provider' },
|
|
919
|
+
protocolAdapters: {
|
|
920
|
+
'anthropic-messages': { kind: 'anthropic', auth: 'api-key', normalizeBaseUrl: true },
|
|
921
|
+
'openai-responses': { kind: 'openai', apiProtocol: 'openai-responses' },
|
|
922
|
+
},
|
|
907
923
|
modelDiscovery: { kind: 'protocol' },
|
|
908
924
|
category: 'overseas',
|
|
909
925
|
catalogGroup: 'plans',
|
|
@@ -1182,6 +1198,22 @@ const providerRegistry = {
|
|
|
1182
1198
|
signupUrl: 'https://modelstudio.console.alibabacloud.com/',
|
|
1183
1199
|
catalogOrder: 41.4,
|
|
1184
1200
|
},
|
|
1201
|
+
commandcode: {
|
|
1202
|
+
label: 'Command Code',
|
|
1203
|
+
baseUrl: 'https://api.commandcode.ai/provider/v1',
|
|
1204
|
+
authKind: 'api_key',
|
|
1205
|
+
fallbackModels: [],
|
|
1206
|
+
status: 'ready',
|
|
1207
|
+
runtimeAdapter: { kind: 'openai-compatible', name: 'provider' },
|
|
1208
|
+
protocolAdapters: {
|
|
1209
|
+
'anthropic-messages': { kind: 'anthropic', auth: 'bearer', normalizeBaseUrl: true },
|
|
1210
|
+
},
|
|
1211
|
+
modelDiscovery: { kind: 'protocol', modelProtocols: 'commandcode' },
|
|
1212
|
+
category: 'overseas',
|
|
1213
|
+
catalogGroup: 'plans',
|
|
1214
|
+
signupUrl: 'https://commandcode.ai/docs/plans/goat',
|
|
1215
|
+
catalogOrder: 41.5,
|
|
1216
|
+
},
|
|
1185
1217
|
'cloudflare-workers-ai': {
|
|
1186
1218
|
label: cloudflareWorkersAi.name,
|
|
1187
1219
|
baseUrl: '',
|
|
@@ -1299,7 +1331,16 @@ const providerRegistry = {
|
|
|
1299
1331
|
authKind: 'oauth_token',
|
|
1300
1332
|
fallbackModels: githubCopilotModelIds,
|
|
1301
1333
|
status: 'ready',
|
|
1302
|
-
runtimeAdapter: { kind: '
|
|
1334
|
+
runtimeAdapter: { kind: 'openai-compatible', name: 'provider', includeUsage: false },
|
|
1335
|
+
protocolAdapters: {
|
|
1336
|
+
'anthropic-messages': {
|
|
1337
|
+
kind: 'anthropic',
|
|
1338
|
+
auth: 'bearer',
|
|
1339
|
+
normalizeBaseUrl: true,
|
|
1340
|
+
includeBetaHeaders: false,
|
|
1341
|
+
},
|
|
1342
|
+
'openai-responses': { kind: 'openai', apiProtocol: 'openai-responses' },
|
|
1343
|
+
},
|
|
1303
1344
|
modelDiscovery: { kind: 'protocol', auth: 'github-copilot' },
|
|
1304
1345
|
category: 'oauth',
|
|
1305
1346
|
signupUrl: 'https://github.com/features/copilot/plans',
|
|
@@ -19,11 +19,18 @@
|
|
|
19
19
|
/**
|
|
20
20
|
* Defines a JSON object shape while making schema additions a type error until
|
|
21
21
|
* both the required and optional key lists are updated.
|
|
22
|
+
*
|
|
23
|
+
* `retired` names keys older writers persisted that this type no longer has.
|
|
24
|
+
* They are accepted on read and dropped by {@link pickShape}, so what the shape
|
|
25
|
+
* emits may shrink freely while what it accepts only grows. Removing a key from
|
|
26
|
+
* `optional` without listing it here makes every stored record carrying it fail
|
|
27
|
+
* validation outright.
|
|
22
28
|
*/
|
|
23
29
|
export function defineObjectShape() {
|
|
24
|
-
return (required, optional) => ({
|
|
30
|
+
return (required, optional, retired = []) => ({
|
|
25
31
|
required,
|
|
26
32
|
allowed: new Set([...required, ...optional]),
|
|
33
|
+
retired: new Set(retired),
|
|
27
34
|
});
|
|
28
35
|
}
|
|
29
36
|
export function isRecord(value) {
|
|
@@ -31,7 +38,7 @@ export function isRecord(value) {
|
|
|
31
38
|
}
|
|
32
39
|
export function hasExactShape(value, shape) {
|
|
33
40
|
return (shape.required.every((key) => Object.hasOwn(value, key)) &&
|
|
34
|
-
Object.keys(value).every((key) => shape.allowed.has(key)));
|
|
41
|
+
Object.keys(value).every((key) => shape.allowed.has(key) || shape.retired?.has(key) === true));
|
|
35
42
|
}
|
|
36
43
|
/**
|
|
37
44
|
* Narrows a record to the keys a shape allows. `undefined` entries are dropped
|
|
@@ -65,6 +65,8 @@ export function createRuntimeBoundaryCursor(segments) {
|
|
|
65
65
|
const invocationIds = new Set();
|
|
66
66
|
const runIds = new Set();
|
|
67
67
|
const turnIds = new Set();
|
|
68
|
+
let previousTurnId;
|
|
69
|
+
let protocol = 'runtime_boundary_cursor_v1';
|
|
68
70
|
for (const segment of canonicalSegments) {
|
|
69
71
|
if (segment.identity.sessionId !== sessionId) {
|
|
70
72
|
throw new Error('Runtime boundary segments must belong to the same session');
|
|
@@ -78,24 +80,30 @@ export function createRuntimeBoundaryCursor(segments) {
|
|
|
78
80
|
}
|
|
79
81
|
invocationIds.add(segment.identity.invocationId);
|
|
80
82
|
if (turnIds.has(segment.identity.turnId)) {
|
|
81
|
-
|
|
83
|
+
if (previousTurnId !== segment.identity.turnId) {
|
|
84
|
+
throw new Error('Runtime boundary lineage returns to a previous turnId');
|
|
85
|
+
}
|
|
86
|
+
protocol = 'runtime_boundary_cursor_v2';
|
|
82
87
|
}
|
|
83
88
|
turnIds.add(segment.identity.turnId);
|
|
89
|
+
previousTurnId = segment.identity.turnId;
|
|
84
90
|
}
|
|
85
91
|
return {
|
|
86
|
-
protocol
|
|
92
|
+
protocol,
|
|
87
93
|
segments: canonicalSegments,
|
|
88
|
-
manifestDigest: digestRuntimeBoundaryManifest(canonicalSegments),
|
|
94
|
+
manifestDigest: digestRuntimeBoundaryManifest(canonicalSegments, protocol),
|
|
89
95
|
};
|
|
90
96
|
}
|
|
91
|
-
export function digestRuntimeBoundaryManifest(segments) {
|
|
97
|
+
export function digestRuntimeBoundaryManifest(segments, protocol = 'runtime_boundary_cursor_v1') {
|
|
92
98
|
const canonicalSegments = segments.map(decodeRuntimePrefixSegment);
|
|
93
99
|
const json = stableJsonStringify({
|
|
94
|
-
protocol
|
|
100
|
+
protocol,
|
|
95
101
|
segments: canonicalSegments,
|
|
96
102
|
});
|
|
97
103
|
const hash = nodeCrypto.createHash('sha256');
|
|
98
|
-
updateLengthPrefixed(hash, Buffer.from(
|
|
104
|
+
updateLengthPrefixed(hash, Buffer.from(protocol === 'runtime_boundary_cursor_v1'
|
|
105
|
+
? 'maka.runtime-boundary-manifest.v1'
|
|
106
|
+
: 'maka.runtime-boundary-manifest.v2', 'utf8'));
|
|
99
107
|
updateLengthPrefixed(hash, Buffer.from(json, 'utf8'));
|
|
100
108
|
return `sha256:${hash.digest('hex')}`;
|
|
101
109
|
}
|
|
@@ -115,13 +123,16 @@ export function decodeRuntimePrefixSegment(value) {
|
|
|
115
123
|
export function decodeRuntimeBoundaryCursor(value) {
|
|
116
124
|
if (!isRecord(value) ||
|
|
117
125
|
!hasExactKeys(value, ['protocol', 'segments', 'manifestDigest']) ||
|
|
118
|
-
value.protocol !== 'runtime_boundary_cursor_v1'
|
|
126
|
+
(value.protocol !== 'runtime_boundary_cursor_v1' &&
|
|
127
|
+
value.protocol !== 'runtime_boundary_cursor_v2') ||
|
|
119
128
|
!Array.isArray(value.segments) ||
|
|
120
129
|
value.segments.length === 0) {
|
|
121
130
|
throw new Error('Invalid RuntimeEvent boundary cursor');
|
|
122
131
|
}
|
|
123
132
|
const segments = value.segments.map(decodeRuntimePrefixSegment);
|
|
124
133
|
const cursor = createRuntimeBoundaryCursor(segments);
|
|
134
|
+
if (cursor.protocol !== value.protocol)
|
|
135
|
+
throw new Error('RuntimeEvent boundary cursor version mismatch');
|
|
125
136
|
const manifestDigest = decodeBoundaryDigest(value.manifestDigest);
|
|
126
137
|
if (cursor.manifestDigest !== manifestDigest) {
|
|
127
138
|
throw new Error('RuntimeEvent boundary manifest digest mismatch');
|
|
@@ -173,12 +184,21 @@ export function decodeContinuationClaim(value) {
|
|
|
173
184
|
throw new Error('Continuation claim target invocationId reuses source identity');
|
|
174
185
|
}
|
|
175
186
|
const targetTurnId = value.target.turnId;
|
|
176
|
-
if (boundary.segments.some((segment) => segment.identity.turnId === targetTurnId)) {
|
|
177
|
-
throw new Error('Continuation claim target turnId reuses source identity');
|
|
178
|
-
}
|
|
179
187
|
const targetOpening = decodeRuntimeInvocationOpened(value.targetOpening);
|
|
180
188
|
const openSource = targetOpening.source;
|
|
181
|
-
if (openSource.kind
|
|
189
|
+
if (openSource.kind === 'handoff') {
|
|
190
|
+
if (targetTurnId !== source.identity.turnId) {
|
|
191
|
+
throw new Error('Handoff claim must preserve the logical turnId');
|
|
192
|
+
}
|
|
193
|
+
const root = boundary.segments.find((segment) => segment.identity.turnId === targetTurnId);
|
|
194
|
+
if (root?.identity.runId !== openSource.rootRunId) {
|
|
195
|
+
throw new Error('Handoff claim logical root mismatch');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else if (boundary.segments.some((segment) => segment.identity.turnId === targetTurnId)) {
|
|
199
|
+
throw new Error('Continuation claim target turnId reuses source identity');
|
|
200
|
+
}
|
|
201
|
+
if ((openSource.kind !== 'continuation' && openSource.kind !== 'handoff') ||
|
|
182
202
|
openSource.claimId !== value.claimId ||
|
|
183
203
|
openSource.boundaryDigest !== boundaryDigest ||
|
|
184
204
|
openSource.sourceInvocationId !== source.identity.invocationId ||
|
|
@@ -31,12 +31,15 @@
|
|
|
31
31
|
* Phase 1 scope: types + small pure helpers only. No storage, runner,
|
|
32
32
|
* projection, or ledger logic lives here. Those arrive in later nodes.
|
|
33
33
|
*/
|
|
34
|
+
import { isModelRetryDecision } from './model-failure.js';
|
|
35
|
+
import { isRuntimeHandoffPause, runtimeHandoffPause, } from './runtime-handoff.js';
|
|
34
36
|
import { isMessageContent, normalizeMessageContent, } from './events.js';
|
|
35
37
|
import { INTERACTION_ID_MAX_BYTES, INTERACTION_TOOL_NAME_MAX_BYTES, decodeInteractionRequest, } from './interaction.js';
|
|
36
38
|
import { isPermissionMode, } from './permission.js';
|
|
37
39
|
import { isCollaborationMode } from './collaboration.js';
|
|
38
40
|
import { isAgentSwarmAuthorizationSource, isEffectiveOrchestrationSource, isOrchestrationMode, } from './orchestration.js';
|
|
39
41
|
import { isToolMode } from './tool-mode.js';
|
|
42
|
+
import { isRuntimeSystemNoteKind, } from './session.js';
|
|
40
43
|
import { decodeTurnOrigin } from './turn-origin.js';
|
|
41
44
|
import { defineObjectShape, hasExactShape, isFiniteNumber, isOptionalMember, isOptionalString, isRecord, isStringArray, } from './record-schema.js';
|
|
42
45
|
import { isPermissionDecisionFields, isPermissionRequestPayload, isUserQuestionRequest, } from './interaction-record-schema.js';
|
|
@@ -106,6 +109,7 @@ export const RUNTIME_EVENT_CONTENT_KINDS = [
|
|
|
106
109
|
'function_call',
|
|
107
110
|
'function_response',
|
|
108
111
|
'error',
|
|
112
|
+
'system_note',
|
|
109
113
|
'invocation_opened',
|
|
110
114
|
];
|
|
111
115
|
export const TOOL_BOUNDARY_PROTOCOL_V1 = 't1_after_preflight_v1';
|
|
@@ -178,7 +182,8 @@ const TEXT_CONTENT_SHAPE = defineObjectShape()(['kind', 'text'], [
|
|
|
178
182
|
const THINKING_CONTENT_SHAPE = defineObjectShape()(['kind', 'text'], ['signature', 'providerOptions']);
|
|
179
183
|
const FUNCTION_CALL_CONTENT_SHAPE = defineObjectShape()(['kind', 'id', 'name', 'args'], ['providerOptions', 'providerExecuted']);
|
|
180
184
|
const FUNCTION_RESPONSE_CONTENT_SHAPE = defineObjectShape()(['kind', 'id', 'name', 'result'], ['isError', 'providerExecuted', 'providerOutput', 'modelProjection']);
|
|
181
|
-
const ERROR_CONTENT_SHAPE = defineObjectShape()(['kind', 'message'], ['code', 'reason', 'details']);
|
|
185
|
+
const ERROR_CONTENT_SHAPE = defineObjectShape()(['kind', 'message'], ['code', 'reason', 'details', 'retry']);
|
|
186
|
+
const SYSTEM_NOTE_CONTENT_SHAPE = defineObjectShape()(['kind', 'note'], ['data']);
|
|
182
187
|
const INVOCATION_OPENED_CONTENT_SHAPE = defineObjectShape()(['kind', 'protocol', 'route', 'configuration', 'root', 'source'], ['lineage']);
|
|
183
188
|
const INVOCATION_ROUTE_RUNTIME_SHAPE = defineObjectShape()(['provenance', 'backendKind', 'llmConnectionId', 'llmConnectionSlug', 'modelId'], ['providerStateIdentity']);
|
|
184
189
|
const INVOCATION_ROUTE_UNKNOWN_SHAPE = defineObjectShape()(['provenance', 'backendKind', 'llmConnectionSlug', 'modelId'], []);
|
|
@@ -204,6 +209,16 @@ const INVOCATION_LINEAGE_SHAPE = defineObjectShape()([], [
|
|
|
204
209
|
]);
|
|
205
210
|
const INVOCATION_CONTINUATION_SOURCE_SHAPE = defineObjectShape()(['kind', 'sourceInvocationId', 'sourceRunId', 'sourceTurnId', 'sourceRuntimeEventHighWater'], ['claimId', 'boundaryDigest']);
|
|
206
211
|
const INVOCATION_FRESH_SOURCE_SHAPE = defineObjectShape()(['kind'], []);
|
|
212
|
+
const INVOCATION_HANDOFF_SOURCE_SHAPE = defineObjectShape()([
|
|
213
|
+
'kind',
|
|
214
|
+
'rootRunId',
|
|
215
|
+
'sourceInvocationId',
|
|
216
|
+
'sourceRunId',
|
|
217
|
+
'sourceTurnId',
|
|
218
|
+
'sourceRuntimeEventHighWater',
|
|
219
|
+
'claimId',
|
|
220
|
+
'boundaryDigest',
|
|
221
|
+
], []);
|
|
207
222
|
const INVOCATION_ROOT_SHAPES = {
|
|
208
223
|
user: defineObjectShape()(['kind'], []),
|
|
209
224
|
context_compact: defineObjectShape()(['kind'], []),
|
|
@@ -213,6 +228,7 @@ const INVOCATION_ROOT_SHAPES = {
|
|
|
213
228
|
legacy_automation: defineObjectShape()(['kind', 'legacyAutomationId'], []),
|
|
214
229
|
};
|
|
215
230
|
const RUNTIME_ACTIONS_SHAPE = defineObjectShape()([], [
|
|
231
|
+
'handoffPause',
|
|
216
232
|
'stateDelta',
|
|
217
233
|
'artifactDelta',
|
|
218
234
|
'permissionRequest',
|
|
@@ -236,7 +252,7 @@ const RUNTIME_ACTIONS_SHAPE = defineObjectShape()([], [
|
|
|
236
252
|
const RUNTIME_MANAGED_MUTATION_TERMINAL_SHAPE = defineObjectShape()(['protocol', 'operationId', 'dispatchEventId', 'workspaceInstanceId', 'terminalKind'], []);
|
|
237
253
|
const ANSWER_ACCEPTED_IDENTITY_SHAPE = defineObjectShape()(['requestId'], []);
|
|
238
254
|
const PERMISSION_CLOSURE_ACCEPTED_SHAPE = defineObjectShape()(['requestId', 'reason'], []);
|
|
239
|
-
const RUNTIME_PERMISSION_DECISION_SHAPE = defineObjectShape()(['requestId', 'decision'], ['rememberForTurn', 'reviewer', 'rationale', 'riskLevel', 'toolName']);
|
|
255
|
+
const RUNTIME_PERMISSION_DECISION_SHAPE = defineObjectShape()(['requestId', 'decision'], ['rememberForTurn', 'reviewer', 'rationale', 'riskLevel', 'toolName', 'hint']);
|
|
240
256
|
const UTF8 = new TextEncoder();
|
|
241
257
|
const RUNTIME_TOOL_DISPATCH_SHAPE = defineObjectShape()([
|
|
242
258
|
'protocol',
|
|
@@ -341,6 +357,7 @@ export function decodeRuntimeEvent(value) {
|
|
|
341
357
|
(value.refs !== undefined && !isRuntimeEventRefs(value.refs))) {
|
|
342
358
|
throw new Error('Invalid RuntimeEvent schema');
|
|
343
359
|
}
|
|
360
|
+
runtimeHandoffPause(value);
|
|
344
361
|
if (isRecord(value.content) && value.content.kind === 'text') {
|
|
345
362
|
return {
|
|
346
363
|
...value,
|
|
@@ -415,8 +432,13 @@ function isRuntimeEventContent(value) {
|
|
|
415
432
|
return (hasExactShape(value, ERROR_CONTENT_SHAPE) &&
|
|
416
433
|
isOptionalString(value.code) &&
|
|
417
434
|
isOptionalString(value.reason) &&
|
|
435
|
+
(value.retry === undefined || isModelRetryDecision(value.retry)) &&
|
|
418
436
|
typeof value.message === 'string' &&
|
|
419
437
|
(value.details === undefined || isStringArray(value.details) || isRecord(value.details)));
|
|
438
|
+
case 'system_note':
|
|
439
|
+
return (hasExactShape(value, SYSTEM_NOTE_CONTENT_SHAPE) &&
|
|
440
|
+
typeof value.note === 'string' &&
|
|
441
|
+
isRuntimeSystemNoteKind(value.note));
|
|
420
442
|
case 'invocation_opened':
|
|
421
443
|
return isRuntimeInvocationOpened(value);
|
|
422
444
|
default:
|
|
@@ -508,8 +530,14 @@ function isRuntimeInvocationOpenSource(value) {
|
|
|
508
530
|
return false;
|
|
509
531
|
if (value.kind === 'fresh')
|
|
510
532
|
return hasExactShape(value, INVOCATION_FRESH_SOURCE_SHAPE);
|
|
511
|
-
return (value.kind === 'continuation' &&
|
|
512
|
-
hasExactShape(value, INVOCATION_CONTINUATION_SOURCE_SHAPE)
|
|
533
|
+
return (((value.kind === 'continuation' &&
|
|
534
|
+
hasExactShape(value, INVOCATION_CONTINUATION_SOURCE_SHAPE)) ||
|
|
535
|
+
(value.kind === 'handoff' &&
|
|
536
|
+
hasExactShape(value, INVOCATION_HANDOFF_SOURCE_SHAPE) &&
|
|
537
|
+
isNonEmptyString(value.rootRunId) &&
|
|
538
|
+
isNonEmptyString(value.claimId) &&
|
|
539
|
+
isSha256Digest(value.boundaryDigest) &&
|
|
540
|
+
value.sourceRuntimeEventHighWater > 0)) &&
|
|
513
541
|
isNonEmptyString(value.sourceInvocationId) &&
|
|
514
542
|
isNonEmptyString(value.sourceRunId) &&
|
|
515
543
|
isNonEmptyString(value.sourceTurnId) &&
|
|
@@ -554,7 +582,8 @@ function isRuntimeEventActions(value) {
|
|
|
554
582
|
Object.keys(value).some((key) => key !== 'permissionClosureAccepted')) {
|
|
555
583
|
return false;
|
|
556
584
|
}
|
|
557
|
-
return ((value.
|
|
585
|
+
return ((value.handoffPause === undefined || isRuntimeHandoffPause(value.handoffPause)) &&
|
|
586
|
+
(value.stateDelta === undefined || isRecord(value.stateDelta)) &&
|
|
558
587
|
(value.artifactDelta === undefined ||
|
|
559
588
|
(isRecord(value.artifactDelta) &&
|
|
560
589
|
Object.values(value.artifactDelta).every((item) => typeof item === 'string' || typeof item === 'boolean' || isFiniteNumber(item)))) &&
|
|
@@ -621,7 +650,8 @@ function isRuntimeEventPermissionDecision(value) {
|
|
|
621
650
|
(value.toolName === undefined ||
|
|
622
651
|
(typeof value.toolName === 'string' &&
|
|
623
652
|
value.toolName.length > 0 &&
|
|
624
|
-
UTF8.encode(value.toolName).byteLength <= INTERACTION_TOOL_NAME_MAX_BYTES))
|
|
653
|
+
UTF8.encode(value.toolName).byteLength <= INTERACTION_TOOL_NAME_MAX_BYTES)) &&
|
|
654
|
+
isOptionalString(value.hint));
|
|
625
655
|
}
|
|
626
656
|
function isRuntimeEventAnswerAcceptedIdentity(value) {
|
|
627
657
|
return (isRecord(value) &&
|
|
@@ -804,6 +834,7 @@ export function runtimeEventHasModelVisibleContent(event) {
|
|
|
804
834
|
case 'function_response':
|
|
805
835
|
return true;
|
|
806
836
|
case 'error':
|
|
837
|
+
case 'system_note':
|
|
807
838
|
case 'invocation_opened':
|
|
808
839
|
return false;
|
|
809
840
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { defineObjectShape, hasExactShape, isRecord } from './record-schema.js';
|
|
20
|
+
import { stableJsonStringify } from './tool-args-identity.js';
|
|
21
|
+
const PAUSE_SHAPE = defineObjectShape()([
|
|
22
|
+
'protocol',
|
|
23
|
+
'handoffId',
|
|
24
|
+
'hostEpoch',
|
|
25
|
+
'rootRunId',
|
|
26
|
+
'successorRunId',
|
|
27
|
+
'successorInvocationId',
|
|
28
|
+
'claimId',
|
|
29
|
+
'remainingSteps',
|
|
30
|
+
], []);
|
|
31
|
+
export function isRuntimeHandoffPause(value) {
|
|
32
|
+
return (isRecord(value) &&
|
|
33
|
+
hasExactShape(value, PAUSE_SHAPE) &&
|
|
34
|
+
value.protocol === 'runtime_handoff_pause_v1' &&
|
|
35
|
+
(value.remainingSteps === null ||
|
|
36
|
+
(Number.isSafeInteger(value.remainingSteps) && value.remainingSteps > 0)) &&
|
|
37
|
+
[
|
|
38
|
+
value.handoffId,
|
|
39
|
+
value.hostEpoch,
|
|
40
|
+
value.rootRunId,
|
|
41
|
+
value.successorRunId,
|
|
42
|
+
value.successorInvocationId,
|
|
43
|
+
value.claimId,
|
|
44
|
+
].every((id) => typeof id === 'string' &&
|
|
45
|
+
id.length > 0 &&
|
|
46
|
+
id.length <= 256 &&
|
|
47
|
+
!/[\u0000-\u001f\u007f]/u.test(id)));
|
|
48
|
+
}
|
|
49
|
+
/** The seal cannot simultaneously publish content or claim a logical outcome. */
|
|
50
|
+
export function runtimeHandoffPause(event) {
|
|
51
|
+
const pause = event.actions?.handoffPause;
|
|
52
|
+
if (pause === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (!isRuntimeHandoffPause(pause) ||
|
|
55
|
+
event.partial !== false ||
|
|
56
|
+
event.role !== 'system' ||
|
|
57
|
+
(event.author !== 'host' && event.author !== 'system') ||
|
|
58
|
+
event.status !== undefined ||
|
|
59
|
+
event.content !== undefined ||
|
|
60
|
+
event.actions?.endInvocation !== true ||
|
|
61
|
+
Object.keys(event.actions).some((key) => key !== 'endInvocation' && key !== 'handoffPause') ||
|
|
62
|
+
pause.successorRunId === event.runId ||
|
|
63
|
+
pause.successorRunId === event.invocationId ||
|
|
64
|
+
pause.successorInvocationId === event.invocationId ||
|
|
65
|
+
pause.successorRunId === pause.rootRunId) {
|
|
66
|
+
throw new Error('Invalid Runtime handoff pause seal');
|
|
67
|
+
}
|
|
68
|
+
return pause;
|
|
69
|
+
}
|
|
70
|
+
/** Verify against source facts, not the proposed claim's description of them. */
|
|
71
|
+
export function assertHandoffClaimSource(claim, prefix) {
|
|
72
|
+
const last = prefix.events.at(-1);
|
|
73
|
+
const pause = last && runtimeHandoffPause(last);
|
|
74
|
+
const source = claim.targetOpening.source;
|
|
75
|
+
if (source.kind !== 'handoff') {
|
|
76
|
+
if (pause)
|
|
77
|
+
throw new Error('A handoff pause is reserved for its authorized successor');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const opening = prefix.events[0]?.content;
|
|
81
|
+
if (!pause ||
|
|
82
|
+
opening?.kind !== 'invocation_opened' ||
|
|
83
|
+
prefix.identity.sessionId !== claim.target.sessionId ||
|
|
84
|
+
prefix.identity.turnId !== claim.target.turnId ||
|
|
85
|
+
pause.claimId !== claim.claimId ||
|
|
86
|
+
pause.successorRunId !== claim.target.runId ||
|
|
87
|
+
pause.successorInvocationId !== claim.target.invocationId ||
|
|
88
|
+
pause.rootRunId !== source.rootRunId ||
|
|
89
|
+
pause.rootRunId !==
|
|
90
|
+
(opening.source.kind === 'handoff' ? opening.source.rootRunId : prefix.identity.runId) ||
|
|
91
|
+
stableJsonStringify(opening.root) !== stableJsonStringify(claim.targetOpening.root) ||
|
|
92
|
+
stableJsonStringify(opening.configuration) !==
|
|
93
|
+
stableJsonStringify(claim.targetOpening.configuration) ||
|
|
94
|
+
stableJsonStringify(opening.lineage ?? null) !==
|
|
95
|
+
stableJsonStringify(claim.targetOpening.lineage ?? null)) {
|
|
96
|
+
throw new Error('Handoff claim does not preserve the sealed source authority');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -149,7 +149,7 @@ export function runtimeInvocationOutcome(record) {
|
|
|
149
149
|
export function isSessionInlineInvocation(opening) {
|
|
150
150
|
const lineage = opening.lineage;
|
|
151
151
|
return (lineage?.parentRunId === undefined ||
|
|
152
|
-
(opening.source.kind
|
|
152
|
+
(opening.source.kind !== 'fresh' && lineage.agentId === undefined));
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
155
155
|
* Is this invocation the one the Host admitted for that root execution?
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { continuationStartEventMatchesClaim, createRuntimeBoundaryCursor, invocationMatchesClaimTarget, runtimePrefixSegment, } from './runtime-boundary.js';
|
|
20
|
+
import { readRunInvocation, } from './runtime-event-store.js';
|
|
21
|
+
import { assertHandoffClaimSource, runtimeHandoffPause, } from './runtime-handoff.js';
|
|
22
|
+
/** All logical snapshot and command routing paths share these edge checks. */
|
|
23
|
+
export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
|
|
24
|
+
const root = knownRoot ?? (await readRunInvocation(store, identity.sessionId, identity.runId));
|
|
25
|
+
if (!root)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (root.sessionId !== identity.sessionId ||
|
|
28
|
+
root.runId !== identity.runId ||
|
|
29
|
+
root.turnId !== identity.turnId ||
|
|
30
|
+
root.opening.source.kind === 'handoff') {
|
|
31
|
+
throw new Error('Logical execution reference does not name its original admission');
|
|
32
|
+
}
|
|
33
|
+
let tip = root;
|
|
34
|
+
let segments;
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
for (;;) {
|
|
37
|
+
if (seen.has(tip.runId) || seen.size >= 64)
|
|
38
|
+
throw new Error('Invalid logical execution handoff lineage');
|
|
39
|
+
seen.add(tip.runId);
|
|
40
|
+
const events = await store.readImmutableRuntimeEvents(identity.sessionId, tip.runId);
|
|
41
|
+
const last = events.at(-1);
|
|
42
|
+
const pause = last && runtimeHandoffPause(last);
|
|
43
|
+
if (!pause)
|
|
44
|
+
return { root, tip, events, runIds: [...seen] };
|
|
45
|
+
if (pause.rootRunId !== root.runId)
|
|
46
|
+
throw new Error('Handoff seal changes the logical root');
|
|
47
|
+
const prefix = await store.readImmutableRuntimePrefix({
|
|
48
|
+
sessionId: identity.sessionId,
|
|
49
|
+
runId: tip.runId,
|
|
50
|
+
});
|
|
51
|
+
if (prefix.position.lastEventId !== last.id)
|
|
52
|
+
throw new Error('Handoff source changed after its seal');
|
|
53
|
+
if (!segments) {
|
|
54
|
+
// A manual continuation may itself become a handoff root. Preserve its
|
|
55
|
+
// earlier replay boundary without treating those earlier Turns as this Turn.
|
|
56
|
+
const source = root.opening.source;
|
|
57
|
+
if (source.kind === 'continuation') {
|
|
58
|
+
if (!source.boundaryDigest)
|
|
59
|
+
throw new Error('Handoff requires authenticated source lineage');
|
|
60
|
+
const initial = await store.readContinuationClaimStateByBoundary(source.boundaryDigest);
|
|
61
|
+
if (!initial ||
|
|
62
|
+
initial.startEventId !== prefix.events[0]?.id ||
|
|
63
|
+
!invocationMatchesClaimTarget(root, initial.claim) ||
|
|
64
|
+
!continuationStartEventMatchesClaim(prefix.events[0], initial.claim, initial.startKind)) {
|
|
65
|
+
throw new Error('Handoff root continuation is not authenticated');
|
|
66
|
+
}
|
|
67
|
+
segments = [...initial.claim.boundary.segments];
|
|
68
|
+
}
|
|
69
|
+
else
|
|
70
|
+
segments = [];
|
|
71
|
+
}
|
|
72
|
+
segments.push(runtimePrefixSegment(prefix));
|
|
73
|
+
const boundary = createRuntimeBoundaryCursor(segments);
|
|
74
|
+
let state = await store.readContinuationClaimStateByBoundary(boundary.manifestDigest);
|
|
75
|
+
const next = await readRunInvocation(store, identity.sessionId, pause.successorRunId);
|
|
76
|
+
// A start can commit between the two reads. Authority is monotonic; refresh
|
|
77
|
+
// it once when the later opening read proves the earlier snapshot was old.
|
|
78
|
+
if (next && !state?.startEventId) {
|
|
79
|
+
state = await store.readContinuationClaimStateByBoundary(boundary.manifestDigest);
|
|
80
|
+
}
|
|
81
|
+
if (!state) {
|
|
82
|
+
if (next)
|
|
83
|
+
throw new Error('Handoff successor opened without its continuation claim');
|
|
84
|
+
return { root, tip, events, runIds: [...seen], pendingHandoff: pause };
|
|
85
|
+
}
|
|
86
|
+
assertHandoffClaimSource(state.claim, prefix);
|
|
87
|
+
if (!next) {
|
|
88
|
+
if (state.startEventId)
|
|
89
|
+
throw new Error('Handoff successor opening is missing');
|
|
90
|
+
return { root, tip, events, runIds: [...seen], pendingHandoff: pause };
|
|
91
|
+
}
|
|
92
|
+
const first = (await store.readImmutableRuntimeEvents(identity.sessionId, next.runId))[0];
|
|
93
|
+
if (state.startEventId !== first?.id ||
|
|
94
|
+
!invocationMatchesClaimTarget(next, state.claim) ||
|
|
95
|
+
!continuationStartEventMatchesClaim(first, state.claim, state.startKind)) {
|
|
96
|
+
throw new Error('Handoff successor does not match its authenticated start');
|
|
97
|
+
}
|
|
98
|
+
tip = next;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Resolve a durable physical proof back to its root, then authenticate membership. */
|
|
102
|
+
export async function readLogicalRuntimeExecutionForRun(store, identity) {
|
|
103
|
+
const run = await readRunInvocation(store, identity.sessionId, identity.runId);
|
|
104
|
+
if (!run)
|
|
105
|
+
return undefined;
|
|
106
|
+
const rootRunId = run.opening.source.kind === 'handoff' ? run.opening.source.rootRunId : run.runId;
|
|
107
|
+
const logical = await readLogicalRuntimeExecution(store, { ...identity, runId: rootRunId });
|
|
108
|
+
if (!logical?.runIds.includes(identity.runId)) {
|
|
109
|
+
throw new Error('Physical proof is not an authenticated member of its logical execution');
|
|
110
|
+
}
|
|
111
|
+
return logical;
|
|
112
|
+
}
|