maka-agent 0.2.0-dev.14.20260902 → 0.2.0-dev.16.20260903
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-core.js +0 -1
- package/dist/pi-transcript.js +48 -0
- package/dist/pi-tui-form-interaction.js +497 -0
- package/dist/pi-tui-pickers.js +256 -55
- package/dist/pi-tui-runner.js +215 -12
- package/dist/runtime-host-cli.js +17 -42
- package/dist/runtime-host-lifecycle-transaction.js +31 -13
- package/dist/runtime-host-managed-lifecycle-manager.js +1 -1
- package/dist/runtime-host-onboarding.js +3 -1
- package/dist/runtime-host-openrc-service.js +469 -0
- package/dist/runtime-host-run-command.js +6 -2
- package/dist/runtime-host-service-command.js +3 -3
- package/dist/runtime-host-service-management-command.js +43 -15
- package/dist/runtime-host-session-channel.js +3 -0
- package/dist/runtime-host-session-driver.js +21 -6
- package/dist/runtime-host-setup-command.js +30 -31
- package/dist/tui-context-refresh.js +103 -0
- package/dist/tui-copy-catalog.js +73 -0
- package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/node_modules/@maka/core/dist/chat-model-choice.js +4 -0
- package/node_modules/@maka/core/dist/computer-use.js +27 -92
- package/node_modules/@maka/core/dist/interaction.js +577 -3
- package/node_modules/@maka/core/dist/llm-connections.js +4 -0
- package/node_modules/@maka/core/dist/local-memory.js +2 -2
- package/node_modules/@maka/core/dist/model-call-usage-projection.js +3 -0
- package/node_modules/@maka/core/dist/model-metadata.js +26 -7
- package/node_modules/@maka/core/dist/model-thinking.js +17 -0
- package/node_modules/@maka/core/dist/provider-registry.js +1 -0
- package/node_modules/@maka/core/dist/runtime-event.js +23 -1
- package/node_modules/@maka/core/dist/scheduled-task.js +4 -0
- package/node_modules/@maka/core/dist/session.js +95 -2
- package/node_modules/@maka/core/dist/text-sanitize.js +19 -0
- package/node_modules/@maka/core/dist/usage-ledger-merge.js +3 -0
- package/node_modules/@maka/core/dist/usage-record-schema.js +11 -5
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +75 -9
- package/node_modules/@maka/core/dist/workhub-session-resolver.js +50 -0
- package/node_modules/@maka/core/package.json +2 -0
- package/node_modules/@maka/eval/dist/harbor-external-subject.js +2 -1
- package/node_modules/@maka/eval/dist/harness-executor.js +3 -2
- package/node_modules/@maka/eval/dist/install-preflight.js +39 -10
- package/node_modules/@maka/eval/dist/process-termination.js +83 -0
- package/node_modules/@maka/mcp/dist/sep-2243.js +3 -3
- package/node_modules/@maka/runtime/dist/agent-run.js +6 -3
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +332 -118
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction-contract.js +6 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +171 -341
- package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +3 -26
- package/node_modules/@maka/runtime/dist/bundled-skill-catalog.generated.js +1 -1
- package/node_modules/@maka/runtime/dist/computer-use-codec.js +1 -141
- package/node_modules/@maka/runtime/dist/computer-use-tools.js +23 -105
- package/node_modules/@maka/runtime/dist/context-budget-policy.js +20 -26
- package/node_modules/@maka/runtime/dist/context-budget.js +18 -20
- package/node_modules/@maka/runtime/dist/conversation-copy.js +23 -19
- package/node_modules/@maka/runtime/dist/cua-frame-state.js +1 -69
- package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +267 -0
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +6 -9
- package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +67 -23
- package/node_modules/@maka/runtime/dist/history-compact-summary-validation.js +11 -18
- package/node_modules/@maka/runtime/dist/history-compaction.js +66 -73
- package/node_modules/@maka/runtime/dist/interaction-authority.js +69 -0
- package/node_modules/@maka/runtime/dist/mcp-tools.js +13 -1
- package/node_modules/@maka/runtime/dist/memory-extraction.js +11 -11
- package/node_modules/@maka/runtime/dist/model-adapter.js +24 -6
- package/node_modules/@maka/runtime/dist/model-factory.js +14 -5
- package/node_modules/@maka/runtime/dist/oauth-provider-contracts.js +28 -1
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +7 -6
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +29 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +11 -1
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +12 -11
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +51 -13
- package/node_modules/@maka/runtime/dist/scheduled-task-tools.js +3 -0
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +26 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +1 -0
- package/node_modules/@maka/runtime/dist/session-projection-helpers.js +16 -1
- package/node_modules/@maka/runtime/dist/stream-graph-projection.js +6 -0
- package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +5 -0
- package/node_modules/@maka/runtime/dist/stream-usage-fallback-fetch.js +74 -0
- package/node_modules/@maka/runtime/dist/subscription-credentials.js +51 -4
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +47 -3
- package/node_modules/@maka/runtime/dist/system-prompt/main-session-prompt.js +20 -1
- package/node_modules/@maka/runtime/dist/test-connection.js +18 -11
- package/node_modules/@maka/runtime/dist/tool-runtime.js +183 -0
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +14 -0
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +84 -1
- package/node_modules/@maka/runtime-host/dist/client/host-profile.js +68 -51
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +8 -5
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +342 -36
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +60 -4
- package/node_modules/@maka/runtime-host/dist/peer-mesh/index.js +3 -3
- package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +1 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +48 -60
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +847 -431
- package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +33 -74
- package/node_modules/@maka/runtime-host/dist/peer-mesh/store.js +82 -69
- package/node_modules/@maka/runtime-host/dist/peer-reachability/index.js +21 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/model.js +202 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/owner.js +119 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +245 -0
- package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +4 -1
- package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +68 -0
- package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +15 -8
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +7 -23
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +47 -1
- package/node_modules/@maka/runtime-host/dist/protocol/oauth.js +25 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/peer-mesh.js +14 -31
- package/node_modules/@maka/runtime-host/dist/protocol/scheduled-task.js +19 -6
- package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +36 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-revision.js +11 -2
- package/node_modules/@maka/runtime-host/dist/protocol/usage-pricing.js +16 -2
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +51 -2
- package/node_modules/@maka/runtime-host/dist/server/access-authority.js +35 -2
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +12 -10
- package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +163 -30
- package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +38 -14
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +45 -22
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +78 -20
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +4 -2
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-service.js +49 -28
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -6
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +130 -27
- package/node_modules/@maka/runtime-host/dist/server/interaction-projection.js +30 -1
- package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -4
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +16 -3
- package/node_modules/@maka/runtime-host/dist/server/oauth-coordinator.js +66 -3
- package/node_modules/@maka/runtime-host/dist/server/oauth-execution-authority.js +20 -3
- package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +8 -21
- package/node_modules/@maka/runtime-host/dist/server/peer-mesh-authority.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +6 -3
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +72 -12
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +48 -18
- package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +43 -26
- package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +15 -1
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +229 -4
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +146 -8
- package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +32 -6
- package/node_modules/@maka/runtime-host/package.json +1 -0
- package/node_modules/@maka/storage/dist/agent-run-store.js +18 -5
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +14 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +5 -0
- package/node_modules/@maka/storage/dist/interaction-store.js +13 -1
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +9 -3
- package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +49 -19
- package/node_modules/@maka/storage/dist/runtime-policy/oauth-login-receipt-document.js +3 -1
- package/node_modules/@maka/storage/dist/runtime-policy/onboarding-transaction.js +13 -5
- package/node_modules/@maka/storage/dist/session-copy-cleanup.js +4 -2
- package/node_modules/@maka/storage/dist/session-store.js +39 -6
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +16 -9
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +30 -3
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +117 -0
- package/node_modules/@maka/storage/dist/sqlite-usage-store.js +29 -6
- package/node_modules/@maka/storage/dist/usage-stores.js +3 -1
- package/package.json +1 -1
|
@@ -154,7 +154,9 @@ function decodeIdentity(value) {
|
|
|
154
154
|
}
|
|
155
155
|
function decodeOAuthProvider(value) {
|
|
156
156
|
const providerType = decodePersistedDomain(() => decodeProviderType(value));
|
|
157
|
-
if (providerType !== 'openai-codex' &&
|
|
157
|
+
if (providerType !== 'openai-codex' &&
|
|
158
|
+
providerType !== 'xai-oauth' &&
|
|
159
|
+
providerType !== 'github-copilot') {
|
|
158
160
|
throw codecError('invalid_document', 'OAuth login receipt provider is invalid');
|
|
159
161
|
}
|
|
160
162
|
return providerType;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { unlink } from 'node:fs/promises';
|
|
20
20
|
import { join } from 'node:path';
|
|
21
|
-
import { decodeProviderType, decodeCanonicalConnectionCatalogEntry, decodeCredentialVersionBasis, decodeConnectionSlug, decodeRuntimePolicyEntityId, normalizeCatalogConnectionBaseUrl, normalizeConnectionCatalogEntryUpdateForProvider, normalizeConnectionModelDiscoveryResult, normalizeCredentialSecret, } from '@maka/core/runtime-policy';
|
|
21
|
+
import { decodeProviderType, decodeCanonicalConnectionCatalogEntry, decodeCredentialVersionBasis, decodeConnectionName, decodeConnectionSlug, decodeRuntimePolicyEntityId, normalizeCatalogConnectionBaseUrl, normalizeConnectionCatalogEntryUpdateForProvider, normalizeConnectionModelDiscoveryResult, normalizeCredentialSecret, } from '@maka/core/runtime-policy';
|
|
22
22
|
import { deriveConnectionSlug, PROVIDER_REGISTRY, providerAuthSupportsApiKey, } from '@maka/core/llm-connections';
|
|
23
23
|
import { syncDirectory } from '../stable-storage.js';
|
|
24
24
|
import { record } from './codec.js';
|
|
@@ -31,10 +31,10 @@ const MAX_BYTES = 5 * 1024 * 1024;
|
|
|
31
31
|
export function prepareConnectionOnboardingIntent(input, source = 'input') {
|
|
32
32
|
const decode = source === 'persisted' ? decodePersistedDomain : decodeConnectionInput;
|
|
33
33
|
const providerType = decode(() => decodeProviderType(input.providerType));
|
|
34
|
-
if (!providerAuthSupportsApiKey(providerType)) {
|
|
35
|
-
throw codecError(source === 'persisted' ? 'invalid_document' : 'invalid_connection_input', 'Onboarding requires an API-key provider');
|
|
36
|
-
}
|
|
37
34
|
const definition = PROVIDER_REGISTRY[providerType];
|
|
35
|
+
if (!providerAuthSupportsApiKey(providerType) && definition.authKind !== 'oauth_token') {
|
|
36
|
+
throw codecError(source === 'persisted' ? 'invalid_document' : 'invalid_connection_input', 'Onboarding requires a provider with a connection credential');
|
|
37
|
+
}
|
|
38
38
|
const discovery = decode(() => normalizeConnectionModelDiscoveryResult(input.discovery));
|
|
39
39
|
// Non-empty is the requirement; `source` is write provenance, not a
|
|
40
40
|
// quality bar. A provider without a model-list endpoint runs discovery by
|
|
@@ -72,6 +72,9 @@ export function prepareConnectionOnboardingIntent(input, source = 'input') {
|
|
|
72
72
|
connectionId: decode(() => decodeRuntimePolicyEntityId(input.connectionId)),
|
|
73
73
|
slug: decode(() => decodeConnectionSlug(input.slug)),
|
|
74
74
|
providerType,
|
|
75
|
+
name: input.name === undefined || input.name === null
|
|
76
|
+
? null
|
|
77
|
+
: decode(() => decodeConnectionName(input.name)),
|
|
75
78
|
suppliedSecret,
|
|
76
79
|
baseUrl,
|
|
77
80
|
enabledModelIds: normalized.enabledModelIds,
|
|
@@ -95,6 +98,7 @@ export async function readConnectionOnboardingIntent(root) {
|
|
|
95
98
|
'connectionId',
|
|
96
99
|
'slug',
|
|
97
100
|
'providerType',
|
|
101
|
+
'name',
|
|
98
102
|
'suppliedSecret',
|
|
99
103
|
'baseUrl',
|
|
100
104
|
'enabledModelIds',
|
|
@@ -110,6 +114,7 @@ export async function readConnectionOnboardingIntent(root) {
|
|
|
110
114
|
'connectionId',
|
|
111
115
|
'slug',
|
|
112
116
|
'providerType',
|
|
117
|
+
'name',
|
|
113
118
|
'suppliedSecret',
|
|
114
119
|
'baseUrl',
|
|
115
120
|
'enabledModelIds',
|
|
@@ -131,6 +136,7 @@ export async function readConnectionOnboardingIntent(root) {
|
|
|
131
136
|
providerType: raw.providerType,
|
|
132
137
|
connectionId: raw.connectionId,
|
|
133
138
|
slug: raw.schemaVersion === 1 ? deriveLegacyIntentPlaceholderSlug(raw.providerType) : raw.slug,
|
|
139
|
+
name: raw.name,
|
|
134
140
|
suppliedSecret: raw.suppliedSecret,
|
|
135
141
|
baseUrl: raw.baseUrl,
|
|
136
142
|
enabledModelIds: raw.enabledModelIds,
|
|
@@ -231,7 +237,9 @@ function decodeOAuthTarget(value) {
|
|
|
231
237
|
throw codecError('invalid_document', 'OAuth enrollment target kind is invalid');
|
|
232
238
|
}
|
|
233
239
|
function isOAuthProvider(providerType) {
|
|
234
|
-
return providerType === 'openai-codex' ||
|
|
240
|
+
return (providerType === 'openai-codex' ||
|
|
241
|
+
providerType === 'xai-oauth' ||
|
|
242
|
+
providerType === 'github-copilot');
|
|
235
243
|
}
|
|
236
244
|
function decodeOAuthAttemptId(value, source) {
|
|
237
245
|
if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(value)) {
|
|
@@ -261,7 +261,7 @@ class SqliteSessionCopyCleanupStore {
|
|
|
261
261
|
creation: {
|
|
262
262
|
kind: creation.kind,
|
|
263
263
|
sourceSessionId: creation.sourceSessionId,
|
|
264
|
-
sourceTurnId: creation.sourceTurnId,
|
|
264
|
+
...(creation.sourceTurnId === undefined ? {} : { sourceTurnId: creation.sourceTurnId }),
|
|
265
265
|
...(creation.intent ? { intent: creation.intent } : {}),
|
|
266
266
|
},
|
|
267
267
|
};
|
|
@@ -365,7 +365,9 @@ function normalizeCreationLease(creation) {
|
|
|
365
365
|
sessionId: normalizeSessionId(creation.sessionId),
|
|
366
366
|
kind: creation.kind,
|
|
367
367
|
sourceSessionId: normalizeSessionId(creation.sourceSessionId),
|
|
368
|
-
|
|
368
|
+
...(creation.sourceTurnId === undefined
|
|
369
|
+
? {}
|
|
370
|
+
: { sourceTurnId: normalizeSessionId(creation.sourceTurnId) }),
|
|
369
371
|
...(creation.intent === 'side_conversation' ? { intent: creation.intent } : {}),
|
|
370
372
|
ownerId: normalizeOwnerId(creation.ownerId),
|
|
371
373
|
};
|
|
@@ -221,6 +221,26 @@ class SqliteSessionStore {
|
|
|
221
221
|
? message
|
|
222
222
|
: undefined;
|
|
223
223
|
}
|
|
224
|
+
async readWorkHubStopRequest(delegationId) {
|
|
225
|
+
const message = await this.readWorkHubCoordinationMessage(`whq_${workHubIdentitySuffix(delegationId)}`);
|
|
226
|
+
return message?.type === 'workhub_coordination' && message.kind === 'delegation_stop_requested'
|
|
227
|
+
? message
|
|
228
|
+
: undefined;
|
|
229
|
+
}
|
|
230
|
+
async readWorkHubStopResolution(delegationId) {
|
|
231
|
+
const message = await this.readWorkHubCoordinationMessage(`whz_${workHubIdentitySuffix(delegationId)}`);
|
|
232
|
+
return message?.type === 'workhub_coordination' && message.kind === 'delegation_stop_resolved'
|
|
233
|
+
? message
|
|
234
|
+
: undefined;
|
|
235
|
+
}
|
|
236
|
+
async claimWorkHubAction(claim) {
|
|
237
|
+
await this.ensureReady();
|
|
238
|
+
return this.metadata.claimWorkHubAction(claim);
|
|
239
|
+
}
|
|
240
|
+
async readWorkHubActionClaim(actionId) {
|
|
241
|
+
await this.ensureReady();
|
|
242
|
+
return this.metadata.readWorkHubActionClaim(actionId);
|
|
243
|
+
}
|
|
224
244
|
async readWorkHubCoordinationMessage(messageId) {
|
|
225
245
|
await this.ensureReady();
|
|
226
246
|
const throughSequence = await this.metadata.readTranscriptHighWater(WORKHUB_COORDINATION_SESSION_ID);
|
|
@@ -443,6 +463,10 @@ class SqliteSessionStore {
|
|
|
443
463
|
await this.ensureReady();
|
|
444
464
|
return this.metadata.hasCancelledMessageAdmission(sessionId, messageId);
|
|
445
465
|
}
|
|
466
|
+
async claimMessageAdmissionCancellation(sessionId, messageId, claimId) {
|
|
467
|
+
await this.ensureReady();
|
|
468
|
+
return this.metadata.claimMessageAdmissionCancellation(sessionId, messageId, claimId);
|
|
469
|
+
}
|
|
446
470
|
async listMessageAdmissions(sessionId) {
|
|
447
471
|
await this.ensureReady();
|
|
448
472
|
return this.metadata.listMessageAdmissions(sessionId);
|
|
@@ -784,15 +808,24 @@ function isValidConversationCopyLineage(header) {
|
|
|
784
808
|
return false;
|
|
785
809
|
}
|
|
786
810
|
if (copy.kind === 'branch') {
|
|
787
|
-
|
|
788
|
-
header.branchOfTurnId === copy.sourceTurnId &&
|
|
789
|
-
header.revisionRootSessionId === undefined &&
|
|
811
|
+
const revisionClear = header.revisionRootSessionId === undefined &&
|
|
790
812
|
header.revisionParentSessionId === undefined &&
|
|
791
813
|
header.revisionOfTurnId === undefined &&
|
|
792
814
|
header.revisionIndex === undefined &&
|
|
793
|
-
header.revisionState === undefined
|
|
794
|
-
|
|
795
|
-
|
|
815
|
+
header.revisionState === undefined;
|
|
816
|
+
if (!revisionClear || header.parentSessionId !== copy.sourceSessionId) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
// An empty copy (absent `sourceTurnId`) records provenance
|
|
820
|
+
// (`parentSessionId`) but must not fabricate a `branchOfTurnId`, and is only
|
|
821
|
+
// valid for a side conversation; a through-turn copy must anchor to it.
|
|
822
|
+
return copy.sourceTurnId === undefined
|
|
823
|
+
? header.branchOfTurnId === undefined && copy.intent === 'side_conversation'
|
|
824
|
+
: header.branchOfTurnId === copy.sourceTurnId;
|
|
825
|
+
}
|
|
826
|
+
// Revision copies always carry a turn boundary (enforced at decode).
|
|
827
|
+
return (copy.sourceTurnId !== undefined &&
|
|
828
|
+
header.revisionParentSessionId === copy.sourceSessionId &&
|
|
796
829
|
header.revisionOfTurnId === copy.sourceTurnId);
|
|
797
830
|
}
|
|
798
831
|
function isValidSubagentSessionLineage(header) {
|
|
@@ -216,19 +216,17 @@ export class SqliteRuntimeStore {
|
|
|
216
216
|
}
|
|
217
217
|
async importConversationCopyRuntimeEvents(sessionId, batches) {
|
|
218
218
|
assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
|
|
219
|
-
const runIds = new Set();
|
|
220
219
|
const canonicalBatches = batches.map(({ runId, events }) => {
|
|
221
220
|
assertRuntimeStorageSafeId(runId, 'Invalid run id');
|
|
222
|
-
if (runIds.has(runId)) {
|
|
223
|
-
throw new Error(`Conversation copy contains duplicate run ${runId}`);
|
|
224
|
-
}
|
|
225
|
-
runIds.add(runId);
|
|
226
221
|
return {
|
|
227
222
|
runId,
|
|
228
223
|
events: events.map(canonicalizeRuntimeEventForStorage),
|
|
229
224
|
};
|
|
230
225
|
});
|
|
231
226
|
const canonicalEvents = canonicalBatches.flatMap(({ events }) => events);
|
|
227
|
+
if (new Set(canonicalEvents.map(({ id }) => id)).size !== canonicalEvents.length) {
|
|
228
|
+
throw new Error('Conversation copy contains duplicate RuntimeEvents');
|
|
229
|
+
}
|
|
232
230
|
for (const { runId, events } of canonicalBatches) {
|
|
233
231
|
for (const event of events) {
|
|
234
232
|
assertNoReservedWorkspaceAuthorityAppend(event);
|
|
@@ -245,7 +243,12 @@ export class SqliteRuntimeStore {
|
|
|
245
243
|
throw new Error(`Conversation copy RuntimeEvent ledger is corrupt: ${scan.issues[0]?.code ?? 'unknown'}`);
|
|
246
244
|
}
|
|
247
245
|
this.transaction(() => {
|
|
246
|
+
const eventsByRun = new Map();
|
|
248
247
|
for (const { runId, events } of canonicalBatches) {
|
|
248
|
+
eventsByRun.set(runId, [...(eventsByRun.get(runId) ?? []), ...events]);
|
|
249
|
+
}
|
|
250
|
+
const newRunIds = new Set();
|
|
251
|
+
for (const [runId, events] of eventsByRun) {
|
|
249
252
|
const existing = this.db
|
|
250
253
|
.prepare(`
|
|
251
254
|
SELECT event_id, session_id, invocation_id, run_id, turn_id, payload_json
|
|
@@ -257,10 +260,14 @@ export class SqliteRuntimeStore {
|
|
|
257
260
|
if (existing.length > 0 && !isDeepStrictEqual(existing, events)) {
|
|
258
261
|
throw new Error(`Conversation copy RuntimeEvent identity conflict for run ${runId}`);
|
|
259
262
|
}
|
|
260
|
-
if (existing.length === 0)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
if (existing.length === 0)
|
|
264
|
+
newRunIds.add(runId);
|
|
265
|
+
}
|
|
266
|
+
for (const { runId, events } of canonicalBatches) {
|
|
267
|
+
if (!newRunIds.has(runId))
|
|
268
|
+
continue;
|
|
269
|
+
for (const event of events)
|
|
270
|
+
this.insertRuntimeEvent(event, event.ts, true);
|
|
264
271
|
}
|
|
265
272
|
if (canonicalEvents.some(isToolLedgerBearingEvent)) {
|
|
266
273
|
this.rebuildToolProjectionsFromRuntimeEventsSync(sessionId);
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
export const SQLITE_SESSION_METADATA_SCHEMA_VERSION =
|
|
19
|
+
export const SQLITE_SESSION_METADATA_SCHEMA_VERSION = 38;
|
|
20
20
|
export const SQLITE_SESSION_MESSAGE_CHUNK_BYTES = 64 * 1024;
|
|
21
21
|
export const SQLITE_SESSION_MESSAGE_CHUNK_MARKER = '{"$maka":"session-message-chunks-v1"}';
|
|
22
22
|
export const SQLITE_AGENT_GRAPH_CONTROL_TABLES = [
|
|
@@ -1237,6 +1237,32 @@ const MIGRATIONS = new Map([
|
|
|
1237
1237
|
SELECT 1;
|
|
1238
1238
|
`,
|
|
1239
1239
|
],
|
|
1240
|
+
[
|
|
1241
|
+
37,
|
|
1242
|
+
`
|
|
1243
|
+
ALTER TABLE cancelled_message_admissions
|
|
1244
|
+
ADD COLUMN cancellation_claim_id TEXT;
|
|
1245
|
+
`,
|
|
1246
|
+
],
|
|
1247
|
+
[
|
|
1248
|
+
38,
|
|
1249
|
+
`
|
|
1250
|
+
-- The one global owner of a WorkHub action identity. It deliberately has no
|
|
1251
|
+
-- Session foreign key: the claim must outlive removal of the target Session
|
|
1252
|
+
-- so a committed destructive claim still converges after that removal.
|
|
1253
|
+
CREATE TABLE IF NOT EXISTS workhub_action_claims (
|
|
1254
|
+
action_id TEXT PRIMARY KEY,
|
|
1255
|
+
operation TEXT NOT NULL CHECK (
|
|
1256
|
+
operation IN (
|
|
1257
|
+
'answer_here', 'clarify', 'delegate_existing', 'create_new', 'replace', 'stop'
|
|
1258
|
+
)
|
|
1259
|
+
),
|
|
1260
|
+
action_fingerprint TEXT NOT NULL,
|
|
1261
|
+
subject TEXT NOT NULL,
|
|
1262
|
+
claimed_at INTEGER NOT NULL CHECK (claimed_at >= 0)
|
|
1263
|
+
);
|
|
1264
|
+
`,
|
|
1265
|
+
],
|
|
1240
1266
|
]);
|
|
1241
1267
|
if (MIGRATIONS.size !== SQLITE_SESSION_METADATA_SCHEMA_VERSION) {
|
|
1242
1268
|
throw new Error('SQLite session metadata migrations contain a duplicate or missing version');
|
|
@@ -1282,12 +1308,13 @@ export function migrateSqliteSessionMetadataDatabase(db, options = {}) {
|
|
|
1282
1308
|
const sql = MIGRATIONS.get(version);
|
|
1283
1309
|
if (!sql)
|
|
1284
1310
|
throw new Error(`Missing SQLite session metadata migration ${version}`);
|
|
1285
|
-
// Versions 32 and
|
|
1311
|
+
// Versions 32, 35, and 37 each add one column, and the post-merge convergence
|
|
1286
1312
|
// path can replay them onto a database that already carries the current
|
|
1287
1313
|
// table shape. SQLite has no `ADD COLUMN IF NOT EXISTS`, so the guards
|
|
1288
1314
|
// live here.
|
|
1289
1315
|
const columnAlreadyPresent = (version === 32 && hasColumn(db, 'message_admissions', 'submitted_intent_json')) ||
|
|
1290
|
-
(version === 35 && hasColumn(db, 'message_admissions', 'skill_invocation_json'))
|
|
1316
|
+
(version === 35 && hasColumn(db, 'message_admissions', 'skill_invocation_json')) ||
|
|
1317
|
+
(version === 37 && hasColumn(db, 'cancelled_message_admissions', 'cancellation_claim_id'));
|
|
1291
1318
|
if (!columnAlreadyPresent) {
|
|
1292
1319
|
db.exec(sql);
|
|
1293
1320
|
}
|
|
@@ -1250,6 +1250,15 @@ export class SqliteSessionMetadataStore {
|
|
|
1250
1250
|
.update(assignment.replacesDelegationId)
|
|
1251
1251
|
.digest('hex')
|
|
1252
1252
|
.slice(0, 48);
|
|
1253
|
+
const stopRequest = this.readMessageByIdSync(WORKHUB_COORDINATION_SESSION_ID, `whq_${abortSuffix}`);
|
|
1254
|
+
if (stopRequest) {
|
|
1255
|
+
const stopResolution = this.readMessageByIdSync(WORKHUB_COORDINATION_SESSION_ID, `whz_${abortSuffix}`);
|
|
1256
|
+
if (stopResolution?.type !== 'workhub_coordination' ||
|
|
1257
|
+
stopResolution.kind !== 'delegation_stop_resolved' ||
|
|
1258
|
+
stopResolution.outcome !== 'not_owned') {
|
|
1259
|
+
throw new SessionMetadataConflictError('WorkHub delegation already has a stop claim');
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1253
1262
|
const existingAbort = this.readMessageByIdSync(WORKHUB_COORDINATION_SESSION_ID, `whb_${abortSuffix}`);
|
|
1254
1263
|
if (existingAbort) {
|
|
1255
1264
|
throw new SessionMetadataConflictError('WorkHub delegation replacement is aborted');
|
|
@@ -1348,6 +1357,106 @@ export class SqliteSessionMetadataStore {
|
|
|
1348
1357
|
return row !== undefined;
|
|
1349
1358
|
});
|
|
1350
1359
|
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Binds one WorkHub action identity to one exact operation, for good.
|
|
1362
|
+
*
|
|
1363
|
+
* Every other durable WorkHub record is keyed by what it is about, so none of
|
|
1364
|
+
* them can see an action id that moved to a second delegation or a second
|
|
1365
|
+
* disposition. This row is the global owner that rejects both, and it is
|
|
1366
|
+
* written before the action's effect so a rejected or recovering attempt can
|
|
1367
|
+
* never leak its identity into a different operation.
|
|
1368
|
+
*/
|
|
1369
|
+
async claimWorkHubAction(claim) {
|
|
1370
|
+
this.assertOpen();
|
|
1371
|
+
assertSafeSessionId(claim.actionId);
|
|
1372
|
+
assertSafeSessionId(claim.subject);
|
|
1373
|
+
if (!/^sha256:[a-f0-9]{64}$/u.test(claim.actionFingerprint)) {
|
|
1374
|
+
throw new SessionMetadataConflictError('Invalid WorkHub action fingerprint');
|
|
1375
|
+
}
|
|
1376
|
+
return this.transaction(() => {
|
|
1377
|
+
const existing = this.readWorkHubActionClaimSync(claim.actionId);
|
|
1378
|
+
if (existing) {
|
|
1379
|
+
return existing.operation === claim.operation &&
|
|
1380
|
+
existing.actionFingerprint === claim.actionFingerprint &&
|
|
1381
|
+
existing.subject === claim.subject
|
|
1382
|
+
? 'same_claim'
|
|
1383
|
+
: 'conflict';
|
|
1384
|
+
}
|
|
1385
|
+
this.db
|
|
1386
|
+
.prepare(`
|
|
1387
|
+
INSERT INTO workhub_action_claims(
|
|
1388
|
+
action_id, operation, action_fingerprint, subject, claimed_at
|
|
1389
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
1390
|
+
`)
|
|
1391
|
+
.run(claim.actionId, claim.operation, claim.actionFingerprint, claim.subject, this.now());
|
|
1392
|
+
return 'claimed';
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
async readWorkHubActionClaim(actionId) {
|
|
1396
|
+
this.assertOpen();
|
|
1397
|
+
assertSafeSessionId(actionId);
|
|
1398
|
+
return this.readTransaction(() => this.readWorkHubActionClaimSync(actionId));
|
|
1399
|
+
}
|
|
1400
|
+
readWorkHubActionClaimSync(actionId) {
|
|
1401
|
+
const row = this.db
|
|
1402
|
+
.prepare('SELECT operation, action_fingerprint, subject FROM workhub_action_claims WHERE action_id = ?')
|
|
1403
|
+
.get(actionId);
|
|
1404
|
+
if (!row)
|
|
1405
|
+
return undefined;
|
|
1406
|
+
if (!isWorkHubActionOperation(row.operation) ||
|
|
1407
|
+
typeof row.action_fingerprint !== 'string' ||
|
|
1408
|
+
!/^sha256:[a-f0-9]{64}$/u.test(row.action_fingerprint) ||
|
|
1409
|
+
typeof row.subject !== 'string') {
|
|
1410
|
+
throw new SessionMetadataConflictError('Invalid WorkHub action claim row');
|
|
1411
|
+
}
|
|
1412
|
+
return {
|
|
1413
|
+
actionId,
|
|
1414
|
+
operation: row.operation,
|
|
1415
|
+
actionFingerprint: row.action_fingerprint,
|
|
1416
|
+
subject: row.subject,
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
async claimMessageAdmissionCancellation(sessionId, messageId, claimId) {
|
|
1420
|
+
this.assertOpen();
|
|
1421
|
+
assertSafeSessionId(sessionId);
|
|
1422
|
+
assertSafeSessionId(messageId);
|
|
1423
|
+
assertSafeSessionId(claimId);
|
|
1424
|
+
return this.transaction(() => {
|
|
1425
|
+
const cancelled = this.db
|
|
1426
|
+
.prepare('SELECT cancellation_claim_id FROM cancelled_message_admissions WHERE session_id = ? AND message_id = ?')
|
|
1427
|
+
.get(sessionId, messageId);
|
|
1428
|
+
if (cancelled) {
|
|
1429
|
+
return cancelled.cancellation_claim_id === claimId ? 'same_claim' : 'already_cancelled';
|
|
1430
|
+
}
|
|
1431
|
+
const admission = this.db
|
|
1432
|
+
.prepare(`
|
|
1433
|
+
SELECT submitted_content_digest, submitted_placement
|
|
1434
|
+
FROM message_admissions
|
|
1435
|
+
WHERE session_id = ? AND message_id = ?
|
|
1436
|
+
`)
|
|
1437
|
+
.get(sessionId, messageId);
|
|
1438
|
+
if (typeof admission?.submitted_content_digest !== 'string' ||
|
|
1439
|
+
(admission.submitted_placement !== 'current_turn' &&
|
|
1440
|
+
admission.submitted_placement !== 'next_turn')) {
|
|
1441
|
+
throw new SessionMetadataConflictError('Message admission cancellation identity conflict');
|
|
1442
|
+
}
|
|
1443
|
+
this.db
|
|
1444
|
+
.prepare(`
|
|
1445
|
+
INSERT INTO cancelled_message_admissions(
|
|
1446
|
+
session_id, message_id, submitted_content_digest, submitted_placement,
|
|
1447
|
+
cancellation_claim_id
|
|
1448
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
1449
|
+
`)
|
|
1450
|
+
.run(sessionId, messageId, admission.submitted_content_digest, admission.submitted_placement, claimId);
|
|
1451
|
+
const deleted = this.db
|
|
1452
|
+
.prepare('DELETE FROM message_admissions WHERE session_id = ? AND message_id = ?')
|
|
1453
|
+
.run(sessionId, messageId);
|
|
1454
|
+
if (deleted.changes !== 1) {
|
|
1455
|
+
throw new SessionMetadataConflictError('Message admission cancellation identity conflict');
|
|
1456
|
+
}
|
|
1457
|
+
return 'cancelled_by_claim';
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1351
1460
|
async listMessageAdmissions(sessionId) {
|
|
1352
1461
|
this.assertOpen();
|
|
1353
1462
|
assertSafeSessionId(sessionId);
|
|
@@ -4938,6 +5047,14 @@ function readStoredMessageRecordJson(db, sessionId, sequence, row) {
|
|
|
4938
5047
|
}
|
|
4939
5048
|
return recordJson;
|
|
4940
5049
|
}
|
|
5050
|
+
function isWorkHubActionOperation(value) {
|
|
5051
|
+
return (value === 'answer_here' ||
|
|
5052
|
+
value === 'clarify' ||
|
|
5053
|
+
value === 'delegate_existing' ||
|
|
5054
|
+
value === 'create_new' ||
|
|
5055
|
+
value === 'replace' ||
|
|
5056
|
+
value === 'stop');
|
|
5057
|
+
}
|
|
4941
5058
|
function sameWorkHubAssignmentRequest(existing, requested) {
|
|
4942
5059
|
return isDeepStrictEqual({
|
|
4943
5060
|
actionId: existing.actionId,
|
|
@@ -117,6 +117,7 @@ class SqliteTelemetryRepo {
|
|
|
117
117
|
range: { from, to },
|
|
118
118
|
totalRequests: rows.length,
|
|
119
119
|
totalCostUsd: sum(rows.map((row) => row.costUsd)),
|
|
120
|
+
totalDurationMs: sum(rows.map((row) => row.latencyMs)),
|
|
120
121
|
totalTokens: {
|
|
121
122
|
input: sum(rows.map((row) => row.inputTokens)),
|
|
122
123
|
output: sum(rows.map((row) => row.outputTokens)),
|
|
@@ -131,6 +132,15 @@ class SqliteTelemetryRepo {
|
|
|
131
132
|
errorRequests: rows.filter((row) => row.status === 'error').length,
|
|
132
133
|
});
|
|
133
134
|
}
|
|
135
|
+
toolSummary(query) {
|
|
136
|
+
this.assertReady();
|
|
137
|
+
const { from, to } = resolveRange(query.range);
|
|
138
|
+
const rows = this.filteredToolRows(query, from, to);
|
|
139
|
+
return detached({
|
|
140
|
+
requests: rows.length,
|
|
141
|
+
durationMs: sum(rows.map((row) => row.durationMs)),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
134
144
|
buckets(query, groupBy) {
|
|
135
145
|
this.assertReady();
|
|
136
146
|
const { from, to } = resolveRange(query.range);
|
|
@@ -226,13 +236,22 @@ class SqliteTelemetryRepo {
|
|
|
226
236
|
});
|
|
227
237
|
}
|
|
228
238
|
filteredToolRows(query, from, to) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
// One filter policy for every tool read — summary, buckets, and logs — so
|
|
240
|
+
// two views of one query cannot disagree. Fields the narrower query types
|
|
241
|
+
// do not carry simply never match a row out of range.
|
|
242
|
+
return this.readToolRows(from, to).filter((row) => {
|
|
232
243
|
if (query.toolName && row.toolName !== query.toolName)
|
|
233
244
|
return false;
|
|
234
245
|
if (query.status && query.status !== 'all' && row.status !== query.status)
|
|
235
246
|
return false;
|
|
247
|
+
if ('sessionId' in query && query.sessionId && row.sessionId !== query.sessionId) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
if ('providerId' in query && query.providerId && row.providerId !== query.providerId) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
if ('modelId' in query && query.modelId && row.modelId !== query.modelId)
|
|
254
|
+
return false;
|
|
236
255
|
return true;
|
|
237
256
|
});
|
|
238
257
|
}
|
|
@@ -245,10 +264,14 @@ class SqliteTelemetryRepo {
|
|
|
245
264
|
WHERE ts >= ? AND ts <= ?`)
|
|
246
265
|
.all(...(sessionId ? [sessionId, from, to] : [from, to])).map((row) => decodePersistedLlmCallRecord(JSON.parse(row.record_json)));
|
|
247
266
|
}
|
|
248
|
-
|
|
267
|
+
// The ts range goes into the query, not the decode: the invocation ledger
|
|
268
|
+
// has no retention, and a full-table decode per summary read would only grow
|
|
269
|
+
// with it. The `(ts, id)` index answers the range; the remaining filters run
|
|
270
|
+
// over the few decoded rows it returns.
|
|
271
|
+
readToolRows(from, to) {
|
|
249
272
|
return this.#lease.database
|
|
250
|
-
.prepare('SELECT record_json FROM usage_tool_invocations')
|
|
251
|
-
.all().map((row) => decodePersistedToolInvocationRecord(JSON.parse(row.record_json)));
|
|
273
|
+
.prepare('SELECT record_json FROM usage_tool_invocations WHERE ts >= ? AND ts <= ?')
|
|
274
|
+
.all(from, to).map((row) => decodePersistedToolInvocationRecord(JSON.parse(row.record_json)));
|
|
252
275
|
}
|
|
253
276
|
enqueueMutation(operation) {
|
|
254
277
|
this.assertReady();
|
|
@@ -282,12 +282,13 @@ function createWriterFacade(lease, telemetry, modelCalls, pricing) {
|
|
|
282
282
|
[writerBrand]: true,
|
|
283
283
|
telemetry: {
|
|
284
284
|
summary: (query) => read(() => telemetry.summary(query)),
|
|
285
|
+
toolSummary: (query) => read(() => telemetry.toolSummary(query)),
|
|
285
286
|
buckets: (query, groupBy) => read(() => telemetry.buckets(query, groupBy)),
|
|
286
287
|
logs: (query, offset, limit) => read(() => telemetry.logs(query, offset, limit)),
|
|
287
288
|
toolLogs: (query, offset, limit) => read(() => telemetry.toolLogs(query, offset, limit)),
|
|
288
289
|
latestLlmRuntimeProbe: (connectionSlug, modelId) => read(() => telemetry.latestLlmRuntimeProbe(connectionSlug, modelId)),
|
|
289
290
|
recordLlmCall: (record) => admitSessionUsageMutation(record.sessionId, () => telemetry.insertLlmCall(record)),
|
|
290
|
-
recordToolInvocation: (record) =>
|
|
291
|
+
recordToolInvocation: (record) => admitSessionUsageMutation(record.sessionId, () => telemetry.insertToolInvocation(record)),
|
|
291
292
|
},
|
|
292
293
|
modelCalls: {
|
|
293
294
|
modelCallAttempts: (range, sessionId) => read(() => modelCalls.read(range, sessionId)),
|
|
@@ -313,6 +314,7 @@ function createWriterFacade(lease, telemetry, modelCalls, pricing) {
|
|
|
313
314
|
function telemetryReader(repo, run) {
|
|
314
315
|
return Object.freeze({
|
|
315
316
|
summary: (query) => run(() => repo.summary(query)),
|
|
317
|
+
toolSummary: (query) => run(() => repo.toolSummary(query)),
|
|
316
318
|
buckets: (query, groupBy) => run(() => repo.buckets(query, groupBy)),
|
|
317
319
|
logs: (query, offset, limit) => run(() => repo.logs(query, offset, limit)),
|
|
318
320
|
toolLogs: (query, offset, limit) => run(() => repo.toolLogs(query, offset, limit)),
|