maka-agent 0.2.0-dev.76.20261009 → 0.2.0-dev.78.20261010
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/README.md +1 -1
- package/README.zh-CN.md +3 -3
- package/dist/pi-tui-pickers.js +5 -2
- package/dist/pi-tui-runner.js +211 -33
- package/dist/runtime-host-session-driver.js +91 -13
- package/dist/session-catalog-limits.js +22 -0
- package/dist/session-driver.js +10 -0
- package/dist/tui-copy-catalog.js +15 -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-chat-settings.js +3 -0
- package/node_modules/@maka/core/dist/bot-events.js +1 -8
- package/node_modules/@maka/core/dist/local-memory.js +38 -1
- package/node_modules/@maka/core/dist/scheduled-task.js +14 -15
- package/node_modules/@maka/core/dist/session.js +3 -0
- package/node_modules/@maka/core/dist/text-sanitize.js +8 -3
- package/node_modules/@maka/runtime/dist/agent-graph-supervisor-wake.js +115 -17
- package/node_modules/@maka/runtime/dist/agent-run.js +11 -0
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +11 -0
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +13 -5
- package/node_modules/@maka/runtime/dist/bots/proxied-fetch.js +29 -1
- package/node_modules/@maka/runtime/dist/computer-use-observation-text.js +22 -0
- package/node_modules/@maka/runtime/dist/conversation-copy-agent-output.js +227 -0
- package/node_modules/@maka/runtime/dist/conversation-copy.js +176 -46
- package/node_modules/@maka/runtime/dist/file-stable-write.js +15 -3
- package/node_modules/@maka/runtime/dist/filesystem-worker/resource-resolver.js +3 -1
- package/node_modules/@maka/runtime/dist/model-adapter.js +20 -1
- package/node_modules/@maka/runtime/dist/network/proxy-test.js +28 -1
- package/node_modules/@maka/runtime/dist/network/scoped-fetch-transport.js +13 -2
- package/node_modules/@maka/runtime/dist/owned-child-process.js +209 -0
- package/node_modules/@maka/runtime/dist/owned-process-main.js +183 -0
- package/node_modules/@maka/runtime/dist/pipe-process-driver.js +19 -25
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +216 -8
- package/node_modules/@maka/runtime/dist/sandbox-boundary-tool.js +12 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +156 -17
- package/node_modules/@maka/runtime/dist/shell-exec.js +18 -20
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +2 -1
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +279 -8
- package/node_modules/@maka/runtime/dist/stream-graph-schedule-reconcile.js +209 -37
- package/node_modules/@maka/runtime/dist/tool-availability.js +55 -18
- package/node_modules/@maka/runtime/dist/tool-call-snapshot.js +142 -0
- package/node_modules/@maka/runtime/dist/tool-runtime.js +27 -84
- package/node_modules/@maka/runtime/dist/workers/{filesystem-worker.js → filesystem-worker.mjs} +12 -1
- package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +30 -6
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +44 -13
- package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -0
- package/node_modules/@maka/runtime-host/dist/client/wsl-environment.js +4 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
- package/node_modules/@maka/runtime-host/dist/protocol/message.js +1 -13
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +46 -9
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -13
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +1 -13
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +4 -7
- package/node_modules/@maka/runtime-host/dist/server/archive-retention-coordinator.js +81 -105
- package/node_modules/@maka/runtime-host/dist/server/candidate.js +47 -40
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +87 -67
- package/node_modules/@maka/runtime-host/dist/server/execution-service.js +5 -1
- package/node_modules/@maka/runtime-host/dist/server/host-maintenance-guards.js +95 -0
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +18 -8
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +32 -3
- package/node_modules/@maka/runtime-host/dist/server/project-catalog-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +109 -8
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +20 -3
- package/node_modules/@maka/runtime-host/dist/server/session-background-activity.js +71 -0
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +8 -4
- package/node_modules/@maka/runtime-host/dist/server/session-interaction-activity.js +78 -0
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +56 -0
- package/node_modules/@maka/runtime-host/dist/server/session-revision-graph-references.js +16 -2
- package/node_modules/@maka/runtime-host/dist/server/skill-catalog-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +19 -24
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +74 -19
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +6 -99
- package/node_modules/@maka/runtime-host/dist/server/workhub-target-execution-authority.js +14 -126
- package/node_modules/@maka/storage/dist/context-offload-store.js +8 -3
- package/node_modules/@maka/storage/dist/git-entry.js +142 -5
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +54 -19
- package/package.json +2 -2
|
@@ -23,7 +23,7 @@ import { resolveCollaborationPermissionMode } from '@maka/core/collaboration';
|
|
|
23
23
|
import { serializedByteLength } from '@maka/core/serialized-byte-length';
|
|
24
24
|
import { encodeToolStepProgress, ToolOutcomeUnknownError } from '@maka/core/events';
|
|
25
25
|
import { isInteractionAnswerValidForRequest, projectInteractionFormRequest, } from '@maka/core/interaction';
|
|
26
|
-
import {
|
|
26
|
+
import { buildToolCallArgs, snapshotToolArgs } from './tool-call-snapshot.js';
|
|
27
27
|
import { redactSecrets } from '@maka/core/redaction';
|
|
28
28
|
import { TOOL_BOUNDARY_PROTOCOL_V1 } from '@maka/core/runtime-event';
|
|
29
29
|
import { recordToolArtifactsSafely } from './tool-artifacts.js';
|
|
@@ -544,36 +544,30 @@ export class ToolRuntime {
|
|
|
544
544
|
? `Tool ${tool.name} is direct-only and cannot run inside exec.`
|
|
545
545
|
: undefined;
|
|
546
546
|
const admissionFailure = directOnlyFailure ?? this.admitToolForStep(tool, stepId);
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
// Permission args are a policy/UI projection. Persistence and model replay
|
|
572
|
-
// share one canonical tool-dialect projection; Computer Use also strips
|
|
573
|
-
// screen-derived and user-typed values at this boundary.
|
|
574
|
-
const persistedArgs = tool.categoryHint === 'computer_use'
|
|
575
|
-
? snapshotToolArgs(computerUseModelCallArgs(permissionArgs))
|
|
576
|
-
: permissionArgs;
|
|
547
|
+
// An unavailable sandbox-boundary surface cannot carry the expansion the
|
|
548
|
+
// tool requests, and that availability contract holds even when an older
|
|
549
|
+
// caller sends a legacy shape — validation stays deferred for it.
|
|
550
|
+
const sandboxBoundaryUnavailable = tool.name === 'request_sandbox_boundary' &&
|
|
551
|
+
!this.interactionRun() &&
|
|
552
|
+
(!this.input.createSandboxBoundaryRequest || !this.input.settleSandboxBoundaryRequest);
|
|
553
|
+
const callArgs = await buildToolCallArgs({
|
|
554
|
+
toolName: tool.name,
|
|
555
|
+
parameters: tool.parameters,
|
|
556
|
+
categoryHint: tool.categoryHint,
|
|
557
|
+
permissionArgs: tool.permissionArgs,
|
|
558
|
+
// Upstream renamed rawExecutionArgs to executionArgs after this
|
|
559
|
+
// refactor; buildToolCallArgs passes it through unchanged, and the
|
|
560
|
+
// entry snapshot above already holds that same value.
|
|
561
|
+
executionArgs,
|
|
562
|
+
sessionId: this.input.sessionId,
|
|
563
|
+
turnId,
|
|
564
|
+
toolCallId: toolUseId,
|
|
565
|
+
directOnlyRejected: directOnlyFailure !== undefined,
|
|
566
|
+
validationDeferred: sandboxBoundaryUnavailable,
|
|
567
|
+
});
|
|
568
|
+
const permissionArgs = callArgs.permissionArgs;
|
|
569
|
+
const persistedArgs = callArgs.persistedArgs;
|
|
570
|
+
const permissionArgsError = callArgs.permissionArgsError;
|
|
577
571
|
const now = this.input.now();
|
|
578
572
|
const trace = this.input.getRunTrace?.() ?? null;
|
|
579
573
|
const runId = this.input.runId;
|
|
@@ -2068,36 +2062,6 @@ export class ToolRuntime {
|
|
|
2068
2062
|
});
|
|
2069
2063
|
}
|
|
2070
2064
|
}
|
|
2071
|
-
async function validateDeclaredToolArgs(parameters, args) {
|
|
2072
|
-
if (!parameters || (typeof parameters !== 'object' && typeof parameters !== 'function')) {
|
|
2073
|
-
return;
|
|
2074
|
-
}
|
|
2075
|
-
const schema = parameters;
|
|
2076
|
-
if (typeof schema.safeParseAsync === 'function') {
|
|
2077
|
-
const parsed = await schema.safeParseAsync(args);
|
|
2078
|
-
if (parsed.success)
|
|
2079
|
-
return;
|
|
2080
|
-
throw parsed.error;
|
|
2081
|
-
}
|
|
2082
|
-
if (typeof schema.safeParse === 'function') {
|
|
2083
|
-
const parsed = schema.safeParse(args);
|
|
2084
|
-
if (parsed.success)
|
|
2085
|
-
return;
|
|
2086
|
-
throw parsed.error;
|
|
2087
|
-
}
|
|
2088
|
-
if (typeof schema.validate === 'function') {
|
|
2089
|
-
const parsed = await schema.validate(args);
|
|
2090
|
-
if (parsed.success)
|
|
2091
|
-
return;
|
|
2092
|
-
throw parsed.error;
|
|
2093
|
-
}
|
|
2094
|
-
if (typeof schema['~standard']?.validate === 'function') {
|
|
2095
|
-
const parsed = await schema['~standard'].validate(args);
|
|
2096
|
-
if ('value' in parsed)
|
|
2097
|
-
return;
|
|
2098
|
-
throw new Error('Tool arguments failed declared schema validation', { cause: parsed.issues });
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
2065
|
function isInteractionControlError(error) {
|
|
2102
2066
|
return (error instanceof RuntimeInteractionAdmissionRejectedError ||
|
|
2103
2067
|
error instanceof RuntimeInteractionClosedError ||
|
|
@@ -2152,7 +2116,8 @@ function racePromiseWithAbort(promise, signal) {
|
|
|
2152
2116
|
*/
|
|
2153
2117
|
export function formatDeferredNotLoadedText(toolName) {
|
|
2154
2118
|
return (`Tool "${toolName}" is available but not loaded yet. ` +
|
|
2155
|
-
`Call tool_search
|
|
2119
|
+
`Call tool_search with query "${toolName}" to activate it first, ` +
|
|
2120
|
+
`then call "${toolName}" on a later step.`);
|
|
2156
2121
|
}
|
|
2157
2122
|
/**
|
|
2158
2123
|
* Canonical key for a tool call's args; order-independent so identical calls
|
|
@@ -2597,25 +2562,3 @@ function byteLength(value) {
|
|
|
2597
2562
|
const text = typeof value === 'string' ? value : JSON.stringify(value ?? null);
|
|
2598
2563
|
return Buffer.byteLength(text, 'utf8');
|
|
2599
2564
|
}
|
|
2600
|
-
function snapshotToolArgs(value) {
|
|
2601
|
-
return snapshotJsonValue(value, new WeakSet());
|
|
2602
|
-
}
|
|
2603
|
-
function snapshotJsonValue(value, seen) {
|
|
2604
|
-
if (value === null || typeof value !== 'object')
|
|
2605
|
-
return value;
|
|
2606
|
-
if (seen.has(value))
|
|
2607
|
-
throw new Error('Tool arguments must not contain cycles');
|
|
2608
|
-
seen.add(value);
|
|
2609
|
-
if (Array.isArray(value)) {
|
|
2610
|
-
return Object.freeze(value.map((entry) => snapshotJsonValue(entry, seen)));
|
|
2611
|
-
}
|
|
2612
|
-
const output = {};
|
|
2613
|
-
for (const key of Object.keys(value)) {
|
|
2614
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
2615
|
-
if (!descriptor || !('value' in descriptor)) {
|
|
2616
|
-
throw new Error(`Tool argument ${key} must be a plain data property`);
|
|
2617
|
-
}
|
|
2618
|
-
output[key] = snapshotJsonValue(descriptor.value, seen);
|
|
2619
|
-
}
|
|
2620
|
-
return Object.freeze(output);
|
|
2621
|
-
}
|
package/node_modules/@maka/runtime/dist/workers/{filesystem-worker.js → filesystem-worker.mjs}
RENAMED
|
@@ -24203,9 +24203,20 @@ async function openExistingNoTruncate(path2) {
|
|
|
24203
24203
|
}
|
|
24204
24204
|
}
|
|
24205
24205
|
async function writeThroughHandle(handle, content) {
|
|
24206
|
+
const bytes = Buffer.from(content, "utf8");
|
|
24206
24207
|
try {
|
|
24207
24208
|
await handle.truncate(0);
|
|
24208
|
-
|
|
24209
|
+
let offset = 0;
|
|
24210
|
+
while (offset < bytes.length) {
|
|
24211
|
+
const { bytesWritten } = await handle.write(bytes, offset, bytes.length - offset, offset);
|
|
24212
|
+
if (bytesWritten <= 0) {
|
|
24213
|
+
throw new StableWriteFailure(
|
|
24214
|
+
"outcome_unknown",
|
|
24215
|
+
"The write stopped making progress; the file may be truncated. Re-read the file before writing to it again."
|
|
24216
|
+
);
|
|
24217
|
+
}
|
|
24218
|
+
offset += bytesWritten;
|
|
24219
|
+
}
|
|
24209
24220
|
} catch (error62) {
|
|
24210
24221
|
const code = error62.code;
|
|
24211
24222
|
if (code === "ENOSPC" || code === "EIO" || code === "EDQUOT" || code === "EFBIG") {
|
|
@@ -30,7 +30,7 @@ import { abortable, waitForRuntimeHostReady } from './wait-for-ready.js';
|
|
|
30
30
|
// Candidate readiness includes the Windows named-pipe ACL helper, whose
|
|
31
31
|
// fail-closed ceiling is 60s. Leave enough room for election and connection
|
|
32
32
|
// bookkeeping after that helper returns.
|
|
33
|
-
const DEFAULT_ELECTION_DEADLINE_MS = 75_000;
|
|
33
|
+
export const DEFAULT_ELECTION_DEADLINE_MS = 75_000;
|
|
34
34
|
const DEFAULT_BACKOFF_MIN_MS = 20;
|
|
35
35
|
const DEFAULT_BACKOFF_MAX_MS = 250;
|
|
36
36
|
const MIN_CANDIDATE_INTERVAL_MS = 250;
|
|
@@ -251,10 +251,24 @@ export async function connectOrSpawnRuntimeHostWithDependencies(input, dependenc
|
|
|
251
251
|
return result;
|
|
252
252
|
}
|
|
253
253
|
if (isPermanentCandidateStartupFailure(startupFailure) && pendingCandidateReports === 0) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
254
|
+
if (isLaunchElectionLoss(startupFailure)) {
|
|
255
|
+
// Losing the launch election is not a Host failure. It is proof that
|
|
256
|
+
// another candidate holds this root and is still coming up, so the
|
|
257
|
+
// only honest reading is "keep waiting for the one that won".
|
|
258
|
+
// Returning here is what made a slow first startup look permanently
|
|
259
|
+
// unavailable: the desktop saw a failure a second after it asked,
|
|
260
|
+
// while the candidate that owned the root was still loading its
|
|
261
|
+
// transcript store, and every retry looked the same (issue #5843).
|
|
262
|
+
// The loser's startup diagnostic stays on disk, because that trace is
|
|
263
|
+
// what made the storm visible at all; it is simply no longer terminal.
|
|
264
|
+
startupFailure = undefined;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
const selectedFailure = startupFailure;
|
|
268
|
+
electionSettled = true;
|
|
269
|
+
await selectCandidateStartupDiagnostic(capability.rootId, selectedFailure.startupAttemptId).catch(() => undefined);
|
|
270
|
+
return { kind: 'failed', reason: selectedFailure.reason };
|
|
271
|
+
}
|
|
258
272
|
}
|
|
259
273
|
const now = performance.now();
|
|
260
274
|
if (shouldLaunchCandidate(result) &&
|
|
@@ -362,7 +376,7 @@ export async function connectOrSpawnRuntimeHostWithDependencies(input, dependenc
|
|
|
362
376
|
await sleep(Math.min(remaining, Math.max(1, Math.round(backoffMs * jitter))), input.signal);
|
|
363
377
|
backoffMs = Math.min(DEFAULT_BACKOFF_MAX_MS, backoffMs * 2);
|
|
364
378
|
}
|
|
365
|
-
if (startupFailure) {
|
|
379
|
+
if (startupFailure && !isLaunchElectionLoss(startupFailure)) {
|
|
366
380
|
const selectedFailure = startupFailure;
|
|
367
381
|
electionSettled = true;
|
|
368
382
|
await selectCandidateStartupDiagnostic(capability.rootId, selectedFailure.startupAttemptId).catch(() => undefined);
|
|
@@ -387,6 +401,16 @@ export async function connectOrSpawnRuntimeHostWithDependencies(input, dependenc
|
|
|
387
401
|
electionSettled = true;
|
|
388
402
|
}
|
|
389
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* A Candidate that lost the launch election is evidence about *another*
|
|
406
|
+
* Candidate - it holds the root and is still coming up - so the loss must never
|
|
407
|
+
* decide how this election ends, whether the loop breaks on it or the window
|
|
408
|
+
* simply runs out. Failing on it made a slow first startup look like a
|
|
409
|
+
* permanently unavailable Host (issue #5843).
|
|
410
|
+
*/
|
|
411
|
+
function isLaunchElectionLoss(failure) {
|
|
412
|
+
return failure?.reason === 'launch_election_lost';
|
|
413
|
+
}
|
|
390
414
|
function recordElectionResult(result, observations) {
|
|
391
415
|
observations.totalResults += 1;
|
|
392
416
|
const registration = 'registration' in result ? result.registration : undefined;
|
|
@@ -34,7 +34,7 @@ export { RuntimeHostSubscriptionError, SessionRemovedSubscriptionError, subscrip
|
|
|
34
34
|
export { RuntimeHostStartupError, runtimeHostStartupError, } from './startup-error.js';
|
|
35
35
|
export { connectRuntimeHostWslEnvironment, listRuntimeHostWslDistributions, normalizeRuntimeHostWslDistribution, resolveSystemRuntimeHostWslExecutable, } from './wsl-environment.js';
|
|
36
36
|
export { RuntimeHostCatalogReadError, RuntimeHostSessionCatalogRevisionChangedError, readRuntimeHostConnectionCatalog, readRuntimeHostInvocableSkills, readRuntimeHostProjectDetails, readRuntimeHostResources, readRuntimeHostProjects, readRuntimeHostSessionCatalogPage, readRuntimeHostSessions, readRuntimeHostSkillCatalog, } from './catalog-reader.js';
|
|
37
|
-
export { IDLE_GRACE_MS_ENV_VAR, connectOrSpawnRuntimeHost, } from './connect-or-spawn.js';
|
|
37
|
+
export { DEFAULT_ELECTION_DEADLINE_MS, ELECTION_DEADLINE_MS_ENV_VAR, IDLE_GRACE_MS_ENV_VAR, connectOrSpawnRuntimeHost, electionDeadlineMsFromEnvironment, } from './connect-or-spawn.js';
|
|
38
38
|
export { abortable, waitForRuntimeHostReady } from './wait-for-ready.js';
|
|
39
39
|
export { createRuntimeHostCandidateLaunchBarrier, } from './candidate-launch-barrier.js';
|
|
40
40
|
export { runHostedExecution } from './hosted-execution.js';
|
|
@@ -63,6 +63,7 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
63
63
|
#closed = false;
|
|
64
64
|
#quiesced = false;
|
|
65
65
|
#terminalError;
|
|
66
|
+
#startTask;
|
|
66
67
|
#reconnectTask;
|
|
67
68
|
#reconnectAbort;
|
|
68
69
|
#discardTask = Promise.resolve();
|
|
@@ -96,10 +97,22 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
96
97
|
get current() {
|
|
97
98
|
return this.#current;
|
|
98
99
|
}
|
|
99
|
-
|
|
100
|
+
start() {
|
|
101
|
+
if (this.#closed)
|
|
102
|
+
return Promise.reject(new Error('Runtime Host reconnect lifecycle is closed'));
|
|
103
|
+
if (this.#startTask)
|
|
104
|
+
return this.#startTask;
|
|
105
|
+
const task = deferredTask();
|
|
106
|
+
this.#startTask = task.promise;
|
|
107
|
+
void this.#start().then(task.resolve, task.reject);
|
|
108
|
+
return task.promise;
|
|
109
|
+
}
|
|
110
|
+
async #start() {
|
|
100
111
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
const initial = this.#initial;
|
|
113
|
+
this.#initial = undefined;
|
|
114
|
+
if (initial) {
|
|
115
|
+
this.#install(initial);
|
|
103
116
|
return;
|
|
104
117
|
}
|
|
105
118
|
this.#initialSignal?.throwIfAborted();
|
|
@@ -109,6 +122,8 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
109
122
|
this.#install(await this.#connect(signal));
|
|
110
123
|
}
|
|
111
124
|
catch (error) {
|
|
125
|
+
if (this.#closed)
|
|
126
|
+
throw error;
|
|
112
127
|
const failure = asError(error);
|
|
113
128
|
if ((typeof this.#retryInitialFailure === 'function'
|
|
114
129
|
? this.#retryInitialFailure(failure)
|
|
@@ -198,8 +213,12 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
198
213
|
return this.#suspension(current);
|
|
199
214
|
}
|
|
200
215
|
close() {
|
|
201
|
-
this.#closeTask
|
|
202
|
-
|
|
216
|
+
if (this.#closeTask)
|
|
217
|
+
return this.#closeTask;
|
|
218
|
+
const task = deferredTask();
|
|
219
|
+
this.#closeTask = task.promise;
|
|
220
|
+
void this.#close().then(task.resolve, task.reject);
|
|
221
|
+
return task.promise;
|
|
203
222
|
}
|
|
204
223
|
async #close() {
|
|
205
224
|
if (this.#closed)
|
|
@@ -210,9 +229,11 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
210
229
|
this.#abort.abort();
|
|
211
230
|
const error = new Error('Runtime Host reconnect lifecycle is closed');
|
|
212
231
|
this.#rejectWaiters(error);
|
|
213
|
-
const current = this.#current;
|
|
232
|
+
const current = this.#current ?? this.#initial;
|
|
233
|
+
this.#initial = undefined;
|
|
214
234
|
this.#setCurrent(undefined);
|
|
215
235
|
await current?.close().catch(() => undefined);
|
|
236
|
+
await this.#startTask?.catch(() => undefined);
|
|
216
237
|
await this.#reconnectTask?.catch(() => undefined);
|
|
217
238
|
await this.#discardTask;
|
|
218
239
|
this.#resolveClosed();
|
|
@@ -248,17 +269,18 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
248
269
|
this.#reconnectTask)
|
|
249
270
|
return;
|
|
250
271
|
const reconnectAbort = new AbortController();
|
|
251
|
-
const task =
|
|
272
|
+
const task = deferredTask();
|
|
252
273
|
this.#reconnectAbort = reconnectAbort;
|
|
253
|
-
this.#reconnectTask = task;
|
|
274
|
+
this.#reconnectTask = task.promise;
|
|
275
|
+
void this.#reconnect(AbortSignal.any([this.#abort.signal, reconnectAbort.signal])).then(task.resolve, task.reject);
|
|
254
276
|
const finalize = () => {
|
|
255
|
-
if (this.#reconnectTask === task) {
|
|
277
|
+
if (this.#reconnectTask === task.promise) {
|
|
256
278
|
this.#reconnectTask = undefined;
|
|
257
279
|
this.#reconnectAbort = undefined;
|
|
258
280
|
}
|
|
259
281
|
this.#scheduleReconnect();
|
|
260
282
|
};
|
|
261
|
-
void task.then(finalize, finalize);
|
|
283
|
+
void task.promise.then(finalize, finalize);
|
|
262
284
|
}
|
|
263
285
|
async #reconnect(signal) {
|
|
264
286
|
let attemptedWakeGeneration = this.#wakeGeneration;
|
|
@@ -348,13 +370,13 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
348
370
|
}
|
|
349
371
|
}
|
|
350
372
|
#failPermanently(error) {
|
|
351
|
-
if (this.#terminalError)
|
|
373
|
+
if (this.#closed || this.#terminalError)
|
|
352
374
|
return;
|
|
353
375
|
this.#terminalError = error;
|
|
354
|
-
this.#abort.abort();
|
|
355
376
|
this.#rejectWaiters(error);
|
|
377
|
+
// Acquisition tasks must settle before close can finish joining them.
|
|
378
|
+
void this.close();
|
|
356
379
|
notifyError(this.#onFatalError, error);
|
|
357
|
-
this.#resolveClosed();
|
|
358
380
|
}
|
|
359
381
|
#rejectWaiters(error) {
|
|
360
382
|
for (const waiter of this.#waiters) {
|
|
@@ -364,6 +386,15 @@ class RuntimeHostReconnectLifecycleImpl {
|
|
|
364
386
|
this.#waiters.clear();
|
|
365
387
|
}
|
|
366
388
|
}
|
|
389
|
+
function deferredTask() {
|
|
390
|
+
let resolve;
|
|
391
|
+
let reject;
|
|
392
|
+
const promise = new Promise((resolveTask, rejectTask) => {
|
|
393
|
+
resolve = resolveTask;
|
|
394
|
+
reject = rejectTask;
|
|
395
|
+
});
|
|
396
|
+
return { promise, resolve, reject };
|
|
397
|
+
}
|
|
367
398
|
function notifyError(listener, error) {
|
|
368
399
|
try {
|
|
369
400
|
listener?.(error);
|
|
@@ -35,6 +35,12 @@ export function projectSessionCatalogSummary(session) {
|
|
|
35
35
|
? {}
|
|
36
36
|
: { lastMessagePreview: session.lastMessagePreview }),
|
|
37
37
|
status: session.status,
|
|
38
|
+
...(session.backgroundActivity === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: { backgroundActivity: session.backgroundActivity }),
|
|
41
|
+
...(session.backgroundActivityVersion === undefined
|
|
42
|
+
? {}
|
|
43
|
+
: { backgroundActivityVersion: session.backgroundActivityVersion }),
|
|
38
44
|
...(session.blockedReason === undefined ? {} : { blockedReason: session.blockedReason }),
|
|
39
45
|
...(session.statusUpdatedAt === undefined ? {} : { statusUpdatedAt: session.statusUpdatedAt }),
|
|
40
46
|
...(session.liveRunState === undefined
|
|
@@ -112,6 +112,10 @@ class WslProcessByteStream {
|
|
|
112
112
|
}
|
|
113
113
|
onError(listener) {
|
|
114
114
|
this.child.once('error', listener);
|
|
115
|
+
// Pipe errors are emitted independently of the write callback and the
|
|
116
|
+
// ChildProcess error event. Route them through the transport failure fence.
|
|
117
|
+
this.child.stdin.on('error', listener);
|
|
118
|
+
this.child.stdout.on('error', listener);
|
|
115
119
|
}
|
|
116
120
|
write(chunk) {
|
|
117
121
|
return new Promise((resolve, reject) => {
|
|
@@ -65,7 +65,11 @@ export const RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION = 1;
|
|
|
65
65
|
export const RUNTIME_HOST_PROTOCOL_VERSION = 0;
|
|
66
66
|
// Increment when the same protocol version no longer guarantees safe Client-Host
|
|
67
67
|
// interoperability. Mismatches are rejected before domain commands are admitted.
|
|
68
|
-
export const RUNTIME_HOST_COMPATIBILITY_EPOCH =
|
|
68
|
+
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 215;
|
|
69
|
+
// 215: WorkHub coordination uses canonical configuration and scoped delegated permissions.
|
|
70
|
+
// 212: Session catalogs carry `backgroundActivity` and `backgroundActivityVersion`
|
|
71
|
+
// for Host-owned activity and generation-scoped ordering independent of Session
|
|
72
|
+
// revisions and Turn epochs. Older closed decoders reject these fields.
|
|
69
73
|
// 208: Usage queries filter by model call kind. Epoch-207 peers reject the
|
|
70
74
|
// filter or ignore it.
|
|
71
75
|
// 206: `storage.retention.query` and `storage.retention.set` read and change the
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { invalidProtocolFrame } from './errors.js';
|
|
20
|
-
import { assertExactKeys, requireCount, requireEntityId, requireExactRecord, requireId, requireRecord, requireShapedRecord, } from './codec.js';
|
|
20
|
+
import { assertExactKeys, requireCount, requireEncodedByteLimit, requireEntityId, requireExactRecord, requireId, requireRecord, requireShapedRecord, } from './codec.js';
|
|
21
21
|
import { defineOperation } from './operation-spec.js';
|
|
22
22
|
import { decodeMessageAdmissionContent, decodeSkillIds, decodeTurnOrchestration, decodeTurnSnapshot, } from './turn.js';
|
|
23
23
|
import { decodeSkillInvocationResult } from '@maka/core/skill-invocation';
|
|
@@ -377,15 +377,3 @@ function assertUniqueQueueEntries(entries, label) {
|
|
|
377
377
|
messageIds.add(entry.messageId);
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
|
-
function requireEncodedByteLimit(value, label, maxBytes) {
|
|
381
|
-
let encoded;
|
|
382
|
-
try {
|
|
383
|
-
encoded = JSON.stringify(value);
|
|
384
|
-
}
|
|
385
|
-
catch {
|
|
386
|
-
throw invalidProtocolFrame(`Invalid ${label}`);
|
|
387
|
-
}
|
|
388
|
-
if (encoded === undefined || Buffer.byteLength(encoded, 'utf8') > maxBytes) {
|
|
389
|
-
throw invalidProtocolFrame(`Invalid ${label}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
@@ -101,6 +101,8 @@ const PROJECTION_FIELDS = [
|
|
|
101
101
|
'thinkingLevel',
|
|
102
102
|
'lastReadMessageId',
|
|
103
103
|
'liveRunState',
|
|
104
|
+
'backgroundActivity',
|
|
105
|
+
'backgroundActivityVersion',
|
|
104
106
|
];
|
|
105
107
|
export const SESSION_CATALOG_OPERATION_SPECS = {
|
|
106
108
|
'session.shared.query': defineOperation({
|
|
@@ -176,7 +178,15 @@ function decodeSharedSessionCatalogQueryResult(value) {
|
|
|
176
178
|
return { session };
|
|
177
179
|
}
|
|
178
180
|
export function decodeSharedSessionCatalogProjection(value) {
|
|
179
|
-
const exact = requireShapedRecord(value, 'shared Session catalog projection', ['kind', 'id', 'revision', 'createdAt', 'activityAt', 'name', 'status'], [
|
|
181
|
+
const exact = requireShapedRecord(value, 'shared Session catalog projection', ['kind', 'id', 'revision', 'createdAt', 'activityAt', 'name', 'status'], [
|
|
182
|
+
'lastMessageAt',
|
|
183
|
+
'lastMessagePreview',
|
|
184
|
+
'liveRunState',
|
|
185
|
+
'backgroundActivity',
|
|
186
|
+
'backgroundActivityVersion',
|
|
187
|
+
'blockedReason',
|
|
188
|
+
'statusUpdatedAt',
|
|
189
|
+
]);
|
|
180
190
|
if (exact.kind !== 'shared_session')
|
|
181
191
|
throw invalidProtocolFrame('Invalid shared Session kind');
|
|
182
192
|
return {
|
|
@@ -190,6 +200,7 @@ export function decodeSharedSessionCatalogProjection(value) {
|
|
|
190
200
|
...optionalText(exact, 'lastMessagePreview', SESSION_CATALOG_PREVIEW_MAX_BYTES),
|
|
191
201
|
status: decodeSessionStatus(exact.status),
|
|
192
202
|
...optionalLiveRunState(exact),
|
|
203
|
+
...optionalBackgroundActivity(exact),
|
|
193
204
|
...optionalBlockedReason(exact),
|
|
194
205
|
...optionalTimestamp(exact, 'statusUpdatedAt'),
|
|
195
206
|
};
|
|
@@ -518,6 +529,7 @@ export function decodeSessionCatalogProjection(value) {
|
|
|
518
529
|
...optionalText(record, 'lastMessagePreview', SESSION_CATALOG_PREVIEW_MAX_BYTES),
|
|
519
530
|
status: decodeSessionStatus(record.status),
|
|
520
531
|
...optionalLiveRunState(record),
|
|
532
|
+
...optionalBackgroundActivity(record),
|
|
521
533
|
...optionalBlockedReason(record),
|
|
522
534
|
...optionalTimestamp(record, 'statusUpdatedAt'),
|
|
523
535
|
...optionalEntityId(record, 'parentSessionId'),
|
|
@@ -640,6 +652,29 @@ function optionalBlockedReason(record) {
|
|
|
640
652
|
}
|
|
641
653
|
return { blockedReason: record.blockedReason };
|
|
642
654
|
}
|
|
655
|
+
function optionalBackgroundActivity(record) {
|
|
656
|
+
const activity = record.backgroundActivity;
|
|
657
|
+
if (activity === undefined) {
|
|
658
|
+
if (record.backgroundActivityVersion !== undefined)
|
|
659
|
+
throw invalidProtocolFrame('Session background activity version requires activity');
|
|
660
|
+
return {};
|
|
661
|
+
}
|
|
662
|
+
if (activity !== 'idle' &&
|
|
663
|
+
activity !== 'running' &&
|
|
664
|
+
activity !== 'waiting_for_user' &&
|
|
665
|
+
activity !== 'blocked')
|
|
666
|
+
throw invalidProtocolFrame('Invalid Session background activity');
|
|
667
|
+
if (record.backgroundActivityVersion === undefined)
|
|
668
|
+
return { backgroundActivity: activity };
|
|
669
|
+
const version = requireExactRecord(record.backgroundActivityVersion, 'Session background activity version', ['hostGeneration', 'revision']);
|
|
670
|
+
return {
|
|
671
|
+
backgroundActivity: activity,
|
|
672
|
+
backgroundActivityVersion: {
|
|
673
|
+
hostGeneration: decodeHostGeneration(version.hostGeneration),
|
|
674
|
+
revision: requireCount(version.revision, 'Session background activity revision'),
|
|
675
|
+
},
|
|
676
|
+
};
|
|
677
|
+
}
|
|
643
678
|
function optionalLiveRunState(record) {
|
|
644
679
|
if (record.liveRunState === undefined)
|
|
645
680
|
return {};
|
|
@@ -671,13 +706,6 @@ function optionalLiveRunState(record) {
|
|
|
671
706
|
liveRunState.runEpoch < 0)) {
|
|
672
707
|
throw invalidProtocolFrame('Invalid Session catalog run epoch');
|
|
673
708
|
}
|
|
674
|
-
if (liveRunState.hostGeneration !== undefined &&
|
|
675
|
-
(typeof liveRunState.hostGeneration !== 'string' ||
|
|
676
|
-
liveRunState.hostGeneration.length === 0 ||
|
|
677
|
-
liveRunState.hostGeneration.length > SESSION_CATALOG_HOST_GENERATION_MAX_CHARS ||
|
|
678
|
-
/[\u0000-\u001f\u007f]/.test(liveRunState.hostGeneration))) {
|
|
679
|
-
throw invalidProtocolFrame('Invalid Session catalog host generation');
|
|
680
|
-
}
|
|
681
709
|
return {
|
|
682
710
|
liveRunState: {
|
|
683
711
|
schemaVersion: SESSION_CATALOG_LIVE_RUN_STATE_SCHEMA_VERSION,
|
|
@@ -685,10 +713,19 @@ function optionalLiveRunState(record) {
|
|
|
685
713
|
...(liveRunState.runEpoch === undefined ? {} : { runEpoch: liveRunState.runEpoch }),
|
|
686
714
|
...(liveRunState.hostGeneration === undefined
|
|
687
715
|
? {}
|
|
688
|
-
: { hostGeneration: liveRunState.hostGeneration }),
|
|
716
|
+
: { hostGeneration: decodeHostGeneration(liveRunState.hostGeneration) }),
|
|
689
717
|
},
|
|
690
718
|
};
|
|
691
719
|
}
|
|
720
|
+
function decodeHostGeneration(value) {
|
|
721
|
+
if (typeof value !== 'string' ||
|
|
722
|
+
value.length === 0 ||
|
|
723
|
+
value.length > SESSION_CATALOG_HOST_GENERATION_MAX_CHARS ||
|
|
724
|
+
/[\u0000-\u001f\u007f]/.test(value)) {
|
|
725
|
+
throw invalidProtocolFrame('Invalid Session catalog host generation');
|
|
726
|
+
}
|
|
727
|
+
return value;
|
|
728
|
+
}
|
|
692
729
|
function optionalSubagent(record) {
|
|
693
730
|
if (!Object.hasOwn(record, 'subagent'))
|
|
694
731
|
return {};
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { TOOL_ACTIVITY_KINDS, TOOL_OUTPUT_DELTA_MAX_CHARS } from '@maka/core/events';
|
|
20
20
|
import { decodeToolResultPreviewContent } from '@maka/core/tool-result-preview';
|
|
21
|
-
import { assertExactKeys, requireCount, requireEntityId, requireExactRecord, requireId, requireOpaqueIdentity, requireRecord, } from './codec.js';
|
|
21
|
+
import { assertExactKeys, requireCount, requireEncodedByteLimit, requireEntityId, requireExactRecord, requireId, requireOpaqueIdentity, requireRecord, } from './codec.js';
|
|
22
22
|
import { invalidProtocolFrame } from './errors.js';
|
|
23
23
|
import { decodeSessionStatus } from './session-status.js';
|
|
24
24
|
import { decodeSessionInteractionProjection, } from './interaction.js';
|
|
@@ -773,18 +773,6 @@ function requireUtf8BoundedString(value, label, maxBytes) {
|
|
|
773
773
|
}
|
|
774
774
|
return value;
|
|
775
775
|
}
|
|
776
|
-
function requireEncodedByteLimit(value, label, maxBytes) {
|
|
777
|
-
let encoded;
|
|
778
|
-
try {
|
|
779
|
-
encoded = JSON.stringify(value);
|
|
780
|
-
}
|
|
781
|
-
catch {
|
|
782
|
-
throw invalidProtocolFrame(`Invalid ${label}`);
|
|
783
|
-
}
|
|
784
|
-
if (encoded === undefined || Buffer.byteLength(encoded, 'utf8') > maxBytes) {
|
|
785
|
-
throw invalidProtocolFrame(`Invalid ${label}`);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
776
|
/**
|
|
789
777
|
* Read against the one list rather than a copy of it.
|
|
790
778
|
*
|
|
@@ -22,7 +22,7 @@ import { isOrchestrationMode, isTurnOrchestrationSource, } from '@maka/core/orch
|
|
|
22
22
|
import { decodeSkillInvocationResult, } from '@maka/core/skill-invocation';
|
|
23
23
|
import { decodeTurnOrigin } from '@maka/core/turn-origin';
|
|
24
24
|
import { invalidProtocolFrame } from './errors.js';
|
|
25
|
-
import { assertExactKeys, requireCount, requireEntityId, requireExactRecord, requireShapedRecord, requireId, requireRecord, requireString, } from './codec.js';
|
|
25
|
+
import { assertExactKeys, requireCount, requireEncodedByteLimit, requireEntityId, requireExactRecord, requireShapedRecord, requireId, requireRecord, requireString, } from './codec.js';
|
|
26
26
|
import { defineOperation } from './operation-spec.js';
|
|
27
27
|
import { decodeTurnProviderRetry } from './turn-provider-retry.js';
|
|
28
28
|
export { decodeTurnProviderRetry } from './turn-provider-retry.js';
|
|
@@ -281,18 +281,6 @@ function requireUtf8String(value, label, maxBytes, allowEmpty) {
|
|
|
281
281
|
}
|
|
282
282
|
return value;
|
|
283
283
|
}
|
|
284
|
-
function requireEncodedByteLimit(value, label, maxBytes) {
|
|
285
|
-
let encoded;
|
|
286
|
-
try {
|
|
287
|
-
encoded = JSON.stringify(value);
|
|
288
|
-
}
|
|
289
|
-
catch {
|
|
290
|
-
throw invalidProtocolFrame(`Invalid ${label}`);
|
|
291
|
-
}
|
|
292
|
-
if (encoded === undefined || Buffer.byteLength(encoded, 'utf8') > maxBytes) {
|
|
293
|
-
throw invalidProtocolFrame(`Invalid ${label}`);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
284
|
function decodeTurnQueryInput(value) {
|
|
297
285
|
const record = requireExactRecord(value, 'turn.query input', ['sessionId', 'turnId']);
|
|
298
286
|
return {
|
|
@@ -190,13 +190,7 @@ export function decodeWorkHubCoordinationCandidatesResult(value) {
|
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
192
|
export function decodeWorkHubCoordinationSelectAndDelegateInput(value) {
|
|
193
|
-
const input =
|
|
194
|
-
'turnId',
|
|
195
|
-
'actionId',
|
|
196
|
-
'candidateSetId',
|
|
197
|
-
'candidateRefs',
|
|
198
|
-
'delegationText',
|
|
199
|
-
]);
|
|
193
|
+
const input = requireShapedRecord(value, 'WorkHub selection input', ['turnId', 'actionId', 'candidateSetId', 'candidateRefs', 'delegationText'], ['create']);
|
|
200
194
|
if (!Array.isArray(input.candidateRefs) ||
|
|
201
195
|
input.candidateRefs.length < 1 ||
|
|
202
196
|
input.candidateRefs.length > WORKHUB_COORDINATION_CANDIDATE_MAX_ITEMS) {
|
|
@@ -206,6 +200,9 @@ export function decodeWorkHubCoordinationSelectAndDelegateInput(value) {
|
|
|
206
200
|
if (new Set(candidateRefs).size !== candidateRefs.length)
|
|
207
201
|
throw invalidProtocolFrame('Duplicate WorkHub selection candidates');
|
|
208
202
|
return {
|
|
203
|
+
...(input.create !== undefined
|
|
204
|
+
? { create: decodeWorkHubCoordinationCreateContext(input.create) }
|
|
205
|
+
: {}),
|
|
209
206
|
turnId: requireEntityId(input.turnId, 'WorkHub Coordination Turn id'),
|
|
210
207
|
actionId: requireEntityId(input.actionId, 'WorkHub action id'),
|
|
211
208
|
candidateSetId: requireUtf8String(input.candidateSetId, 'WorkHub candidate set', 256),
|