maka-agent 0.2.0-dev.22.20260905 → 0.2.0-dev.23.20260906
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/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/bot-events.js +18 -63
- package/node_modules/@maka/core/dist/model-call-attempt.js +0 -82
- package/node_modules/@maka/core/dist/model-call-usage-projection.js +0 -186
- package/node_modules/@maka/core/dist/record-schema.js +13 -0
- package/node_modules/@maka/core/dist/redaction.js +41 -58
- package/node_modules/@maka/core/dist/session-trace.js +5 -1
- package/node_modules/@maka/core/dist/usage-ledger-merge.js +7 -9
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +34 -20
- package/node_modules/@maka/eval/harbor/egress-proxy/entrypoint.sh +2 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +51 -24
- package/node_modules/@maka/runtime/dist/bots/base-adapter.js +17 -0
- package/node_modules/@maka/runtime/dist/bots/bot-test.js +38 -22
- package/node_modules/@maka/runtime/dist/bots/dingtalk-bridge.js +7 -5
- package/node_modules/@maka/runtime/dist/bots/discord-bridge.js +4 -4
- package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +4 -5
- package/node_modules/@maka/runtime/dist/bots/qq-bridge.js +6 -4
- package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +3 -4
- package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +4 -5
- package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +17 -19
- package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +5 -6
- package/node_modules/@maka/runtime/dist/bots/ws-bridge-base.js +2 -3
- package/node_modules/@maka/runtime/dist/session-trace-projection.js +3 -30
- package/node_modules/@maka/runtime-host/dist/candidate-entry.js +4 -1
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +13 -0
- package/node_modules/@maka/runtime-host/dist/client/registered-host-termination.js +0 -59
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +28 -16
- package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +4 -0
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +8 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +13 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +28 -1
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +33 -0
- package/node_modules/@maka/runtime-host/dist/server/access-authority.js +26 -1
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +5 -1
- package/node_modules/@maka/runtime-host/dist/server/canonical-usage-reader.js +24 -11
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +7 -4
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +70 -6
- package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +6 -3
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +12 -6
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +9 -2
- package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +7 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +21 -0
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +151 -5
- package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +4 -11
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -15
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +6 -1
- package/node_modules/@maka/storage/dist/agent-run-store.js +44 -0
- package/node_modules/@maka/storage/dist/conversation-operational-state.js +4 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +2 -0
- package/node_modules/@maka/storage/dist/model-call-ledger.js +199 -32
- package/node_modules/@maka/storage/dist/model-call-usage-sql.js +136 -0
- package/node_modules/@maka/storage/dist/operational-state-store-public.js +1 -1
- package/node_modules/@maka/storage/dist/operational-state-store.js +5 -3
- package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +11 -1
- package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +200 -20
- package/node_modules/@maka/storage/dist/usage-stores.js +6 -2
- package/package.json +1 -1
|
@@ -274,6 +274,27 @@ export function decodeWorkHubCoordinationActResult(value) {
|
|
|
274
274
|
}),
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
|
+
if (result.disposition === 'resume_work') {
|
|
278
|
+
const exact = requireShapedRecord(result, 'WorkHub Coordination resume result', ['disposition', 'outcome', 'targetSessionId'], ['targetTurnId']);
|
|
279
|
+
if (exact.outcome !== 'resume_started' && exact.outcome !== 'already_running') {
|
|
280
|
+
throw invalidProtocolFrame('Invalid WorkHub resume outcome');
|
|
281
|
+
}
|
|
282
|
+
// Only a started continuation names a Turn: the Host has one to name, and
|
|
283
|
+
// the other two outcomes changed nothing that could carry an identity.
|
|
284
|
+
if ((exact.outcome === 'resume_started') !== (exact.targetTurnId !== undefined)) {
|
|
285
|
+
throw invalidProtocolFrame('Invalid WorkHub resume target Turn');
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
disposition: 'resume_work',
|
|
289
|
+
outcome: exact.outcome,
|
|
290
|
+
targetSessionId: requireEntityId(exact.targetSessionId, 'WorkHub target Session id'),
|
|
291
|
+
...(exact.targetTurnId === undefined
|
|
292
|
+
? {}
|
|
293
|
+
: {
|
|
294
|
+
targetTurnId: requireEntityId(exact.targetTurnId, 'WorkHub target Turn id'),
|
|
295
|
+
}),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
277
298
|
throw invalidProtocolFrame('Invalid WorkHub Coordination action disposition');
|
|
278
299
|
}
|
|
279
300
|
function decodeWorkHubCoordinationCandidate(value) {
|
|
@@ -372,6 +393,18 @@ function decodeWorkHubCoordinationProposal(value) {
|
|
|
372
393
|
expects: decodeWorkHubCoordinationStopPreconditions(exact.expects),
|
|
373
394
|
};
|
|
374
395
|
}
|
|
396
|
+
if (proposal.disposition === 'resume_work') {
|
|
397
|
+
const exact = requireExactRecord(proposal, 'WorkHub resume proposal', [
|
|
398
|
+
'disposition',
|
|
399
|
+
'expects',
|
|
400
|
+
'resumesActionId',
|
|
401
|
+
]);
|
|
402
|
+
return {
|
|
403
|
+
disposition: 'resume_work',
|
|
404
|
+
resumesActionId: requireEntityId(exact.resumesActionId, 'WorkHub resume assignment'),
|
|
405
|
+
expects: decodeWorkHubCoordinationStopPreconditions(exact.expects),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
375
408
|
throw invalidProtocolFrame('Invalid WorkHub Coordination proposal disposition');
|
|
376
409
|
}
|
|
377
410
|
function decodeWorkHubCoordinationStopPreconditions(value) {
|
|
@@ -20,7 +20,7 @@ import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import { isDeepStrictEqual } from 'node:util';
|
|
22
22
|
import { runtimeHostAccessCredentialHash } from '../access-credential-identity.js';
|
|
23
|
-
import { encodeCollaborationInvitationCode, } from '../protocol/index.js';
|
|
23
|
+
import { decodeCollaborationDisplayName, encodeCollaborationInvitationCode, } from '../protocol/index.js';
|
|
24
24
|
import { createRuntimeHostConnectionAuthority, } from './connection-authority.js';
|
|
25
25
|
import { createAccessCredentialDelivery, discardAccessCredentialDelivery, purgeAccessCredentialDeliveries, } from '../control/access-credential-delivery.js';
|
|
26
26
|
import { ACCESS_FILE_NAME, assertAccessCredentialFileCapacity, CAPABILITY_PROVIDER_OPERATION_GRANTS, createAccessCredentialFile, effectiveOperationGrants, issuedAccessGrants, readAccessCredentialFile, RuntimeHostAccessCommitOutcomeUnknownError, RuntimeHostAccessInputError, SESSION_GUEST_OPERATION_GRANTS, writeAccessCredentialFile, } from './access-credential-store.js';
|
|
@@ -152,6 +152,9 @@ class FileRuntimeHostAccessAuthority {
|
|
|
152
152
|
principalId,
|
|
153
153
|
status: credential.status,
|
|
154
154
|
createdAt: credential.createdAt,
|
|
155
|
+
...(credential.displayName === undefined
|
|
156
|
+
? {}
|
|
157
|
+
: { displayName: credential.displayName }),
|
|
155
158
|
...(credential.expiresAt ? { expiresAt: credential.expiresAt } : {}),
|
|
156
159
|
},
|
|
157
160
|
]
|
|
@@ -176,6 +179,18 @@ class FileRuntimeHostAccessAuthority {
|
|
|
176
179
|
principalId,
|
|
177
180
|
});
|
|
178
181
|
}
|
|
182
|
+
renameCollaborationPrincipal(input) {
|
|
183
|
+
const displayName = decodeCollaborationDisplayName(input.displayName);
|
|
184
|
+
return this.#mutate(async () => {
|
|
185
|
+
const matches = (credential) => credential.principalKind === 'session_guest' &&
|
|
186
|
+
credential.principalId === input.principalId &&
|
|
187
|
+
credential.status !== 'revoked';
|
|
188
|
+
if (!this.#file.credentials.some(matches))
|
|
189
|
+
return { renamed: false };
|
|
190
|
+
await this.#commit(createNextAccessCredentialFile(this.#file, this.#file.credentials.map((credential) => matches(credential) ? { ...credential, displayName } : credential), this.#file.sessionGrants), []);
|
|
191
|
+
return { renamed: true };
|
|
192
|
+
});
|
|
193
|
+
}
|
|
179
194
|
createTurnAccessRequest(principalId, input) {
|
|
180
195
|
return this.#mutate(async () => {
|
|
181
196
|
const grant = this.activeSessionGrant(principalId, input.intent.sessionId, 'session_turn_request');
|
|
@@ -960,6 +975,16 @@ export async function revokeCollaborationPrincipal(authority, principalId) {
|
|
|
960
975
|
return accessPersistenceFailure(error, 'Collaboration Guest revocation outcome is unknown', 'Collaboration Guest could not be revoked');
|
|
961
976
|
}
|
|
962
977
|
}
|
|
978
|
+
export async function renameCollaborationPrincipal(authority, input) {
|
|
979
|
+
if (!authority)
|
|
980
|
+
return collaborationUnavailable('collaboration.principal.rename');
|
|
981
|
+
try {
|
|
982
|
+
return { ok: true, result: await authority.renameCollaborationPrincipal(input) };
|
|
983
|
+
}
|
|
984
|
+
catch (error) {
|
|
985
|
+
return accessPersistenceFailure(error, 'Guest alias commit outcome is unknown', 'Guest alias could not be saved');
|
|
986
|
+
}
|
|
987
|
+
}
|
|
963
988
|
function collaborationUnavailable(operation) {
|
|
964
989
|
return {
|
|
965
990
|
ok: false,
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { randomUUID } from 'node:crypto';
|
|
20
20
|
import { chmod, open, rename, rm } from 'node:fs/promises';
|
|
21
21
|
import { dirname } from 'node:path';
|
|
22
|
-
import { HOST_OPERATION_SPECS, operationAllowsRemoteOwner, decodeSessionTurnAccessRequest, } from '../protocol/index.js';
|
|
22
|
+
import { HOST_OPERATION_SPECS, operationAllowsRemoteOwner, decodeSessionTurnAccessRequest, decodeCollaborationDisplayName, } from '../protocol/index.js';
|
|
23
23
|
// Schema 4 makes provider ownership downgrade-safe once an association exists.
|
|
24
24
|
// Ordinary access files remain schema 3 so this feature does not fence a
|
|
25
25
|
// downgrade before there is an owner association to preserve.
|
|
@@ -225,6 +225,7 @@ function encodeAccessCredentialFile(file) {
|
|
|
225
225
|
schemaVersion: file.schemaVersion,
|
|
226
226
|
credentials: file.credentials.map((credential) => ({
|
|
227
227
|
credentialId: credential.credentialId,
|
|
228
|
+
...(credential.displayName === undefined ? {} : { displayName: credential.displayName }),
|
|
228
229
|
credentialHash: credential.credentialHash,
|
|
229
230
|
principalId: credential.principalId,
|
|
230
231
|
principalKind: credential.principalKind,
|
|
@@ -391,6 +392,9 @@ function decodeStoredCredential(value) {
|
|
|
391
392
|
...(typeof clientInstanceId === 'string' ? { clientInstanceId } : {}),
|
|
392
393
|
...(typeof expiresAt === 'string' ? { expiresAt } : {}),
|
|
393
394
|
...(revokedAt === undefined ? {} : { revokedAt }),
|
|
395
|
+
...(value.displayName === undefined
|
|
396
|
+
? {}
|
|
397
|
+
: { displayName: decodeCollaborationDisplayName(value.displayName) }),
|
|
394
398
|
};
|
|
395
399
|
}
|
|
396
400
|
function decodeStoredSessionGrant(value) {
|
|
@@ -16,33 +16,46 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
import { resolveUsageRange } from '@maka/core/model-call-usage-projection';
|
|
20
19
|
export class CanonicalUsageProjectionIncompleteError extends Error {
|
|
21
20
|
constructor() {
|
|
22
21
|
super('Canonical Usage projection is incomplete');
|
|
23
22
|
this.name = 'CanonicalUsageProjectionIncompleteError';
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Repairs the projection, then asks the ledger for one answer.
|
|
27
|
+
*
|
|
28
|
+
* `catchUpModelCallProjection` is a write. Paged log reads call this once per
|
|
29
|
+
* page, so a caller that already repaired on its first page passes
|
|
30
|
+
* `repair: false` on later pages to avoid a redundant repair write per page.
|
|
31
|
+
*/
|
|
32
|
+
async function readCanonical(stores, query, repair, ask) {
|
|
31
33
|
const repairOutcome = repair
|
|
32
34
|
? await stores.modelCalls
|
|
33
35
|
.catchUpModelCallProjection(query.sessionId === undefined ? undefined : { sessionId: query.sessionId })
|
|
34
36
|
.catch(() => ({ pendingRuns: 1, unreadableEvents: 0 }))
|
|
35
37
|
: { pendingRuns: 0, unreadableEvents: 0 };
|
|
36
|
-
const
|
|
38
|
+
const answer = await ask();
|
|
37
39
|
return {
|
|
38
|
-
|
|
39
|
-
unreadableRecords:
|
|
40
|
+
projection: answer.projection,
|
|
41
|
+
unreadableRecords: answer.unreadableRecords + repairOutcome.unreadableEvents,
|
|
40
42
|
pendingRepairs: repairOutcome.pendingRuns,
|
|
41
43
|
};
|
|
42
44
|
}
|
|
45
|
+
export function readCanonicalUsageSummary(stores, query, now, repair = true) {
|
|
46
|
+
return readCanonical(stores, query, repair, () => stores.modelCalls.modelCallSummary(query, now));
|
|
47
|
+
}
|
|
48
|
+
export function readCanonicalUsageBuckets(stores, query, groupBy, now, repair = true) {
|
|
49
|
+
return readCanonical(stores, query, repair, () => stores.modelCalls.modelCallBuckets(query, groupBy, now));
|
|
50
|
+
}
|
|
51
|
+
export function readCanonicalUsageLogs(stores, query, now, limit, repair = true) {
|
|
52
|
+
// Both sources are newest-first, so a merged page can only be drawn from each
|
|
53
|
+
// source's own first `offset + limit` rows.
|
|
54
|
+
return readCanonical(stores, query, repair, () => stores.modelCalls.modelCallLogs(query, now, 0, limit));
|
|
55
|
+
}
|
|
43
56
|
/** Runs one bounded repair pass and rejects data still unsafe for durable derivatives. */
|
|
44
|
-
export async function
|
|
45
|
-
const source = await
|
|
57
|
+
export async function readCompleteCanonicalUsageSummary(stores, query, now) {
|
|
58
|
+
const source = await readCanonicalUsageSummary(stores, query, now);
|
|
46
59
|
if (source.unreadableRecords > 0 || source.pendingRepairs > 0) {
|
|
47
60
|
throw new CanonicalUsageProjectionIncompleteError();
|
|
48
61
|
}
|
|
@@ -22,7 +22,7 @@ import { mergeUsageBuckets, mergeUsageSummary } from '@maka/core/usage-ledger-me
|
|
|
22
22
|
import { authenticateInteractiveDailyReviewAuthorityWriter, } from '@maka/storage/daily-review-authority';
|
|
23
23
|
import { authenticateInteractiveUsageStoresWriter, } from '@maka/storage/usage-stores';
|
|
24
24
|
import { DAILY_REVIEW_RESULT_MAX_BYTES, } from '../protocol/index.js';
|
|
25
|
-
import { CanonicalUsageProjectionIncompleteError,
|
|
25
|
+
import { CanonicalUsageProjectionIncompleteError, readCanonicalUsageBuckets, readCompleteCanonicalUsageSummary, } from './canonical-usage-reader.js';
|
|
26
26
|
const ARCHIVE_LIMIT = 180;
|
|
27
27
|
const SCHEDULER_INTERVAL_MS = 60_000;
|
|
28
28
|
/** Host-owned Daily Review data, generation, archive, and scheduling boundary. */
|
|
@@ -196,7 +196,10 @@ export class HostDailyReviewCoordinator {
|
|
|
196
196
|
}
|
|
197
197
|
async #buildSummary(range, now) {
|
|
198
198
|
const query = dailyUsageQuery(range);
|
|
199
|
-
|
|
199
|
+
// The summary read repairs the projection and refuses an incomplete one; the
|
|
200
|
+
// bucket read that follows reuses that pass rather than repairing again.
|
|
201
|
+
const canonical = await readCompleteCanonicalUsageSummary(this.#usage, query, now);
|
|
202
|
+
const canonicalModels = await readCanonicalUsageBuckets(this.#usage, query, 'model', now, false);
|
|
200
203
|
const [usageSummary, toolBuckets, modelBuckets, sessions] = await Promise.all([
|
|
201
204
|
this.#usage.telemetry.summary(query),
|
|
202
205
|
this.#usage.telemetry.buckets(query, 'tool'),
|
|
@@ -205,10 +208,10 @@ export class HostDailyReviewCoordinator {
|
|
|
205
208
|
]);
|
|
206
209
|
return buildDailyReviewSummary({
|
|
207
210
|
day: range,
|
|
208
|
-
usageSummary: mergeUsageSummary(usageSummary, canonical
|
|
211
|
+
usageSummary: mergeUsageSummary(usageSummary, canonical),
|
|
209
212
|
sessions: pickDailyReviewSessions(collapseSessionRevisions(sessions), range, DAILY_REVIEW_LIST_LIMIT),
|
|
210
213
|
topTools: pickDailyReviewTopEntries(toolBuckets, DAILY_REVIEW_LIST_LIMIT),
|
|
211
|
-
topModels: pickDailyReviewTopEntries(mergeUsageBuckets(modelBuckets,
|
|
214
|
+
topModels: pickDailyReviewTopEntries(mergeUsageBuckets(modelBuckets, canonicalModels).buckets, DAILY_REVIEW_LIST_LIMIT),
|
|
212
215
|
});
|
|
213
216
|
}
|
|
214
217
|
async #run(input) {
|
|
@@ -119,7 +119,7 @@ import { HostTurnControlCoordinator } from './turn-control-coordinator.js';
|
|
|
119
119
|
import { HostUsagePricingCoordinator } from './usage-pricing-coordinator.js';
|
|
120
120
|
import { HostWebSearchCoordinator } from './web-search-coordinator.js';
|
|
121
121
|
import { HostWorkHubCoordinationCoordinator } from './workhub-coordination-coordinator.js';
|
|
122
|
-
import { WorkHubActionEffectFailure } from './workhub-coordination-action-gate.js';
|
|
122
|
+
import { WorkHubActionEffectFailure, workHubResumedTurnId, } from './workhub-coordination-action-gate.js';
|
|
123
123
|
import { createHostWebSearchService, createHostWebSearchToolFromService, resolveHostTavilyWebSearchReadiness, shouldResolveHostTavilyWebSearchReadiness, } from './web-search-tool.js';
|
|
124
124
|
import { createHostWebFetchService, createHostWebFetchToolFromService } from './web-fetch-tool.js';
|
|
125
125
|
import { createHostExecutionArtifactServices } from './execution-artifacts.js';
|
|
@@ -391,6 +391,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
|
|
|
391
391
|
let deepResearch;
|
|
392
392
|
let dailyReview;
|
|
393
393
|
const rootPort = {
|
|
394
|
+
readLatestRootTurnLineage: (identity) => requireRootCoordinator(rootCoordinator).readLatestRootTurnLineage(identity),
|
|
394
395
|
readSessionHeader: (sessionId) => requireRootCoordinator(rootCoordinator).readSessionHeader(sessionId),
|
|
395
396
|
readRootState: (sessionId) => requireRootCoordinator(rootCoordinator).readRootState(sessionId),
|
|
396
397
|
claimStopFence: (input, commitQueueFence, admission) => requireRootCoordinator(rootCoordinator).claimStopFence(input, commitQueueFence, admission),
|
|
@@ -1054,13 +1055,76 @@ export async function createExecutionRuntimeHostComposition(context, options = {
|
|
|
1054
1055
|
turnId: disposition.turnId,
|
|
1055
1056
|
runId: disposition.runId,
|
|
1056
1057
|
};
|
|
1057
|
-
|
|
1058
|
+
const latest = await coordinator.readLatestRootTurnLineage(identity);
|
|
1059
|
+
if (isActiveWorkHubRoot(coordinator, latest))
|
|
1058
1060
|
return 'not_retired';
|
|
1059
1061
|
// The same restart window as `stopOwnedWorkHubRoot`: an unregistered
|
|
1060
1062
|
// root is not evidence that its work ended.
|
|
1061
|
-
const snapshot = await coordinator.read(
|
|
1063
|
+
const snapshot = await coordinator.read(latest);
|
|
1062
1064
|
return isHostedExecutionTerminal(snapshot) ? 'retired' : 'recovering';
|
|
1063
1065
|
},
|
|
1066
|
+
// Resolve and resume only the execution lineage owned by this
|
|
1067
|
+
// delegation. A Session-wide latest-failure query could otherwise
|
|
1068
|
+
// continue unrelated work started directly in the same Session.
|
|
1069
|
+
resumeDelegation: async (assignment, context, actionId) => {
|
|
1070
|
+
const disposition = await messages.readMessageExecutionDisposition(assignment.targetSessionId, assignment.targetMessageId);
|
|
1071
|
+
if (disposition.kind === 'recovering') {
|
|
1072
|
+
throw new WorkHubActionEffectFailure('host_not_ready', 'WorkHub is still recovering the delegated execution');
|
|
1073
|
+
}
|
|
1074
|
+
if (disposition.kind !== 'owned_root') {
|
|
1075
|
+
throw new WorkHubActionEffectFailure('operation_conflict', 'WorkHub delegated execution is not resumable');
|
|
1076
|
+
}
|
|
1077
|
+
const source = await coordinator.readLatestRootTurnLineage({
|
|
1078
|
+
sessionId: assignment.targetSessionId,
|
|
1079
|
+
turnId: disposition.turnId,
|
|
1080
|
+
runId: disposition.runId,
|
|
1081
|
+
});
|
|
1082
|
+
if (isActiveWorkHubRoot(coordinator, source)) {
|
|
1083
|
+
return { outcome: 'already_running' };
|
|
1084
|
+
}
|
|
1085
|
+
const snapshot = await coordinator.read(source);
|
|
1086
|
+
if (!isHostedExecutionTerminal(snapshot)) {
|
|
1087
|
+
throw new WorkHubActionEffectFailure('host_not_ready', 'WorkHub is still recovering the delegated execution');
|
|
1088
|
+
}
|
|
1089
|
+
if (snapshot.status !== 'failed' && snapshot.status !== 'cancelled') {
|
|
1090
|
+
throw new WorkHubActionEffectFailure('operation_conflict', 'WorkHub delegated execution is not resumable');
|
|
1091
|
+
}
|
|
1092
|
+
const plan = await coordinator.handlers['turn.resume.query']({ sessionId: assignment.targetSessionId, sourceRunId: source.runId }, context);
|
|
1093
|
+
if (!plan.ok)
|
|
1094
|
+
throw new WorkHubActionEffectFailure(plan.error.code, plan.error.message);
|
|
1095
|
+
if (plan.result.disposition === 'parked') {
|
|
1096
|
+
throw new WorkHubActionEffectFailure(plan.result.reason === 'resume_feature_disabled'
|
|
1097
|
+
? 'operation_unavailable'
|
|
1098
|
+
: 'operation_conflict', plan.result.reason === 'resume_feature_disabled'
|
|
1099
|
+
? 'Safe-boundary resume is disabled for this Runtime Host'
|
|
1100
|
+
: 'WorkHub delegated execution is not resumable');
|
|
1101
|
+
}
|
|
1102
|
+
if (plan.result.sourceRunId !== source.runId ||
|
|
1103
|
+
plan.result.sourceTurnId !== source.turnId) {
|
|
1104
|
+
throw new WorkHubActionEffectFailure('operation_conflict', 'WorkHub resume source lineage changed during planning');
|
|
1105
|
+
}
|
|
1106
|
+
const targetTurnId = workHubResumedTurnId(actionId);
|
|
1107
|
+
const started = await coordinator.handlers['turn.resume.start']({
|
|
1108
|
+
sessionId: assignment.targetSessionId,
|
|
1109
|
+
turnId: targetTurnId,
|
|
1110
|
+
sourceRunId: plan.result.sourceRunId,
|
|
1111
|
+
sourceRuntimeEventHighWater: plan.result.sourceRuntimeEventHighWater,
|
|
1112
|
+
}, context);
|
|
1113
|
+
if (!started.ok) {
|
|
1114
|
+
throw new WorkHubActionEffectFailure(started.error.code, started.error.message);
|
|
1115
|
+
}
|
|
1116
|
+
if (started.result.kind === 'parked') {
|
|
1117
|
+
throw new WorkHubActionEffectFailure(started.result.plan.reason === 'resume_feature_disabled'
|
|
1118
|
+
? 'operation_unavailable'
|
|
1119
|
+
: 'operation_conflict', started.result.plan.reason === 'resume_feature_disabled'
|
|
1120
|
+
? 'Safe-boundary resume is disabled for this Runtime Host'
|
|
1121
|
+
: 'WorkHub delegated execution is not resumable');
|
|
1122
|
+
}
|
|
1123
|
+
return {
|
|
1124
|
+
outcome: 'resume_started',
|
|
1125
|
+
targetTurnId: started.result.turn.turnId,
|
|
1126
|
+
};
|
|
1127
|
+
},
|
|
1064
1128
|
retireDelegation: async (assignment, retirement) => {
|
|
1065
1129
|
const disposition = await messages.cancelMessageIfPending(assignment.targetSessionId, assignment.targetMessageId, retirement.cancellationClaimId);
|
|
1066
1130
|
if (disposition.kind === 'recovering') {
|
|
@@ -1076,11 +1140,11 @@ export async function createExecutionRuntimeHostComposition(context, options = {
|
|
|
1076
1140
|
return { outcome: 'not_owned', targetTurnId: disposition.turnId };
|
|
1077
1141
|
}
|
|
1078
1142
|
if (disposition.kind === 'owned_root') {
|
|
1079
|
-
const identity = {
|
|
1143
|
+
const identity = await coordinator.readLatestRootTurnLineage({
|
|
1080
1144
|
sessionId: assignment.targetSessionId,
|
|
1081
1145
|
turnId: disposition.turnId,
|
|
1082
1146
|
runId: disposition.runId,
|
|
1083
|
-
};
|
|
1147
|
+
});
|
|
1084
1148
|
return retirement.cause === 'direct_stop'
|
|
1085
1149
|
? stopOwnedWorkHubRoot(coordinator, identity, retirement.cancellationClaimId)
|
|
1086
1150
|
: stopReplacedWorkHubRoot(coordinator, identity);
|
|
@@ -1205,7 +1269,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
|
|
|
1205
1269
|
},
|
|
1206
1270
|
},
|
|
1207
1271
|
resolveCreateTarget: async () => {
|
|
1208
|
-
const { projectId: _projectId, ...target } = await sessionCatalog.
|
|
1272
|
+
const { projectId: _projectId, ...target } = await sessionCatalog.resolveDefaultCreateTarget();
|
|
1209
1273
|
return { ...target, permissionMode: 'explore' };
|
|
1210
1274
|
},
|
|
1211
1275
|
requestDrain: context.requestDrain,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { ExternalSessionImporter } from '@maka/storage/external-sessions';
|
|
20
20
|
import { EXTERNAL_SESSION_CWD_MAX_BYTES, EXTERNAL_SESSION_IMPORTED_SESSION_IDS_MAX_ITEMS, EXTERNAL_SESSION_NAME_MAX_BYTES, EXTERNAL_SESSION_PAGE_MAX_ITEMS, EXTERNAL_SESSION_RESULT_MAX_BYTES, EXTERNAL_SESSION_SOURCE_MAX_ITEMS, EXTERNAL_SESSION_SOURCE_SESSION_ID_MAX_BYTES, } from '../protocol/index.js';
|
|
21
|
-
import { projectSessionCatalogRecord, SessionOperationFailure, } from './session-catalog-coordinator.js';
|
|
21
|
+
import { projectSessionCatalogRecord, SessionOperationFailure, NoUsableImportModelError, } from './session-catalog-coordinator.js';
|
|
22
22
|
import { WorkspaceResolutionError } from './workspace-resolver.js';
|
|
23
23
|
/** Source discovery/conversion stays host-side so raw transcripts never cross the wire. */
|
|
24
24
|
export class HostExternalSessionCoordinator {
|
|
@@ -172,6 +172,9 @@ export class HostExternalSessionCoordinator {
|
|
|
172
172
|
target = await this.#resolveTarget();
|
|
173
173
|
}
|
|
174
174
|
catch (error) {
|
|
175
|
+
if (error instanceof NoUsableImportModelError) {
|
|
176
|
+
return importFailure('model_unavailable', error.message);
|
|
177
|
+
}
|
|
175
178
|
if (error instanceof SessionOperationFailure) {
|
|
176
179
|
return importFailure(error.code, error.message);
|
|
177
180
|
}
|
|
@@ -194,9 +197,9 @@ export class HostExternalSessionCoordinator {
|
|
|
194
197
|
}
|
|
195
198
|
catch (error) {
|
|
196
199
|
if (!commitAttempted) {
|
|
197
|
-
return importFailure(isSourceSessionNotFound(error) ? 'not_found' : '
|
|
200
|
+
return importFailure(isSourceSessionNotFound(error) ? 'not_found' : 'source_unreadable', isSourceSessionNotFound(error)
|
|
198
201
|
? 'External Session does not exist'
|
|
199
|
-
: 'External Session could not be converted');
|
|
202
|
+
: 'External Session could not be read or converted');
|
|
200
203
|
}
|
|
201
204
|
this.#requestDrain();
|
|
202
205
|
return importFailure('commit_outcome_unknown', 'External Session import outcome is unknown; check the Session list before retrying');
|
|
@@ -25,7 +25,7 @@ import { removeHostRegistration, writeHostRegistration } from '../control/regist
|
|
|
25
25
|
import { decodeClientFrame, encodeProtocolMessage, HOST_OPERATION_SPECS, negotiateProtocol, RUNTIME_HOST_COMPATIBILITY_EPOCH, RUNTIME_HOST_PROTOCOL_VERSION, RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION, requireHostGeneration, } from '../protocol/index.js';
|
|
26
26
|
import { RuntimeHostConnectionSession, } from './connection-session.js';
|
|
27
27
|
import { composeOperationHandlers, createUnavailableDomainOperationHandlers, } from './operation-dispatcher.js';
|
|
28
|
-
import { issueAccessCredential, acknowledgeCollaborationTurnRequest, createCollaborationTurnRequest, decideCollaborationTurnRequest, withdrawCollaborationTurnRequest, finalizeAccessCredential, prepareCollaborationInvitation, queryCollaborationTurnRequests, prepareAccessCredential, prepareAccessCredentialRotation, replaceAccessCredential, revokeAccessCredential, revokeAccessPrincipal, revokeAccessCredentialRotation, revokeCollaborationGrant, revokeCollaborationPrincipal, } from './access-authority.js';
|
|
28
|
+
import { issueAccessCredential, acknowledgeCollaborationTurnRequest, createCollaborationTurnRequest, decideCollaborationTurnRequest, withdrawCollaborationTurnRequest, finalizeAccessCredential, prepareCollaborationInvitation, queryCollaborationTurnRequests, prepareAccessCredential, prepareAccessCredentialRotation, replaceAccessCredential, revokeAccessCredential, revokeAccessPrincipal, revokeAccessCredentialRotation, revokeCollaborationGrant, revokeCollaborationPrincipal, renameCollaborationPrincipal, } from './access-authority.js';
|
|
29
29
|
import { runtimeHostLogBuffer } from '../process-diagnostics.js';
|
|
30
30
|
import { startLocalRuntimeHostListenerSet, } from './listener-set.js';
|
|
31
31
|
import { HostResidencyRegistry } from './host-residency-registry.js';
|
|
@@ -164,7 +164,8 @@ export class RuntimeHostKernel {
|
|
|
164
164
|
get compositionDescriptor() {
|
|
165
165
|
return this.#options.composition.descriptor;
|
|
166
166
|
}
|
|
167
|
-
close() {
|
|
167
|
+
close(input) {
|
|
168
|
+
this.#shutdownReason ??= input?.reason;
|
|
168
169
|
this.#requestDrain();
|
|
169
170
|
return this.closed;
|
|
170
171
|
}
|
|
@@ -502,6 +503,7 @@ export class RuntimeHostKernel {
|
|
|
502
503
|
ok: true,
|
|
503
504
|
result: {
|
|
504
505
|
...this.#statusSnapshot(),
|
|
506
|
+
upgradeBlockingActivity: this.#hasUpgradeBlockingActivity(0),
|
|
505
507
|
compositionModules: this.#composition?.moduleIds ?? [],
|
|
506
508
|
residencies: this.#residencies.snapshot(),
|
|
507
509
|
protocolVersion: RUNTIME_HOST_PROTOCOL_VERSION,
|
|
@@ -531,7 +533,7 @@ export class RuntimeHostKernel {
|
|
|
531
533
|
},
|
|
532
534
|
};
|
|
533
535
|
}
|
|
534
|
-
if (!input.allowInterruptActiveTasks && this.#hasUpgradeBlockingActivity()) {
|
|
536
|
+
if (!input.allowInterruptActiveTasks && this.#hasUpgradeBlockingActivity(1)) {
|
|
535
537
|
return { ok: true, result: { kind: 'active_tasks' } };
|
|
536
538
|
}
|
|
537
539
|
this.#shutdownReason = 'retirement';
|
|
@@ -560,6 +562,7 @@ export class RuntimeHostKernel {
|
|
|
560
562
|
},
|
|
561
563
|
},
|
|
562
564
|
'collaboration.grant.revoke': async (input) => this.#settleAccessCredentialMutation(revokeCollaborationGrant(this.#options.accessAuthority, input)),
|
|
565
|
+
'collaboration.principal.rename': async (input) => renameCollaborationPrincipal(this.#options.accessAuthority, input),
|
|
563
566
|
'collaboration.principal.revoke': async (input) => this.#settleAccessCredentialMutation(revokeCollaborationPrincipal(this.#options.accessAuthority, input.principalId)),
|
|
564
567
|
'collaboration.turn-request.create': async (input, context) => this.#settleAccessCredentialMutation(createCollaborationTurnRequest(this.#options.accessAuthority, context.principal, input)),
|
|
565
568
|
'collaboration.turn-request.query': async (input, context) => queryCollaborationTurnRequests(this.#options.accessAuthority, {
|
|
@@ -605,13 +608,16 @@ export class RuntimeHostKernel {
|
|
|
605
608
|
residencies: this.#residencies.snapshot(),
|
|
606
609
|
};
|
|
607
610
|
}
|
|
608
|
-
#hasUpgradeBlockingActivity() {
|
|
611
|
+
#hasUpgradeBlockingActivity(selfCommands) {
|
|
609
612
|
// The request's own accepted transport is expected. Any other live
|
|
610
613
|
// connection arrived after discovery or remained attached and therefore
|
|
611
|
-
// requires explicit interruption authority before retirement.
|
|
614
|
+
// requires explicit interruption authority before retirement. Callers
|
|
615
|
+
// pass how many of the in-flight commands are their own: the
|
|
616
|
+
// `host.upgrade.prepare` command counts itself, while the diagnostics
|
|
617
|
+
// query path runs outside the command counter.
|
|
612
618
|
if (this.#acceptedTransports.size > 1)
|
|
613
619
|
return true;
|
|
614
|
-
if (this.#activeCommandOperations >
|
|
620
|
+
if (this.#activeCommandOperations > selfCommands)
|
|
615
621
|
return true;
|
|
616
622
|
return this.#residencies.drainCount > 0;
|
|
617
623
|
}
|
|
@@ -112,11 +112,18 @@ export class HostMessageCoordinator {
|
|
|
112
112
|
for (const messageId of input.messageIds) {
|
|
113
113
|
const disposition = await this.#resolveMessageExecution(input.sessionId, messageId);
|
|
114
114
|
if (disposition.kind === 'owned_root' || disposition.kind === 'shared_turn') {
|
|
115
|
+
// This read projects current execution, including safe-boundary
|
|
116
|
+
// continuations. The Message's durable admission ownership is unchanged.
|
|
117
|
+
const latest = await this.#root.readLatestRootTurnLineage({
|
|
118
|
+
sessionId: input.sessionId,
|
|
119
|
+
turnId: disposition.turnId,
|
|
120
|
+
runId: disposition.runId,
|
|
121
|
+
});
|
|
115
122
|
resolutions.push({
|
|
116
123
|
messageId,
|
|
117
124
|
state: 'owned',
|
|
118
|
-
turnId:
|
|
119
|
-
runId:
|
|
125
|
+
turnId: latest.turnId,
|
|
126
|
+
runId: latest.runId,
|
|
120
127
|
});
|
|
121
128
|
continue;
|
|
122
129
|
}
|
|
@@ -62,6 +62,13 @@ export class RootAdmissionOwner {
|
|
|
62
62
|
previousRootTurnId: current?.turnId ?? null,
|
|
63
63
|
});
|
|
64
64
|
const admission = result.admission;
|
|
65
|
+
if (result.kind === 'conflict') {
|
|
66
|
+
const known = this.#admissionsBySession.get(input.sessionId)?.get(admission.turnId);
|
|
67
|
+
if (!known || !sameRootAdmission(known, admission)) {
|
|
68
|
+
throw new Error('Durable Root Turn conflict is outside the owned chain');
|
|
69
|
+
}
|
|
70
|
+
return Object.freeze({ kind: 'conflict', admission: known });
|
|
71
|
+
}
|
|
65
72
|
if (admission.sessionId !== input.sessionId ||
|
|
66
73
|
admission.turnId !== input.turnId ||
|
|
67
74
|
admission.previousRootTurnId !== (current?.turnId ?? null)) {
|
|
@@ -223,6 +223,21 @@ export class RootTurnCoordinator {
|
|
|
223
223
|
}
|
|
224
224
|
return this.#admissions.has(sessionId) ? { kind: 'reserved' } : { kind: 'idle' };
|
|
225
225
|
}
|
|
226
|
+
/** Returns the newest Host-admitted continuation descended from one root execution. */
|
|
227
|
+
async readLatestRootTurnLineage(identity) {
|
|
228
|
+
const origin = await this.stores.agentRunStore.readRootTurnAdmission(identity.sessionId, identity.turnId);
|
|
229
|
+
if (!origin || origin.runId !== identity.runId) {
|
|
230
|
+
throw new RuntimeMessageAuthorityInvariantError(`Root execution ${identity.turnId}/${identity.runId} has no durable admission`);
|
|
231
|
+
}
|
|
232
|
+
let latest = origin;
|
|
233
|
+
while (true) {
|
|
234
|
+
const continuation = await this.stores.agentRunStore.readRootTurnContinuationAdmission(identity.sessionId, latest.turnId, latest.runId);
|
|
235
|
+
if (!continuation)
|
|
236
|
+
break;
|
|
237
|
+
latest = continuation;
|
|
238
|
+
}
|
|
239
|
+
return { sessionId: latest.sessionId, turnId: latest.turnId, runId: latest.runId };
|
|
240
|
+
}
|
|
226
241
|
startHostedExternalTransition(input, context) {
|
|
227
242
|
return this.startInteractiveRootMessage({
|
|
228
243
|
sessionId: input.sessionId,
|
|
@@ -1291,6 +1306,12 @@ export class RootTurnCoordinator {
|
|
|
1291
1306
|
sourceMessages: [],
|
|
1292
1307
|
admittedAt: Date.now(),
|
|
1293
1308
|
});
|
|
1309
|
+
if (admitted.kind === 'conflict') {
|
|
1310
|
+
return {
|
|
1311
|
+
kind: 'complete',
|
|
1312
|
+
outcome: operationConflict('This root execution already has a different continuation Turn'),
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1294
1315
|
if (admitted.admission.runId !== continuation.runId ||
|
|
1295
1316
|
!isDeepStrictEqual(admitted.admission.execution, execution)) {
|
|
1296
1317
|
throw new RuntimeMessageAuthorityInvariantError('Safe-boundary continuation admission changed identity');
|