mixdog 1.1.0 → 1.1.2
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 +13 -6
- package/package.json +1 -2
- package/scripts/latest-guard.mjs +63 -0
- package/scripts/lib/run-node-tests.mjs +103 -27
- package/scripts/lib/test-failure-summary.mjs +7 -2
- package/scripts/merge-mac-update-feed.mjs +107 -0
- package/scripts/mobile-release.mjs +210 -0
- package/scripts/post-release-check.mjs +353 -0
- package/scripts/publish-asset-release.mjs +43 -0
- package/scripts/release-guard.mjs +63 -0
- package/scripts/release-paths.mjs +39 -2
- package/scripts/test.mjs +29 -7
- package/src/defaults/shipped-history.json +2 -0
- package/src/defaults/skills/computer-use/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +1 -0
- package/src/headless-role.mjs +2 -2
- package/src/lib/keychain-cjs.cjs +582 -17
- package/src/lib/rules-builder.cjs +0 -23
- package/src/rules/lead/LEAD.md +2 -2
- package/src/runtime/agent/orchestrator/config-storage.mjs +32 -4
- package/src/runtime/agent/orchestrator/config.mjs +18 -0
- package/src/runtime/agent/orchestrator/context/instruction-files.mjs +130 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/open-login-url.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +254 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +218 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +239 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +233 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-login.mjs +247 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +225 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +542 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +25 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +14 -6
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +99 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +4 -8
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +2 -7
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -5
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +12 -14
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-call-ids.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +19 -11
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +1 -5
- package/src/runtime/agent/orchestrator/session/read-dedup.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/turn-effort.mjs +18 -0
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +11 -8
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +9 -4
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +245 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +13 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +49 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -7
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +61 -8
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +28 -3
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +73 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +17 -6
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +23 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +93 -43
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +32 -6
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +9 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +124 -91
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +19 -4
- package/src/runtime/channels/index.mjs +1 -5
- package/src/runtime/channels/lib/runtime-paths.mjs +8 -23
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -13
- package/src/runtime/channels/lib/worker-ipc.mjs +2 -2
- package/src/runtime/channels/lib/worker-main.mjs +4 -34
- package/src/runtime/computer-bridge/client.mjs +25 -1
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +84 -0
- package/src/runtime/computer-bridge/linux-readiness.d.mts +52 -0
- package/src/runtime/computer-bridge/linux-readiness.mjs +411 -0
- package/src/runtime/computer-bridge/test-linux-display.mjs +5 -0
- package/src/runtime/effort-judge/compact-tokenizer.mjs +13 -8
- package/src/runtime/effort-judge/judge-input.mjs +5 -2
- package/src/runtime/effort-judge/model-install.mjs +9 -3
- package/src/runtime/effort-judge/model-manifest.json +7 -7
- package/src/runtime/memory/lib/embedding-worker.mjs +9 -3
- package/src/runtime/memory/lib/pg/supervisor.mjs +3 -0
- package/src/runtime/shared/config.mjs +50 -2
- package/src/runtime/shared/developer-options.mjs +7 -0
- package/src/runtime/shared/instruction-files-mode.mjs +9 -0
- package/src/runtime/shared/llm/model-catalog.mjs +2 -1
- package/src/runtime/shared/llm/openai-daybreak.mjs +79 -0
- package/src/runtime/shared/pristine-execution-contract.json +9 -0
- package/src/runtime/shared/pristine-execution.mjs +1 -1
- package/src/runtime/shared/provider-accounts.mjs +2 -0
- package/src/runtime/shared/resource-admission.mjs +10 -3
- package/src/runtime/shared/schedule-model-ref.mjs +11 -0
- package/src/runtime/shared/semver.d.mts +1 -0
- package/src/runtime/shared/semver.mjs +57 -0
- package/src/runtime/shared/session-approval-wait.mjs +35 -0
- package/src/runtime/shared/shell-display.mjs +4 -2
- package/src/runtime/shared/tool-surface.mjs +2 -2
- package/src/runtime/shared/turn-worktree-snapshot.mjs +11 -10
- package/src/runtime/shared/update-checker.mjs +2 -31
- package/src/session-runtime/boot/apis.mjs +5 -0
- package/src/session-runtime/boot/begin.mjs +0 -2
- package/src/session-runtime/boot/tools.mjs +5 -4
- package/src/session-runtime/model-recency.mjs +3 -0
- package/src/session-runtime/provider-auth/login.mjs +10 -10
- package/src/session-runtime/provider-auth/setup.mjs +22 -2
- package/src/session-runtime/provider-models/row-hydration.mjs +12 -4
- package/src/session-runtime/provider-readiness.mjs +4 -0
- package/src/session-runtime/resource-mcp-api.mjs +7 -3
- package/src/session-runtime/runtime-bootstrap.mjs +0 -4
- package/src/session-runtime/runtime-facade.mjs +5 -0
- package/src/session-runtime/runtime-paths.mjs +0 -1
- package/src/session-runtime/schedule-session-run.mjs +3 -1
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +55 -14
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +15 -3
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/approval-wait.mjs +97 -0
- package/src/session-runtime/services/agent-tool/worker-index/mutation-batch.mjs +4 -2
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -2
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +3 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +58 -2
- package/src/session-runtime/services/agent-tool/worker-index.mjs +69 -15
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +2 -0
- package/src/session-runtime/services/channel-admin.mjs +4 -2
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +80 -17
- package/src/session-runtime/services/seeds.mjs +7 -10
- package/src/session-runtime/services/usage-dashboard-model.mjs +6 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +15 -0
- package/src/session-runtime/settings-profile-api.mjs +27 -2
- package/src/session-runtime/setup-tool/executor.mjs +5 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
- package/src/session-runtime/starter-guide-state.mjs +62 -0
- package/src/session-runtime/tool-policy-refresh.mjs +3 -0
- package/src/session-runtime/webhook-session-run.mjs +3 -1
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -2
- package/src/standalone/channel-binding.mjs +1 -2
- package/src/standalone/channel-transport/call-dispatch.mjs +4 -10
- package/src/standalone/daemon-channels-loader.mjs +2 -3
- package/src/standalone/local-session-runtime.mjs +2 -78
- package/src/standalone/session-protocol.mjs +2 -0
- package/src/tui/app/model-options.mjs +13 -1
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +10 -4
- package/src/tui/app/provider-setup/oauth-actions.mjs +5 -0
- package/src/tui/dist/index.mjs +84 -34
- package/src/tui/session/context-state.mjs +2 -0
- package/src/tui/session/live-share/owner.mjs +17 -0
- package/src/tui/session/oauth-flows.mjs +2 -0
- package/src/tui/session/session-api/extensions.mjs +12 -8
- package/src/tui/session/session-api/integrations.mjs +9 -8
- package/src/tui/session/session-api/routes.mjs +2 -2
- package/src/tui/session/session-api/settings.mjs +1 -0
- package/src/tui/session/session-api/shared.mjs +10 -6
- package/src/tui/session/tool-card-results/grouped-result.mjs +11 -2
- package/src/tui/session/transcript-spill/spill-dir.mjs +22 -6
- package/src/tui/spinner-meta.mjs +26 -10
- package/src/runtime/channels/tool-defs.mjs +0 -17
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
buildJobNotFoundMessage,
|
|
4
4
|
peekShellJob,
|
|
5
5
|
killShellJob,
|
|
6
|
+
otherSessionBackgroundShellJobCount,
|
|
6
7
|
cancelBackgroundShellJobWatch,
|
|
7
8
|
clearShellJobNotifyCtx,
|
|
8
9
|
shellJobPublicTaskResult,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
} from '../../../../shared/background-tasks.mjs';
|
|
24
25
|
import { recordDeliveredCompletion } from '../../session/manager/delivered-completions.mjs';
|
|
25
26
|
import { listShellJobRecords, readShellJobRecord } from './lib/shell-job-records.mjs';
|
|
27
|
+
import { resourceAdmission } from '../../../../shared/resource-admission.mjs';
|
|
26
28
|
import { readShellTaskOutput } from './lib/shell-task-output.mjs';
|
|
27
29
|
|
|
28
30
|
// Ceiling for confirming that a cancelled task's process really exited. The
|
|
@@ -88,18 +90,42 @@ function resolveTaskWaitTimeoutMs(value) {
|
|
|
88
90
|
return Math.min(TASK_WAIT_TIMEOUT_MAX_MS, Math.max(TASK_WAIT_TIMEOUT_MIN_MS, Math.floor(raw)));
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
// Read-only counts of this runtime's shell work owned by OTHER sessions.
|
|
94
|
+
// Admission is runtime-wide while task list/cancel are session-scoped, so
|
|
95
|
+
// without this those holders are invisible. Counts only: another session's
|
|
96
|
+
// command text is never shown.
|
|
97
|
+
function otherSessionShellHolders(options) {
|
|
98
|
+
const context =
|
|
99
|
+
options?.context && typeof options.context === 'object' ? { ...options.context, ...options } : options;
|
|
100
|
+
const mine = new Set(
|
|
101
|
+
[context?.callerSessionId, context?.routingSessionId, context?.sessionId].filter(Boolean).map(String)
|
|
102
|
+
);
|
|
103
|
+
if (mine.size === 0) return '';
|
|
104
|
+
const leases = (resourceAdmission.snapshot().activeLeases || []).filter(
|
|
105
|
+
(lease) => lease.kind === 'shell' && !mine.has(String(lease.ownerKey || ''))
|
|
106
|
+
).length;
|
|
107
|
+
const jobs = otherSessionBackgroundShellJobCount(mine);
|
|
108
|
+
if (!leases && !jobs) return '';
|
|
109
|
+
return `[other sessions in this runtime] ${leases} foreground shell lease(s), ${jobs} background shell job(s) — read-only; only their owner sessions can cancel them.`;
|
|
110
|
+
}
|
|
111
|
+
|
|
91
112
|
async function listTasks(options) {
|
|
92
113
|
const rendered = renderBackgroundTaskList({ context: options });
|
|
93
114
|
const liveIds = new Set([...rendered.matchAll(/^-\s+(\S+)/gm)].map((match) => match[1]));
|
|
94
115
|
const recovered = (await listShellJobRecords()).filter(
|
|
95
116
|
(record) => recoveredTaskMatches(record, options) && !liveIds.has(record.jobId)
|
|
96
117
|
);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
118
|
+
const sections = [rendered];
|
|
119
|
+
if (recovered.length > 0) {
|
|
120
|
+
const rows = recovered.map(
|
|
121
|
+
(record) =>
|
|
122
|
+
`- ${record.jobId} shell ${record.terminal ? record.status : 'state-unavailable'} recovered=true command=${JSON.stringify(record.command || '')}`
|
|
123
|
+
);
|
|
124
|
+
sections.push(`[recovered shell tasks]\n${rows.join('\n')}`);
|
|
125
|
+
}
|
|
126
|
+
const others = otherSessionShellHolders(options);
|
|
127
|
+
if (others) sections.push(others);
|
|
128
|
+
return sections.join('\n');
|
|
103
129
|
}
|
|
104
130
|
|
|
105
131
|
// Blocks until the shell task settles, the timeout elapses, or the turn/user
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { createIoTrace } from './io-trace.mjs';
|
|
2
2
|
import { classifyResultKind } from '../session/result-classification.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
coerceShapeFlex,
|
|
5
|
+
normalizeInputPath,
|
|
6
|
+
normalizeOutputPath,
|
|
7
|
+
resolveAgainstCwd,
|
|
8
|
+
splitJoinedPathScopes,
|
|
9
|
+
} from './builtin/path-utils.mjs';
|
|
4
10
|
import {
|
|
5
11
|
SMART_BASH_HEAD_LINES,
|
|
6
12
|
SMART_BASH_MAX_BYTES,
|
|
@@ -387,7 +393,8 @@ export async function executeBuiltinTool(name, args, cwd, options = {}) {
|
|
|
387
393
|
readOffsetBase: args && Object.hasOwn(args, 'file_path') ? 1 : (options.readOffsetBase ?? 0),
|
|
388
394
|
};
|
|
389
395
|
}
|
|
390
|
-
let argError;
|
|
396
|
+
let argError = splitJoinedPathScopes(toolName, args, cwd);
|
|
397
|
+
if (argError) return argError;
|
|
391
398
|
if (toolName === 'grep') {
|
|
392
399
|
const prepared = prepareGrepArgs(args);
|
|
393
400
|
argError = prepared.error;
|
|
@@ -133,7 +133,9 @@ function collectGraphParseWarnings(graph, options) {
|
|
|
133
133
|
// instead of erroring "file not found in graph: (missing file)".
|
|
134
134
|
function _resolveRequestedMode(args) {
|
|
135
135
|
let mode = String(args?.mode || '').trim();
|
|
136
|
-
if (!mode)
|
|
136
|
+
if (!mode) {
|
|
137
|
+
throw new Error(`code_graph: "mode" is required, one of ${[...Object.keys(CODE_GRAPH_MODES), 'search'].join('|')}`);
|
|
138
|
+
}
|
|
137
139
|
if (mode === 'search') mode = 'symbol_search';
|
|
138
140
|
const nameOnlySymbols =
|
|
139
141
|
mode === 'symbols' &&
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* promote-job.mjs — hand the still-running foreground child to the shell-jobs
|
|
3
|
-
* registry: the bounded promotion attempt, the lease
|
|
4
|
-
*
|
|
3
|
+
* registry: the bounded promotion attempt, the lease release that follows a
|
|
4
|
+
* successful one, and the kill fallback that follows a failed one.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { promoteForegroundShellJob } from '../../builtin/shell-jobs.mjs';
|
|
7
7
|
import { treeKillForceSettle } from '../shell-run-state.mjs';
|
|
8
8
|
|
|
9
9
|
export async function promoteWithRetry(
|
|
@@ -74,17 +74,14 @@ export function killAfterFailedPromotion(run, reason, promotionFailure) {
|
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
/**
|
|
78
|
+
* The promoted job leaves foreground admission: its lease is RELEASED, which
|
|
79
|
+
* also restores the parent's high-load share. A long-lived server must never
|
|
80
|
+
* hold a foreground slot for its whole life; background jobs are not counted
|
|
81
|
+
* against any cap (session/runtime close stops them via shutdownShellJobs).
|
|
82
|
+
*/
|
|
83
|
+
export async function handOverLease(run) {
|
|
84
|
+
const lease = run.resourceLease;
|
|
79
85
|
run.resourceLease = null;
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
await promotedLease.detachDependency?.();
|
|
83
|
-
attachShellJobResourceLease(jobId, promotedLease);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
try {
|
|
86
|
-
await promotedLease.release();
|
|
87
|
-
} catch {}
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
86
|
+
await lease?.release();
|
|
90
87
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// exclusive with settle() via the autoBackgrounded flag set synchronously at
|
|
12
12
|
// the top before any await.
|
|
13
13
|
//
|
|
14
|
-
// shell-run-background/promote-job.mjs — registry promotion, lease
|
|
14
|
+
// shell-run-background/promote-job.mjs — registry promotion, lease release, kill fallback
|
|
15
15
|
// shell-run-background/promotion-results.mjs — the ExecResult of each promotion outcome
|
|
16
16
|
import { clearForegroundRecord, clearForegroundTimers, elapsedSinceStart } from './shell-run-state.mjs';
|
|
17
17
|
import { readCapturedOutput } from './shell-run-settle.mjs';
|
|
@@ -108,7 +108,7 @@ export function createAutoBackground({
|
|
|
108
108
|
}
|
|
109
109
|
const jobId = job.jobId;
|
|
110
110
|
run.autoBackgroundJobId = jobId;
|
|
111
|
-
await handOverLease(run
|
|
111
|
+
await handOverLease(run);
|
|
112
112
|
// Snapshot the partial output captured so far for the immediate result.
|
|
113
113
|
const { stdout, stderr } = await readCapturedOutput(taskOutput);
|
|
114
114
|
const capture = { stdout, stderr, spill: { ...promotedSpillPaths(taskOutput), stdoutPath } };
|
|
@@ -20,7 +20,7 @@ import { ShellTextDecoder, TaskOutput, treeKill } from '../shell-exec-output.mjs
|
|
|
20
20
|
const KILL_CONFIRM_INTERVAL_MS = 5000;
|
|
21
21
|
const KILL_CONFIRM_ATTEMPTS = 3;
|
|
22
22
|
|
|
23
|
-
export function createShellRun({ abortSignal, resolve }) {
|
|
23
|
+
export function createShellRun({ abortSignal, resolve, jobId = null }) {
|
|
24
24
|
const taskId = `shell_${randomUUID().slice(0, 8)}`;
|
|
25
25
|
let resultResolved = false;
|
|
26
26
|
return {
|
|
@@ -80,7 +80,9 @@ export function createShellRun({ abortSignal, resolve }) {
|
|
|
80
80
|
// reached a shell — not lease/preflight entry, and not a warm standby's
|
|
81
81
|
// process-creation time.
|
|
82
82
|
commandStartedAtMs: 0,
|
|
83
|
-
|
|
83
|
+
// Also the task_id the command keeps once promoted. A queued command
|
|
84
|
+
// passes its queued task's id so one id names it before and after.
|
|
85
|
+
foregroundRecordId: jobId || `job_${Date.now()}_${randomUUID().slice(0, 6)}`,
|
|
84
86
|
foregroundRecordTimer: null,
|
|
85
87
|
foregroundRecordPublished: false,
|
|
86
88
|
};
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
// Async one-shot shell runner.
|
|
2
2
|
//
|
|
3
|
-
// Replaces the legacy spawnSync path in builtin.mjs shell execution. The
|
|
4
|
-
// improvements over spawnSync are:
|
|
5
|
-
// - native process-tree termination on timeout / abort.
|
|
6
|
-
// - automatic spill to $PLUGIN_DATA/shell-output/<taskId>.* once the
|
|
7
|
-
// in-memory buffers exceed SHELL_OUTPUT_INLINE_CAP bytes. The caller
|
|
8
|
-
// receives an outputFilePath marker the model can FileRead later
|
|
9
|
-
// instead of losing the tail past the inline cap.
|
|
10
|
-
// - external AbortSignal hookup so a session-scoped abort (ESC, new
|
|
11
|
-
// prompt) cancels in-flight bash work without orphaning the child.
|
|
12
|
-
//
|
|
13
3
|
// One run is an explicit state record (lib/shell-run-state.mjs) driven
|
|
14
4
|
// through three phases: spawn (lib/shell-run-spawn.mjs), settle
|
|
15
5
|
// (lib/shell-run-settle.mjs) and the auto-background promotion
|
|
16
6
|
// (lib/shell-run-background.mjs). This module owns admission, preflight and
|
|
17
7
|
// the wiring between those phases.
|
|
18
|
-
import { startBackgroundTask } from '../../../shared/background-tasks.mjs';
|
|
8
|
+
import { getBackgroundTask, startBackgroundTask } from '../../../shared/background-tasks.mjs';
|
|
19
9
|
import { resourceAdmission } from '../../../shared/resource-admission.mjs';
|
|
10
|
+
import { buildShellCompletion, killShellJob, peekShellJob, subscribeShellJobSettled } from './builtin/shell-jobs.mjs';
|
|
20
11
|
import { ExecResult, treeKill } from './shell-exec-output.mjs';
|
|
21
12
|
import {
|
|
22
13
|
clearForegroundTimers,
|
|
@@ -50,25 +41,49 @@ const _envAdmissionWait = Math.floor(Number(process.env.MIXDOG_SHELL_ADMISSION_W
|
|
|
50
41
|
const SHELL_ADMISSION_WAIT_MS =
|
|
51
42
|
Number.isFinite(_envAdmissionWait) && _envAdmissionWait >= 0 ? _envAdmissionWait : 30_000;
|
|
52
43
|
|
|
53
|
-
|
|
44
|
+
// Foreground leases are labeled `[task_id: X] command` at spawn (X is the id
|
|
45
|
+
// the command keeps once promoted, or its queued task's id).
|
|
46
|
+
const LEASE_TASK_ID = /^\[task_id: ([^\]\s]+)\]/;
|
|
47
|
+
|
|
48
|
+
// Only the caller's OWN holders are described (oldest first, with task_id and
|
|
49
|
+
// an exact `task cancel` call when cancellable). Other sessions' holders are
|
|
50
|
+
// reported as a count with ages: their command text is never shown here.
|
|
51
|
+
function _admissionSaturationError(admission, waitMs, ownerKey = null) {
|
|
52
|
+
const own = String(ownerKey || '');
|
|
54
53
|
let detail = '';
|
|
55
54
|
try {
|
|
56
55
|
const snap = admission.snapshot();
|
|
57
|
-
const
|
|
56
|
+
const holders = (snap.activeLeases || [])
|
|
58
57
|
.filter((lease) => lease.kind === 'shell')
|
|
59
|
-
.
|
|
60
|
-
|
|
58
|
+
.sort((a, b) => b.ageMs - a.ageMs);
|
|
59
|
+
const rows = [];
|
|
60
|
+
const cancels = [];
|
|
61
|
+
const otherAges = [];
|
|
62
|
+
for (const lease of holders) {
|
|
63
|
+
const age = `${Math.round(lease.ageMs / 1000)}s`;
|
|
64
|
+
if (!own || lease.ownerKey !== own) {
|
|
65
|
+
otherAges.push(age);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const label = String(lease.label || '(unlabeled)');
|
|
69
|
+
const taskId = LEASE_TASK_ID.exec(label)?.[1] || null;
|
|
70
|
+
const cancellable = taskId && getBackgroundTask(taskId);
|
|
71
|
+
if (cancellable) cancels.push(`task cancel task_id=${taskId}`);
|
|
72
|
+
rows.push(`[${age}] ${label}${cancellable ? '' : ' (foreground call)'}`);
|
|
73
|
+
}
|
|
61
74
|
detail =
|
|
62
|
-
` ${snap.active.shell}/${snap.limits.maxShells} shell leases active
|
|
63
|
-
(
|
|
64
|
-
|
|
75
|
+
` ${snap.active.shell}/${snap.limits.maxShells} shell leases active, ${snap.queued} queued.` +
|
|
76
|
+
(rows.length ? ` Your holders, oldest first: ${rows.join(' | ')}.` : '') +
|
|
77
|
+
(cancels.length ? ` Free your own slots: ${cancels.join('; ')}.` : '') +
|
|
78
|
+
(otherAges.length
|
|
79
|
+
? ` ${otherAges.length} holder(s) belong to other sessions of this runtime (ages ${otherAges.join(', ')}); only their owners can cancel them.`
|
|
80
|
+
: '');
|
|
65
81
|
} catch {
|
|
66
82
|
/* diagnostics must not mask the timeout */
|
|
67
83
|
}
|
|
68
84
|
const error = new Error(
|
|
69
85
|
`shell admission wait exceeded ${waitMs}ms —${detail} ` +
|
|
70
|
-
'
|
|
71
|
-
'check task list, cancel stale tasks, kill lingering child processes, or restart the CLI.'
|
|
86
|
+
'A lease returns when its call ends or is promoted to the background; background jobs hold none.'
|
|
72
87
|
);
|
|
73
88
|
error.code = 'ERESOURCEPRESSURE';
|
|
74
89
|
error.admissionSaturated = true;
|
|
@@ -168,7 +183,7 @@ async function acquireShellLeaseBounded(
|
|
|
168
183
|
const deadlineAt = Date.now() + waitMs;
|
|
169
184
|
const deadline = setTimeout(() => {
|
|
170
185
|
try {
|
|
171
|
-
ctl.abort(_admissionSaturationError(admission, waitMs));
|
|
186
|
+
ctl.abort(_admissionSaturationError(admission, waitMs, ownerKey));
|
|
172
187
|
} catch {}
|
|
173
188
|
}, waitMs);
|
|
174
189
|
if (deadline.unref) deadline.unref();
|
|
@@ -223,7 +238,7 @@ async function admitAndSpawn(run, params, { admissionWaitMs, onSaturated }) {
|
|
|
223
238
|
run.resourceLease = await acquireShellLeaseBounded(admission, {
|
|
224
239
|
abortSignal,
|
|
225
240
|
waitMs: admissionWaitMs,
|
|
226
|
-
label: String(command || '').slice(0, 120)
|
|
241
|
+
label: `[task_id: ${run.foregroundRecordId}] ${String(command || '').slice(0, 120)}`,
|
|
227
242
|
ownerKey: ownerSessionId,
|
|
228
243
|
});
|
|
229
244
|
const policyErr = await _execPolicyBlockMessage(command);
|
|
@@ -252,36 +267,62 @@ async function admitAndSpawn(run, params, { admissionWaitMs, onSaturated }) {
|
|
|
252
267
|
}
|
|
253
268
|
}
|
|
254
269
|
|
|
270
|
+
// A queued command that outlived its foreground window was promoted under the
|
|
271
|
+
// queued task's id; the task settles with that job.
|
|
272
|
+
async function settleQueuedPromotion(jobId) {
|
|
273
|
+
await new Promise((resolve) => {
|
|
274
|
+
if (!subscribeShellJobSettled(jobId, resolve)) resolve();
|
|
275
|
+
});
|
|
276
|
+
const completion = buildShellCompletion(jobId, peekShellJob(jobId));
|
|
277
|
+
if (completion.taskStatus !== 'completed') {
|
|
278
|
+
throw new Error(completion.error || `shell task ${completion.taskStatus}`);
|
|
279
|
+
}
|
|
280
|
+
return completion.result;
|
|
281
|
+
}
|
|
282
|
+
|
|
255
283
|
// A saturated shell lane must not fail the call: the command becomes a tracked
|
|
256
|
-
// background task that waits (no admission ceiling) for a lease
|
|
257
|
-
//
|
|
284
|
+
// background task that waits (no admission ceiling) for a lease and then runs
|
|
285
|
+
// as a normal foreground-then-promote command under the task's id, so a
|
|
286
|
+
// long-lived one releases its lease at promotion like any other. The call
|
|
287
|
+
// returns its task_id immediately.
|
|
258
288
|
function queueSaturatedAsTask(run, saturation, spec) {
|
|
259
289
|
const queuedAbort = new AbortController();
|
|
290
|
+
const taskId = run.foregroundRecordId;
|
|
260
291
|
let task;
|
|
261
292
|
try {
|
|
262
293
|
task = startBackgroundTask({
|
|
263
|
-
taskId
|
|
294
|
+
taskId,
|
|
264
295
|
surface: 'shell',
|
|
265
296
|
operation: 'shell',
|
|
266
|
-
label: String(spec.command || '')
|
|
297
|
+
label: String(spec.command || '')
|
|
298
|
+
.replace(/\s+/g, ' ')
|
|
299
|
+
.slice(0, 120),
|
|
267
300
|
context: { callerSessionId: spec.ownerSessionId, clientHostPid: spec.clientHostPid },
|
|
268
301
|
input: { command: spec.command, cwd: spec.cwd },
|
|
269
|
-
meta: { task_id:
|
|
302
|
+
meta: { task_id: taskId, stdout: null, stderr: null, cwd: spec.cwd, timeoutMs: spec.backgroundDeadlineMs || 0 },
|
|
270
303
|
resultType: 'shell_task_result',
|
|
271
|
-
cancel: () =>
|
|
304
|
+
cancel: () => {
|
|
305
|
+
queuedAbort.abort();
|
|
306
|
+
try {
|
|
307
|
+
killShellJob(taskId);
|
|
308
|
+
} catch {}
|
|
309
|
+
},
|
|
272
310
|
run: async () => {
|
|
273
311
|
const result = await execShellCommand({
|
|
274
312
|
...spec,
|
|
313
|
+
jobId: taskId,
|
|
275
314
|
abortSignal: queuedAbort.signal,
|
|
276
|
-
autoBackgroundMs: 0,
|
|
277
|
-
backgroundOnTimeout: false,
|
|
278
315
|
admissionWaitMs: 0,
|
|
316
|
+
// The call already returned: nobody renders live progress.
|
|
317
|
+
onProgress: null,
|
|
318
|
+
onOutputTail: null,
|
|
279
319
|
});
|
|
320
|
+
if (result.backgrounded && result.jobId) return settleQueuedPromotion(result.jobId);
|
|
280
321
|
const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
|
|
281
322
|
if (result.exitCode !== 0) {
|
|
282
323
|
throw new Error(`exit code ${result.exitCode ?? result.signal ?? 'unknown'}${output ? `\n${output}` : ''}`);
|
|
283
324
|
}
|
|
284
|
-
return { task_id:
|
|
325
|
+
return { task_id: taskId, status: 'completed', exit_code: 0, output };
|
|
285
326
|
},
|
|
286
327
|
});
|
|
287
328
|
} catch {
|
|
@@ -474,72 +515,64 @@ function armDeadlines(run, { timeoutMs, autoBackgroundMs, backgroundOnTimeout, a
|
|
|
474
515
|
|
|
475
516
|
// Run one command to a terminal ExecResult. Spawn-time EPERM retries (Windows
|
|
476
517
|
// Defender scanning the child image) live in lib/shell-spawn-retry.mjs.
|
|
477
|
-
export function execShellCommand({
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
518
|
+
export function execShellCommand(options) {
|
|
519
|
+
const {
|
|
520
|
+
shell,
|
|
521
|
+
shellArg,
|
|
522
|
+
shellArgs,
|
|
523
|
+
command,
|
|
524
|
+
env,
|
|
525
|
+
cwd,
|
|
526
|
+
timeoutMs,
|
|
527
|
+
abortSignal,
|
|
528
|
+
autoBackgroundMs,
|
|
529
|
+
onProgress,
|
|
530
|
+
onOutputTail,
|
|
531
|
+
clientHostPid,
|
|
532
|
+
ownerSessionId,
|
|
533
|
+
backgroundOnTimeout,
|
|
534
|
+
promotedTimeoutMs = 0,
|
|
535
|
+
backgroundDeadlineMs = 0,
|
|
536
|
+
// Fixed task_id for the run (a queued command reuses its task's id).
|
|
537
|
+
jobId = null,
|
|
538
|
+
admissionWaitMs = SHELL_ADMISSION_WAIT_MS,
|
|
539
|
+
admission = resourceAdmission,
|
|
540
|
+
directArgv = null,
|
|
541
|
+
// What the shell PARSES, when that must differ from what the caller shows.
|
|
542
|
+
// `command` stays the display/record text (job labels, telemetry, shell-job
|
|
543
|
+
// rows); `execScript` is the argv payload. They diverge only when a caller
|
|
544
|
+
// hands the script to the shell out-of-band — see the POSIX env+eval path in
|
|
545
|
+
// bash-tool.mjs, which keeps the command body out of the child's argv.
|
|
546
|
+
execScript = null,
|
|
547
|
+
} = options;
|
|
504
548
|
return new Promise(async (resolve) => {
|
|
505
|
-
const run = createShellRun({ abortSignal, resolve });
|
|
549
|
+
const run = createShellRun({ abortSignal, resolve, jobId });
|
|
506
550
|
// An async executor's throw is NOT routed to this Promise — it becomes an
|
|
507
551
|
// unhandled rejection of the executor's own promise while the awaiting
|
|
508
552
|
// caller hangs forever. Every path below therefore has to settle the run
|
|
509
553
|
// itself, including the unexpected one.
|
|
510
554
|
try {
|
|
511
|
-
const spawned = await admitAndSpawn(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
command,
|
|
533
|
-
env,
|
|
534
|
-
cwd,
|
|
535
|
-
timeoutMs: backgroundDeadlineMs,
|
|
536
|
-
clientHostPid,
|
|
537
|
-
ownerSessionId,
|
|
538
|
-
admission,
|
|
539
|
-
directArgv,
|
|
540
|
-
execScript,
|
|
541
|
-
}),
|
|
542
|
-
});
|
|
555
|
+
const spawned = await admitAndSpawn(
|
|
556
|
+
run,
|
|
557
|
+
{
|
|
558
|
+
admission,
|
|
559
|
+
shell,
|
|
560
|
+
shellArg,
|
|
561
|
+
shellArgs,
|
|
562
|
+
command,
|
|
563
|
+
env,
|
|
564
|
+
cwd,
|
|
565
|
+
abortSignal,
|
|
566
|
+
directArgv,
|
|
567
|
+
execScript,
|
|
568
|
+
ownerSessionId,
|
|
569
|
+
clientHostPid,
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
admissionWaitMs,
|
|
573
|
+
onSaturated: (err) => backgroundOnTimeout && queueSaturatedAsTask(run, err, options),
|
|
574
|
+
}
|
|
575
|
+
);
|
|
543
576
|
if (!spawned) return;
|
|
544
577
|
|
|
545
578
|
// Pre-aborted signal: kill immediately if the abort already fired
|
|
@@ -83,23 +83,36 @@ function classifyExecPolicy(command) {
|
|
|
83
83
|
const decodedPowerShell = decodePowerShellEncodedCommand(text);
|
|
84
84
|
if (decodedPowerShell) executableTargets.push(stripQuotedAndHeredoc(decodedPowerShell));
|
|
85
85
|
if (executableTargets.some((target) => isBlockedCommand(target))) {
|
|
86
|
-
return {
|
|
86
|
+
return {
|
|
87
|
+
decision: 'deny',
|
|
88
|
+
reason: 'destructive or system-destabilising pattern (hard block)',
|
|
89
|
+
rule: 'hard-block',
|
|
90
|
+
};
|
|
87
91
|
}
|
|
88
92
|
// Hard-deny only real executable syntax. The policy scanner also sees user
|
|
89
93
|
// search strings / regex literals (e.g. `-match 'powershell|bash|grep'`), so
|
|
90
94
|
// high-risk pipe-to-shell / IEX / RunAs patterns must ignore quoted spans.
|
|
91
95
|
const executableText = stripQuotedAndHeredoc(text);
|
|
92
96
|
for (const pat of EXEC_POLICY_DENY_PATTERNS) {
|
|
93
|
-
|
|
97
|
+
const hit = pat.exec(executableText);
|
|
98
|
+
if (hit) {
|
|
94
99
|
return {
|
|
95
100
|
decision: 'deny',
|
|
96
101
|
reason: 'high-risk shell invocation (pipe-to-shell, elevated launcher, or remote-exec pattern)',
|
|
102
|
+
rule: `high-risk-pattern-${EXEC_POLICY_DENY_PATTERNS.indexOf(pat) + 1}`,
|
|
103
|
+
match: hit[0].trim().slice(0, 80),
|
|
104
|
+
alternative: 'save the script to a file, inspect it, then run it directly',
|
|
97
105
|
};
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
for (const name of _commandNamesAtPositions(executableText)) {
|
|
101
109
|
if (EXEC_POLICY_DENY_COMMANDS.has(name)) {
|
|
102
|
-
return {
|
|
110
|
+
return {
|
|
111
|
+
decision: 'deny',
|
|
112
|
+
reason: `command "${name}" is not permitted`,
|
|
113
|
+
rule: 'denied-command',
|
|
114
|
+
match: name,
|
|
115
|
+
};
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
// Destructive-but-legitimate commands are NOT judged here. The caller
|
|
@@ -131,7 +144,9 @@ export function evaluateExecPolicyFromTargets(targets) {
|
|
|
131
144
|
export function formatExecPolicyBlockMessage(policyResult) {
|
|
132
145
|
const r = policyResult || { decision: 'allow' };
|
|
133
146
|
if (r.decision === 'deny') {
|
|
134
|
-
|
|
147
|
+
const rule = r.rule ? ` [rule ${r.rule}${r.match ? `: "${r.match}"` : ''}]` : '';
|
|
148
|
+
const alt = r.alternative ? `; instead: ${r.alternative}` : '';
|
|
149
|
+
return `Error: command blocked by exec policy${r.reason ? ` — ${r.reason}` : ''}${rule}${alt}`;
|
|
135
150
|
}
|
|
136
151
|
return null;
|
|
137
152
|
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
// Thin facade for the channels worker runtime. The implementation was split
|
|
2
2
|
// out into ./lib/worker-main.mjs plus focused lib/ modules; this file preserves
|
|
3
|
-
// the
|
|
3
|
+
// the current worker API and boot side-effects by re-exporting from worker-main.
|
|
4
4
|
// Kept as the channels worker entrypoint the daemon loads.
|
|
5
5
|
export {
|
|
6
|
-
TOOL_DEFS,
|
|
7
6
|
handleToolCall,
|
|
8
|
-
handleToolCallWithBridgeRetry,
|
|
9
|
-
init,
|
|
10
|
-
instructions,
|
|
11
7
|
isChannelBridgeActive,
|
|
12
8
|
isChannelsDegraded,
|
|
13
9
|
start,
|
|
@@ -114,8 +114,7 @@ function getControlResponsePath(instanceId) {
|
|
|
114
114
|
function getStopFlagPath(instanceId) {
|
|
115
115
|
return join(RUNTIME_ROOT, `stop-${sanitize(instanceId)}.flag`);
|
|
116
116
|
}
|
|
117
|
-
// Pure metadata-advert read
|
|
118
|
-
// advert is never evicted here. A transient read during an atomic rename may
|
|
117
|
+
// Pure metadata-advert read. A transient read during an atomic rename may
|
|
119
118
|
// yield empty/partial content: retry once after 50 ms before reporting the
|
|
120
119
|
// advert as unreadable.
|
|
121
120
|
function readActiveInstance() {
|
|
@@ -141,8 +140,6 @@ function buildActiveInstanceState(instanceId, meta) {
|
|
|
141
140
|
supervisor_started_at: Date.now(),
|
|
142
141
|
server_started_at: Date.now(),
|
|
143
142
|
updatedAt: Date.now(),
|
|
144
|
-
turnEndFile: getTurnEndPath(instanceId),
|
|
145
|
-
statusFile: getStatusPath(instanceId),
|
|
146
143
|
...metaFields(meta),
|
|
147
144
|
};
|
|
148
145
|
}
|
|
@@ -193,26 +190,14 @@ function refreshActiveInstance(instanceId, meta, options) {
|
|
|
193
190
|
(curRaw) => {
|
|
194
191
|
const prevForPreserve = curRaw;
|
|
195
192
|
const prev = activeInstanceStaleReason(curRaw) ? null : curRaw;
|
|
196
|
-
// CAS guard (opt-in via options.onlyIfOwned): heartbeat/refresh ticks
|
|
197
|
-
// check ownership OUTSIDE this lock, so a newer session can claim the
|
|
198
|
-
// seat between that check and this locked update. Without this re-read,
|
|
199
|
-
// the tick would overwrite the newer owner's instanceId (TOCTOU
|
|
200
|
-
// re-steal -> ownership ping-pong / double provider connections).
|
|
201
|
-
// Returning undefined aborts the update with no write. Explicit claims
|
|
202
|
-
// (boot, claimBridgeOwnership) omit the option and stay last-wins.
|
|
203
|
-
if (options?.onlyIfOwned && (!prev?.instanceId || prev.instanceId !== instanceId)) {
|
|
204
|
-
return undefined;
|
|
205
|
-
}
|
|
206
|
-
// CAS guard (opt-in via options.onlyIfVacant): auto-start claim-if-vacant.
|
|
207
|
-
// Abort the write when a live (non-stale) owner OTHER than us already holds
|
|
208
|
-
// the seat — an auto-start must never steal from a live owner. A stale/dead
|
|
209
|
-
// prior owner leaves prev=null above, so it does NOT block the claim; an
|
|
210
|
-
// absent seat (prev=null) is claimable too.
|
|
211
|
-
if (options?.onlyIfVacant && prev?.instanceId && prev.instanceId !== instanceId) {
|
|
212
|
-
return undefined;
|
|
213
|
-
}
|
|
214
193
|
// Drop stale fields (pid/startedAt) written by older server versions.
|
|
215
|
-
const {
|
|
194
|
+
const {
|
|
195
|
+
pid: _legacyPid,
|
|
196
|
+
startedAt: _legacyStartedAt,
|
|
197
|
+
turnEndFile: _turnEndFile,
|
|
198
|
+
statusFile: _statusFile,
|
|
199
|
+
...prevRest
|
|
200
|
+
} = prev ?? {};
|
|
216
201
|
const identity = buildRuntimeIdentity();
|
|
217
202
|
// server_started_at tracks the CURRENT server_pid's start time so the
|
|
218
203
|
// dev-sync barrier can verify the CHILD's freshness (the supervisor's
|
|
@@ -82,12 +82,6 @@ function createToolDispatch({ isChannelsDegraded, lifecycle }) {
|
|
|
82
82
|
};
|
|
83
83
|
break;
|
|
84
84
|
}
|
|
85
|
-
case 'rebind_current_transcript': {
|
|
86
|
-
// Transcript forwarding is retired with channel messaging: accept
|
|
87
|
-
// and no-op so legacy lead-side pushes never surface an error.
|
|
88
|
-
result = { content: [{ type: 'text', text: 'transcript rebind retired' }] };
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
85
|
// memory — handled by memory-service.mjs MCP
|
|
92
86
|
default:
|
|
93
87
|
result = {
|
|
@@ -105,13 +99,7 @@ function createToolDispatch({ isChannelsDegraded, lifecycle }) {
|
|
|
105
99
|
return result;
|
|
106
100
|
}
|
|
107
101
|
|
|
108
|
-
|
|
109
|
-
// pre-flush or tool-log relay — plain dispatch.
|
|
110
|
-
async function handleToolCallWithBridgeRetry(toolName, args, signal) {
|
|
111
|
-
return handleToolCall(toolName, args, signal);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return { handleToolCall, handleToolCallWithBridgeRetry };
|
|
102
|
+
return { handleToolCall };
|
|
115
103
|
}
|
|
116
104
|
|
|
117
105
|
export { createToolDispatch };
|
|
@@ -14,7 +14,7 @@ export function runWorkerIpc({
|
|
|
14
14
|
getProvider: _getProvider,
|
|
15
15
|
getConfig: _getConfig,
|
|
16
16
|
handleMemoryCallResponse,
|
|
17
|
-
|
|
17
|
+
handleToolCall,
|
|
18
18
|
bootProfile,
|
|
19
19
|
}) {
|
|
20
20
|
const sendToParent = (message) =>
|
|
@@ -94,7 +94,7 @@ export function runWorkerIpc({
|
|
|
94
94
|
_inFlightChannelCalls.set(msg.callId, ac);
|
|
95
95
|
let result;
|
|
96
96
|
try {
|
|
97
|
-
result = await
|
|
97
|
+
result = await handleToolCall(msg.name, msg.args || {}, ac.signal);
|
|
98
98
|
} finally {
|
|
99
99
|
_inFlightChannelCalls.delete(msg.callId);
|
|
100
100
|
}
|