mixdog 0.9.147 → 0.9.149
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/package.json +23 -3
- package/scripts/build-voice-runtime-unix.sh +101 -0
- package/scripts/embedding-quality-probe.mjs +96 -0
- package/scripts/generate-voice-runtime-manifest.mjs +300 -0
- package/scripts/generate-voice-runtime-manifest.test.mjs +135 -0
- package/scripts/native-binary-arch.mjs +30 -13
- package/scripts/native-binary-arch.test.mjs +40 -4
- package/scripts/plan-voice-runtime-release.mjs +73 -0
- package/scripts/prepare-native-assets.mjs +50 -9
- package/scripts/prune-embedding-runtime.mjs +3 -3
- package/scripts/sync-voice-runtime-manifest.mjs +46 -0
- package/scripts/verify-release-assets.mjs +106 -6
- package/scripts/verify-voice-runtime-release.mjs +89 -0
- package/scripts/voice-runtime-config.json +52 -0
- package/src/app.mjs +42 -5
- package/src/headless-exec.mjs +58 -32
- package/src/headless-exec.test.mjs +74 -13
- package/src/rules/lead/01-general.md +2 -0
- package/src/rules/shared/10-tool-workflow.md +7 -4
- package/src/rules/shared/30-exploration.md +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +28 -9
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +6 -1
- package/src/runtime/agent/orchestrator/config.mjs +53 -18
- package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +139 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +34 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -10
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +98 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +67 -16
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +4 -82
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +0 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +62 -121
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +48 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +215 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +44 -48
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +54 -31
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +56 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +34 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +61 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +61 -15
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +120 -1
- package/src/runtime/agent/orchestrator/providers/lib/env-utils.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +0 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +187 -84
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +32 -9
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +78 -33
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +21 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +36 -36
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +16 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +50 -213
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +135 -126
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +20 -11
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +112 -36
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +220 -50
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +112 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +130 -60
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +28 -10
- package/src/runtime/agent/orchestrator/providers/registry.mjs +122 -95
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +69 -3
- package/src/runtime/agent/orchestrator/providers/tool-stream-state.mjs +4 -22
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +7 -14
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +27 -48
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +19 -14
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +9 -43
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +43 -5
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +325 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +599 -195
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +22 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +26 -16
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +23 -11
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +118 -3
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +332 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +38 -1
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +27 -6
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +291 -128
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -9
- package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +224 -0
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +53 -3
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +255 -25
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +87 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +132 -9
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-pattern-fanout.mjs +29 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +180 -31
- package/src/runtime/agent/orchestrator/tools/builtin/local-search-telemetry.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +32 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +38 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +31 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +11 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-windows.mjs +22 -4
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +6 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +25 -24
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +152 -133
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +114 -42
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +129 -15
- package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +72 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +7 -8
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +201 -19
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +26 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +55 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +115 -25
- package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +268 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +296 -46
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +104 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +136 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +127 -38
- package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +661 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +350 -9
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +418 -44
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +27 -3
- package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +552 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +65 -15
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +284 -109
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +51 -21
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +44 -27
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/attachments/store.mjs +11 -3
- package/src/runtime/channels/data/voice-runtime-manifest.json +46 -70
- package/src/runtime/channels/lib/event-queue.mjs +51 -41
- package/src/runtime/channels/lib/scheduler.mjs +23 -28
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -17
- package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +17 -0
- package/src/runtime/channels/lib/webhook.mjs +39 -20
- package/src/runtime/channels/lib/whisper-server.mjs +46 -1
- package/src/runtime/channels/lib/worker-main.mjs +3 -1
- package/src/runtime/media/adapters/gemini-video.mjs +20 -4
- package/src/runtime/media/jobs.mjs +13 -0
- package/src/runtime/media/renditions.mjs +7 -1
- package/src/runtime/media/store.mjs +42 -22
- package/src/runtime/memory/index.mjs +4 -9
- package/src/runtime/memory/lib/agent-ipc.mjs +1 -6
- package/src/runtime/memory/lib/archive-security.test.mjs +21 -1
- package/src/runtime/memory/lib/core-memory-store.mjs +28 -50
- package/src/runtime/memory/lib/embedding-model-config.mjs +37 -1
- package/src/runtime/memory/lib/embedding-model-config.test.mjs +33 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +34 -12
- package/src/runtime/memory/lib/embedding-worker.mjs +37 -5
- package/src/runtime/memory/lib/ko-morph.mjs +46 -15
- package/src/runtime/memory/lib/memory-action-handlers.mjs +18 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +98 -45
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +71 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +78 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +37 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +50 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +9 -24
- package/src/runtime/memory/lib/memory.mjs +50 -12
- package/src/runtime/memory/lib/pg/process.mjs +1 -10
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -9
- package/src/runtime/memory/lib/query-handlers.mjs +2 -4
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +2 -2
- package/src/runtime/shared/background-tasks.mjs +15 -0
- package/src/runtime/shared/background-tasks.test.mjs +78 -0
- package/src/runtime/shared/child-guardian.mjs +2 -10
- package/src/runtime/shared/pid-liveness.mjs +27 -0
- package/src/runtime/shared/pristine-execution-contract.json +0 -1
- package/src/runtime/shared/runtime-root.mjs +1 -11
- package/src/runtime/shared/service-discovery.mjs +1 -17
- package/src/runtime/shared/singleton-owner.mjs +1 -11
- package/src/runtime/shared/staged-update.mjs +2 -12
- package/src/runtime/shared/tool-status.mjs +5 -1
- package/src/runtime/shared/tool-surface.mjs +15 -7
- package/src/runtime/shared/turn-snapshot-store.mjs +168 -0
- package/src/runtime/shared/turn-snapshot.mjs +137 -13
- package/src/runtime/shared/turn-worktree-snapshot.mjs +110 -18
- package/src/runtime/shared/webhook-session-run.mjs +16 -2
- package/src/runtime/web-search/lib/http-fetch.mjs +34 -4
- package/src/runtime/web-search/lib/state.mjs +15 -15
- package/src/runtime/web-search/lib/web-tools.mjs +46 -5
- package/src/session-runtime/context-status.mjs +4 -12
- package/src/session-runtime/runtime-core.mjs +20 -1
- package/src/session-runtime/session-lifecycle.mjs +9 -0
- package/src/standalone/agent-dispatch-broker.mjs +33 -2
- package/src/standalone/agent-tool/job-task-reconcile.mjs +78 -0
- package/src/standalone/agent-tool/notify.mjs +3 -1
- package/src/standalone/agent-tool/spawn-flow.mjs +52 -70
- package/src/standalone/agent-tool/tag-registry.mjs +116 -33
- package/src/standalone/agent-tool/tool-def.mjs +2 -2
- package/src/standalone/agent-tool/worker-index.mjs +8 -11
- package/src/standalone/agent-tool/worker-rows.mjs +4 -1
- package/src/standalone/agent-tool.mjs +20 -55
- package/src/standalone/agent-watchdog-registry.mjs +28 -4
- package/src/standalone/channel-client.mjs +47 -15
- package/src/standalone/channel-transport.mjs +44 -25
- package/src/standalone/daemon.mjs +31 -2
- package/src/standalone/hook-bus/config.mjs +25 -9
- package/src/standalone/hook-bus/handlers.mjs +16 -52
- package/src/standalone/hook-bus.mjs +29 -23
- package/src/standalone/memory-runtime-proxy.mjs +1 -16
- package/src/standalone/session-client.mjs +86 -23
- package/src/standalone/session-protocol.mjs +0 -2
- package/src/standalone/session-runtime-host-health.test.mjs +5 -4
- package/src/standalone/session-runtime-host.mjs +101 -6
- package/src/standalone/session-runtime-record.mjs +1 -5
- package/src/standalone/session-runtime-worker.mjs +52 -57
- package/src/standalone/session-service.mjs +4 -1
- package/src/standalone/session-transport.mjs +56 -25
- package/src/tui/App.jsx +40 -49
- package/src/tui/app/app-view.jsx +14 -21
- package/src/tui/app/core-memory-picker.mjs +29 -16
- package/src/tui/app/create-app-pickers.mjs +15 -36
- package/src/tui/app/extension-pickers.mjs +60 -49
- package/src/tui/app/maintenance-pickers.mjs +100 -57
- package/src/tui/app/message-selector.mjs +6 -5
- package/src/tui/app/model-picker.mjs +30 -13
- package/src/tui/app/onboarding-steps.mjs +21 -12
- package/src/tui/app/panel-epoch.mjs +56 -0
- package/src/tui/app/panel-epoch.test.mjs +1009 -0
- package/src/tui/app/panel-surface.mjs +98 -0
- package/src/tui/app/project-picker.mjs +30 -29
- package/src/tui/app/prompt-submit.mjs +192 -97
- package/src/tui/app/prompt-submit.test.mjs +206 -0
- package/src/tui/app/provider-setup-picker.mjs +74 -43
- package/src/tui/app/resume-picker.mjs +11 -8
- package/src/tui/app/route-pickers.mjs +45 -28
- package/src/tui/app/settings-picker.mjs +69 -18
- package/src/tui/app/shell-layout.mjs +1 -3
- package/src/tui/app/slash-commands.mjs +0 -2
- package/src/tui/app/slash-dispatch.mjs +4 -2
- package/src/tui/app/text-entry-policy.mjs +25 -0
- package/src/tui/app/theme-effort-pickers.mjs +11 -16
- package/src/tui/app/usage-context-panels.mjs +21 -25
- package/src/tui/app/use-global-key-input.mjs +3 -2
- package/src/tui/app/use-prompt-draft-flow.mjs +6 -18
- package/src/tui/app/use-prompt-handlers.mjs +3 -2
- package/src/tui/app/use-welcome-prompt-hint.mjs +17 -3
- package/src/tui/components/Picker.jsx +1 -1
- package/src/tui/components/TextEntryPanel.jsx +7 -2
- package/src/tui/dist/index.mjs +652 -561
- package/src/tui/lib/voice-setup.mjs +1 -2
- package/src/tui/session/agent-job-feed.mjs +73 -11
- package/src/tui/session/session-api-ext.mjs +5 -6
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +15 -5
- package/src/tui/session/tui-steering-persist.mjs +33 -19
- package/src/tui/session/tui-steering-persist.test.mjs +104 -0
- package/src/tui/session-local.mjs +0 -22
- package/scripts/.tmp-coverage-report.mjs +0 -96
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.test.mjs +0 -57
|
@@ -17,6 +17,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
17
17
|
import { readFileSync, rmSync } from 'node:fs';
|
|
18
18
|
import { basename } from 'node:path';
|
|
19
19
|
import { writeJsonAtomicSync } from '../runtime/shared/atomic-file.mjs';
|
|
20
|
+
import { isPidAlive, parsePid } from '../runtime/shared/pid-liveness.mjs';
|
|
20
21
|
import { readBody, sendJson, sendError } from '../runtime/memory/lib/http-wire.mjs';
|
|
21
22
|
import { createFairCallScheduler } from './fair-call-scheduler.mjs';
|
|
22
23
|
|
|
@@ -26,17 +27,6 @@ function readChannelBody(req) {
|
|
|
26
27
|
return readBody(req, { maxBytes: CHANNEL_HTTP_BODY_MAX_BYTES });
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
function parsePid(value) {
|
|
30
|
-
const n = Number(value);
|
|
31
|
-
return Number.isInteger(n) && n > 0 ? n : null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function isPidAlive(pid) {
|
|
35
|
-
const n = parsePid(pid);
|
|
36
|
-
if (!n) return false;
|
|
37
|
-
try { process.kill(n, 0); return true; }
|
|
38
|
-
catch (error) { return error?.code === 'EPERM'; }
|
|
39
|
-
}
|
|
40
30
|
|
|
41
31
|
const ACTIVATE_TOOL = 'activate_channel_bridge';
|
|
42
32
|
const REBIND_TOOL = 'rebind_current_transcript';
|
|
@@ -103,6 +93,16 @@ export function createChannelTransport({
|
|
|
103
93
|
queueMax: Math.max(16, Math.min(64, CALL_QUEUE_MAX)),
|
|
104
94
|
minOwnerQueue: 4,
|
|
105
95
|
});
|
|
96
|
+
// Binding calls (manual ON/OFF, rebind) mutate GLOBAL pointer/pin state after
|
|
97
|
+
// reading it. The control lane is unbounded, so two sessions toggling at once
|
|
98
|
+
// would interleave read and write and clobber each other's binding. Every
|
|
99
|
+
// binding call therefore runs alone on this chain.
|
|
100
|
+
let bindingChain = Promise.resolve();
|
|
101
|
+
function runExclusiveBinding(run) {
|
|
102
|
+
const result = bindingChain.then(run, run);
|
|
103
|
+
bindingChain = result.then(() => {}, () => {});
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
106
|
// Reconnect register replay: a server may commit replacement just before its
|
|
107
107
|
// HTTP response is lost. The retry supplies this stable id and receives the
|
|
108
108
|
// already-created fresh token instead of creating an orphan replacement.
|
|
@@ -813,6 +813,17 @@ export function createChannelTransport({
|
|
|
813
813
|
const previousRemoteSessionId = c.remoteSessionId || null;
|
|
814
814
|
const previousRemoteAcquired = remoteAcquired;
|
|
815
815
|
const previousStickyRemoteFrame = stickyRemoteFrame;
|
|
816
|
+
// A failed manual ON must leave ownership exactly as it was.
|
|
817
|
+
const rollbackActivation = () => {
|
|
818
|
+
if (!activating || pointerToken !== clientToken
|
|
819
|
+
|| c.remoteSessionId !== bindingSessionId) return;
|
|
820
|
+
pointerToken = previousPointerToken && clients.has(previousPointerToken)
|
|
821
|
+
? previousPointerToken
|
|
822
|
+
: null;
|
|
823
|
+
c.remoteSessionId = previousRemoteSessionId;
|
|
824
|
+
remoteAcquired = previousRemoteAcquired;
|
|
825
|
+
stickyRemoteFrame = previousStickyRemoteFrame;
|
|
826
|
+
};
|
|
816
827
|
if (activating) {
|
|
817
828
|
c.remoteSessionId = bindingSessionId;
|
|
818
829
|
movePointer(clientToken, 'manual remote ON', { notifyDisplaced: false });
|
|
@@ -823,6 +834,16 @@ export function createChannelTransport({
|
|
|
823
834
|
leadPid: c?.leadPid ?? null,
|
|
824
835
|
cwd: c?.cwd ?? null,
|
|
825
836
|
});
|
|
837
|
+
// A soft `{isError:true}` envelope is a FAILED call, not a
|
|
838
|
+
// result: taking the success path here moved control ownership
|
|
839
|
+
// on a failed ON and cleared the durable pin on a failed OFF
|
|
840
|
+
// while the runtime was still bound.
|
|
841
|
+
if (result?.isError === true) {
|
|
842
|
+
rollbackActivation();
|
|
843
|
+
log(`${name} failed (soft error); binding state unchanged for session=${bindingSessionId || '?'}`);
|
|
844
|
+
publishRemoteState();
|
|
845
|
+
return result;
|
|
846
|
+
}
|
|
826
847
|
if (activating && previousPointerToken && previousPointerToken !== clientToken) {
|
|
827
848
|
const displaced = clients.get(previousPointerToken);
|
|
828
849
|
if (displaced && isPidAlive(displaced.leadPid)
|
|
@@ -837,7 +858,7 @@ export function createChannelTransport({
|
|
|
837
858
|
remoteAcquired = false;
|
|
838
859
|
stickyRemoteFrame = null;
|
|
839
860
|
}
|
|
840
|
-
if (activating
|
|
861
|
+
if (activating
|
|
841
862
|
&& pointerToken === clientToken
|
|
842
863
|
&& c.remoteSessionId === bindingSessionId) {
|
|
843
864
|
remoteAcquired = true;
|
|
@@ -846,21 +867,14 @@ export function createChannelTransport({
|
|
|
846
867
|
publishRemoteState();
|
|
847
868
|
return result;
|
|
848
869
|
} catch (err) {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
pointerToken = previousPointerToken && clients.has(previousPointerToken)
|
|
852
|
-
? previousPointerToken
|
|
853
|
-
: null;
|
|
854
|
-
c.remoteSessionId = previousRemoteSessionId;
|
|
855
|
-
remoteAcquired = previousRemoteAcquired;
|
|
856
|
-
stickyRemoteFrame = previousStickyRemoteFrame;
|
|
857
|
-
publishRemoteState();
|
|
858
|
-
}
|
|
870
|
+
rollbackActivation();
|
|
871
|
+
publishRemoteState();
|
|
859
872
|
throw err;
|
|
860
873
|
}
|
|
861
874
|
};
|
|
862
|
-
const
|
|
863
|
-
|
|
875
|
+
const bindingCall = BINDING_TOOLS.has(name);
|
|
876
|
+
const scheduler = bindingCall ? channelControlCalls : channelCalls;
|
|
877
|
+
dispatch = scheduler.enqueue(ownerKey, bindingCall ? () => runExclusiveBinding(run) : run);
|
|
864
878
|
if (cacheKey) {
|
|
865
879
|
const record = { promise: dispatch, at: nowMs() };
|
|
866
880
|
callCache.set(cacheKey, record);
|
|
@@ -880,7 +894,12 @@ export function createChannelTransport({
|
|
|
880
894
|
const result = await dispatch;
|
|
881
895
|
sendJson(res, { result });
|
|
882
896
|
} catch (err) {
|
|
883
|
-
|
|
897
|
+
// Keep the machine-readable code beside the message: callers branch
|
|
898
|
+
// on it instead of pattern-matching free text.
|
|
899
|
+
sendJson(res, {
|
|
900
|
+
error: err?.message || String(err),
|
|
901
|
+
...(err?.code ? { code: String(err.code) } : {}),
|
|
902
|
+
}, 200);
|
|
884
903
|
}
|
|
885
904
|
return;
|
|
886
905
|
}
|
|
@@ -40,6 +40,7 @@ import { writeJsonAtomicSync } from '../runtime/shared/atomic-file.mjs';
|
|
|
40
40
|
import { ensurePrivateRuntimeRoot, resolveRuntimeRoot } from '../runtime/shared/runtime-root.mjs';
|
|
41
41
|
import { ensureProcessListenerHeadroom } from '../runtime/shared/process-listener-headroom.mjs';
|
|
42
42
|
import { claimSingletonOwner, releaseSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
|
|
43
|
+
import { remoteIntentPath } from '../runtime/shared/remote-intent.mjs';
|
|
43
44
|
import { PLUGIN_LOG_MAX_BYTES, PLUGIN_LOG_KEEP_BYTES } from '../lib/mixdog-debug.cjs';
|
|
44
45
|
import { setChannelNotifySink } from '../runtime/channels/lib/parent-bridge.mjs';
|
|
45
46
|
import { setOwnerContext } from '../runtime/channels/lib/runtime-paths.mjs';
|
|
@@ -484,6 +485,19 @@ async function main() {
|
|
|
484
485
|
handleCall,
|
|
485
486
|
agentBroker: agentDispatchBroker,
|
|
486
487
|
log,
|
|
488
|
+
// The durable channel link lives in this file. Without it a restart loses
|
|
489
|
+
// the pinned session (nothing to restore) and every catalog reports Remote
|
|
490
|
+
// disabled because no state ever reaches getRemoteSessionState().
|
|
491
|
+
remoteIntentPath: remoteIntentPath(RUNTIME_ROOT),
|
|
492
|
+
onRemoteStateChange: (state) => {
|
|
493
|
+
remoteSessionState = {
|
|
494
|
+
enabled: state?.enabled === true,
|
|
495
|
+
sessionId: state?.sessionId ?? null,
|
|
496
|
+
cwd: state?.cwd ?? null,
|
|
497
|
+
daemonPid: state?.daemonPid ?? process.pid,
|
|
498
|
+
updatedAt: state?.updatedAt ?? Date.now(),
|
|
499
|
+
};
|
|
500
|
+
},
|
|
487
501
|
// Self-shutdown when the last attached TUI leaves (reuses the SSE/client
|
|
488
502
|
// registry as the liveness signal).
|
|
489
503
|
onClientsEmpty: () => { maybeSelfShutdown('no live channel clients'); },
|
|
@@ -566,7 +580,11 @@ async function main() {
|
|
|
566
580
|
};
|
|
567
581
|
let sessionRuntimePrewarmStarted = false;
|
|
568
582
|
let sessionRuntimePrewarmPromise = null;
|
|
569
|
-
sessionRuntimeHost = createSessionRuntimeHost({
|
|
583
|
+
sessionRuntimeHost = createSessionRuntimeHost({
|
|
584
|
+
cwd: CWD,
|
|
585
|
+
log,
|
|
586
|
+
canRecycle: () => (sessionService?.busyCount ?? 1) === 0,
|
|
587
|
+
});
|
|
570
588
|
function prewarmSessionRuntime() {
|
|
571
589
|
if (sessionRuntimePrewarmPromise) return sessionRuntimePrewarmPromise;
|
|
572
590
|
sessionRuntimePrewarmStarted = true;
|
|
@@ -575,7 +593,7 @@ async function main() {
|
|
|
575
593
|
.catch((error) => log(`image pipeline prewarm failed (non-fatal): ${error?.message || error}`));
|
|
576
594
|
sessionRuntimePrewarmPromise = sessionRuntimeHost.prewarm()
|
|
577
595
|
.then(() => {
|
|
578
|
-
log('session runtime worker/agent-loop/keychain/
|
|
596
|
+
log('session runtime worker/agent-loop/keychain/memory prewarm ready');
|
|
579
597
|
return true;
|
|
580
598
|
})
|
|
581
599
|
.catch((error) => {
|
|
@@ -712,6 +730,17 @@ async function main() {
|
|
|
712
730
|
// client — see the transport's onClientRegistered hook.
|
|
713
731
|
if (process.env.MIXDOG_DAEMON_SPAWNED_FOR !== 'session') startChannels();
|
|
714
732
|
|
|
733
|
+
// A pinned channel session outlives the daemon that pinned it. Reactivate the
|
|
734
|
+
// durable intent AFTER the ready handshake (activation itself brings the
|
|
735
|
+
// messaging bridge up); with no intent on disk this is a no-op and the
|
|
736
|
+
// channels graph stays dormant.
|
|
737
|
+
if (transport.remoteIntentSessionId) {
|
|
738
|
+
void transport.restoreRemoteIntent()
|
|
739
|
+
.then((restored) => {
|
|
740
|
+
if (!restored) log('remote intent restore declined (session unavailable)');
|
|
741
|
+
})
|
|
742
|
+
.catch((error) => log(`remote intent restore failed: ${error?.message || error}`));
|
|
743
|
+
}
|
|
715
744
|
}
|
|
716
745
|
|
|
717
746
|
process.on('SIGTERM', () => { void shutdown('SIGTERM'); });
|
|
@@ -116,16 +116,32 @@ export function hookConfigEntries(dataDir, cwd) {
|
|
|
116
116
|
]);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
/** Classify a hooks file and name the malformed events inside it.
|
|
120
|
+
*
|
|
121
|
+
* One bad value under `hooks` used to demote the WHOLE file to legacy, which
|
|
122
|
+
* silently disabled every standard policy handler in it. A file with at least
|
|
123
|
+
* one well-formed event stays standard; the malformed keys are reported so the
|
|
124
|
+
* caller can surface them instead of dropping them in silence. */
|
|
125
|
+
export function standardConfigReport(parsed) {
|
|
126
|
+
const empty = { standard: false, validEvents: [], invalidEvents: [] };
|
|
127
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return empty;
|
|
121
128
|
const hooks = parsed.hooks;
|
|
122
|
-
if (!hooks || typeof hooks !== 'object' || Array.isArray(hooks)) return
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
if (!hooks || typeof hooks !== 'object' || Array.isArray(hooks)) return empty;
|
|
130
|
+
const entries = Object.entries(hooks);
|
|
131
|
+
if (entries.length === 0) return empty;
|
|
132
|
+
const validEvents = [];
|
|
133
|
+
const invalidEvents = [];
|
|
134
|
+
for (const [eventName, groups] of entries) {
|
|
135
|
+
const wellFormed = Array.isArray(groups)
|
|
136
|
+
&& groups.every((g) => g && typeof g === 'object' && Array.isArray(g.hooks));
|
|
137
|
+
if (wellFormed) validEvents.push(eventName);
|
|
138
|
+
else invalidEvents.push(eventName);
|
|
139
|
+
}
|
|
140
|
+
return { standard: validEvents.length > 0, validEvents, invalidEvents };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function isStandardConfig(parsed) {
|
|
144
|
+
return standardConfigReport(parsed).standard;
|
|
129
145
|
}
|
|
130
146
|
|
|
131
147
|
const SIMPLE_MATCHER_RE = /^[A-Za-z0-9_ ,|]*$/;
|
|
@@ -158,7 +158,7 @@ function hookEnv(projectDir, pluginData, payload, pluginRoot = null) {
|
|
|
158
158
|
return env;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
export function runCommandHandler(handler, payload, eventName, pluginData, onSpawnError = null
|
|
161
|
+
export function runCommandHandler(handler, payload, eventName, pluginData, onSpawnError = null) {
|
|
162
162
|
const projectDir = payload.cwd || process.cwd();
|
|
163
163
|
const effectivePluginData = handler._pluginData || pluginData || null;
|
|
164
164
|
const stdin = JSON.stringify(payload);
|
|
@@ -171,55 +171,6 @@ export function runCommandHandler(handler, payload, eventName, pluginData, onSpa
|
|
|
171
171
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
-
if (handler.asyncRewake === true) {
|
|
175
|
-
try {
|
|
176
|
-
const child = spawn(spec.command, spec.args, withProcessGroup(baseOpts));
|
|
177
|
-
let bgStdout = '';
|
|
178
|
-
let bgStderr = '';
|
|
179
|
-
let bgStdoutBytes = 0;
|
|
180
|
-
let bgStderrBytes = 0;
|
|
181
|
-
let killed = false;
|
|
182
|
-
child.stdout?.on('data', (chunk) => {
|
|
183
|
-
bgStdoutBytes += chunk.length;
|
|
184
|
-
if (bgStdoutBytes <= MAX_BUFFER_BYTES) bgStdout += chunk.toString('utf8');
|
|
185
|
-
});
|
|
186
|
-
child.stderr?.on('data', (chunk) => {
|
|
187
|
-
bgStderrBytes += chunk.length;
|
|
188
|
-
if (bgStderrBytes <= MAX_BUFFER_BYTES) bgStderr += chunk.toString('utf8');
|
|
189
|
-
});
|
|
190
|
-
const killTimer = setTimeout(() => {
|
|
191
|
-
killed = true;
|
|
192
|
-
try { child.stdout?.removeAllListeners('data'); } catch {}
|
|
193
|
-
try { child.stderr?.removeAllListeners('data'); } catch {}
|
|
194
|
-
terminateTree(child);
|
|
195
|
-
}, timeoutMs);
|
|
196
|
-
killTimer.unref?.();
|
|
197
|
-
child.on('error', (error) => {
|
|
198
|
-
clearTimeout(killTimer);
|
|
199
|
-
if (typeof onSpawnError === 'function') onSpawnError(error);
|
|
200
|
-
});
|
|
201
|
-
child.on('close', (code) => {
|
|
202
|
-
clearTimeout(killTimer);
|
|
203
|
-
try {
|
|
204
|
-
if (!killed && code === 2 && typeof onRewake === 'function') {
|
|
205
|
-
const text = (bgStderr || '').trim() || (bgStdout || '').trim();
|
|
206
|
-
onRewake({ eventName, payload, text });
|
|
207
|
-
}
|
|
208
|
-
} catch {}
|
|
209
|
-
});
|
|
210
|
-
child.stdin?.end(stdin);
|
|
211
|
-
return Promise.resolve({ exitCode: 0, stdout: '', stderr: '', async: true });
|
|
212
|
-
} catch (error) {
|
|
213
|
-
return Promise.resolve({
|
|
214
|
-
exitCode: -1,
|
|
215
|
-
stdout: '',
|
|
216
|
-
stderr: error?.message || String(error),
|
|
217
|
-
timedOut: false,
|
|
218
|
-
spawnError: error,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
174
|
if (handler.async === true) {
|
|
224
175
|
try {
|
|
225
176
|
const child = spawn(spec.command, spec.args, withProcessGroup({
|
|
@@ -448,12 +399,25 @@ export async function runMcpToolHandler(handler, payload, eventName, mcpToolRunn
|
|
|
448
399
|
return { exitCode: -1, stdout: '', stderr: 'mcp_tool handler missing tool name', timedOut: false, spawnError: null };
|
|
449
400
|
}
|
|
450
401
|
let timer = null;
|
|
402
|
+
// Losing the race is not cancellation: without this signal the tool call kept
|
|
403
|
+
// running (and holding its MCP slot) long after the hook gave up on it.
|
|
404
|
+
const controller = new AbortController();
|
|
451
405
|
try {
|
|
452
|
-
const runPromise = Promise.resolve(mcpToolRunner({
|
|
406
|
+
const runPromise = Promise.resolve(mcpToolRunner({
|
|
407
|
+
name,
|
|
408
|
+
args: payload,
|
|
409
|
+
signal: controller.signal,
|
|
410
|
+
timeoutMs,
|
|
411
|
+
}));
|
|
412
|
+
// The abandoned call still settles somewhere; keep its rejection handled.
|
|
413
|
+
runPromise.catch(() => {});
|
|
453
414
|
const text = await Promise.race([
|
|
454
415
|
runPromise,
|
|
455
416
|
new Promise((_r, reject) => {
|
|
456
|
-
timer = setTimeout(() =>
|
|
417
|
+
timer = setTimeout(() => {
|
|
418
|
+
try { controller.abort(new Error(`mcp_tool hook timed out: ${name}`)); } catch {}
|
|
419
|
+
reject(new Error(`mcp_tool hook timed out: ${name}`));
|
|
420
|
+
}, timeoutMs);
|
|
457
421
|
// No unref: this timer must keep the event loop alive so the race can
|
|
458
422
|
// settle even when the runner promise never resolves. Cleared in finally.
|
|
459
423
|
}),
|
|
@@ -6,28 +6,25 @@ import {
|
|
|
6
6
|
SUPPORTED_HANDLER_TYPES,
|
|
7
7
|
EXIT2_BLOCK_EVENTS,
|
|
8
8
|
TOP_LEVEL_DECISION_EVENTS,
|
|
9
|
-
limitText,
|
|
10
9
|
} from './hook-bus/constants.mjs';
|
|
11
10
|
import {
|
|
12
11
|
buildEventPayload,
|
|
13
12
|
hookConfigEntries,
|
|
14
13
|
hookRulesPath,
|
|
15
|
-
isStandardConfig,
|
|
16
14
|
matchFieldFor,
|
|
17
15
|
matcherFires,
|
|
18
16
|
mergeEvents,
|
|
19
17
|
normalizeRules,
|
|
20
18
|
parseStandardConfig,
|
|
19
|
+
standardConfigReport,
|
|
21
20
|
} from './hook-bus/config.mjs';
|
|
22
21
|
import {
|
|
23
|
-
handlerTimeoutS,
|
|
24
22
|
ifConditionPasses,
|
|
25
23
|
parseHandlerOutput,
|
|
26
24
|
runCommandHandler,
|
|
27
25
|
runHttpHandler,
|
|
28
26
|
runMcpToolHandler,
|
|
29
27
|
runPromptHandler,
|
|
30
|
-
defaultShellKind,
|
|
31
28
|
} from './hook-bus/handlers.mjs';
|
|
32
29
|
import {
|
|
33
30
|
compactValue,
|
|
@@ -57,6 +54,7 @@ export {
|
|
|
57
54
|
mergeEvents,
|
|
58
55
|
normalizeRules,
|
|
59
56
|
parseStandardConfig,
|
|
57
|
+
standardConfigReport,
|
|
60
58
|
} from './hook-bus/config.mjs';
|
|
61
59
|
export {
|
|
62
60
|
handlerTimeoutS,
|
|
@@ -84,7 +82,6 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null, prompt
|
|
|
84
82
|
const counts = new Map(DEFAULT_EVENTS.map((name) => [name, 0]));
|
|
85
83
|
const rulesPath = hookRulesPath(dataDir);
|
|
86
84
|
const pluginData = dataDir || null;
|
|
87
|
-
let rewakeHandler = null;
|
|
88
85
|
let rulesCache = { mtimeMs: -1, rules: [] };
|
|
89
86
|
let configCache = {
|
|
90
87
|
key: '',
|
|
@@ -163,10 +160,27 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null, prompt
|
|
|
163
160
|
disabled = parsed.disableAllHooks === true;
|
|
164
161
|
disableSeen = true;
|
|
165
162
|
}
|
|
166
|
-
|
|
163
|
+
const report = standardConfigReport(parsed);
|
|
164
|
+
if (report.standard) {
|
|
165
|
+
// A malformed event no longer demotes the whole file to legacy (which
|
|
166
|
+
// silently disabled every standard handler in it): keep the valid
|
|
167
|
+
// events and report the broken ones.
|
|
168
|
+
if (report.invalidEvents.length > 0) {
|
|
169
|
+
errors.push({
|
|
170
|
+
file: filePath,
|
|
171
|
+
error: `ignored malformed hook event(s): ${report.invalidEvents.join(', ')}`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
167
174
|
mergeEvents(events, parseStandardConfig(parsed, filePath, entry));
|
|
168
175
|
} else {
|
|
169
|
-
|
|
176
|
+
const fileRules = normalizeRules(parsed).filter((rule) => rule && typeof rule === 'object');
|
|
177
|
+
if (fileRules.length === 0 && report.invalidEvents.length > 0) {
|
|
178
|
+
errors.push({
|
|
179
|
+
file: filePath,
|
|
180
|
+
error: `no usable hooks: malformed event(s) under "hooks": ${report.invalidEvents.join(', ')}`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
legacyRules.push(...fileRules);
|
|
170
184
|
}
|
|
171
185
|
}
|
|
172
186
|
configCache = {
|
|
@@ -230,16 +244,7 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null, prompt
|
|
|
230
244
|
error: `hook spawn failed: ${error?.message || error}`,
|
|
231
245
|
});
|
|
232
246
|
};
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
if (typeof rewakeHandler === 'function') {
|
|
236
|
-
rewakeHandler({ eventName: en, payload: pl, text });
|
|
237
|
-
} else {
|
|
238
|
-
emit('hook:rewake', { name: pl?.tool_name || en, text: text || null });
|
|
239
|
-
}
|
|
240
|
-
} catch {}
|
|
241
|
-
};
|
|
242
|
-
return await runCommandHandler(handler, payload, eventName, pluginData, reportSpawnError, reportRewake);
|
|
247
|
+
return await runCommandHandler(handler, payload, eventName, pluginData, reportSpawnError);
|
|
243
248
|
}
|
|
244
249
|
if (type === 'http') {
|
|
245
250
|
if (!handler.url) return null;
|
|
@@ -334,6 +339,8 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null, prompt
|
|
|
334
339
|
}
|
|
335
340
|
|
|
336
341
|
function addRule(rule = {}) {
|
|
342
|
+
// Same index-safety rule as deleteRule: settle debounced toggles first.
|
|
343
|
+
flushRules();
|
|
337
344
|
const action = String(rule.action || rule.decision || '').trim().toLowerCase();
|
|
338
345
|
if (!action || !['allow', 'deny', 'block', 'modify', 'rewrite', 'ask'].includes(action)) {
|
|
339
346
|
throw new Error('hook rule action must be allow, deny, block, modify, rewrite, or ask');
|
|
@@ -428,6 +435,10 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null, prompt
|
|
|
428
435
|
}
|
|
429
436
|
|
|
430
437
|
function deleteRule(index) {
|
|
438
|
+
// Pending enabled-state patches are addressed by INDEX. Flushing them
|
|
439
|
+
// before the splice keeps a debounced toggle from landing on whatever rule
|
|
440
|
+
// shifted into that slot after the delete.
|
|
441
|
+
flushRules();
|
|
431
442
|
const rules = [...loadRules()];
|
|
432
443
|
if (!Number.isInteger(index) || index < 0 || index >= rules.length) throw new Error(`hook rule not found: ${index}`);
|
|
433
444
|
rules.splice(index, 1);
|
|
@@ -564,10 +575,5 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null, prompt
|
|
|
564
575
|
};
|
|
565
576
|
}
|
|
566
577
|
|
|
567
|
-
|
|
568
|
-
rewakeHandler = typeof fn === 'function' ? fn : null;
|
|
569
|
-
return rewakeHandler;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
return { addRule, beforeTool, deleteRule, dispatch, emit, flushRules, listRules, setRewakeHandler, setRuleEnabled, status };
|
|
578
|
+
return { addRule, beforeTool, deleteRule, dispatch, emit, flushRules, listRules, setRuleEnabled, status };
|
|
573
579
|
}
|
|
@@ -6,6 +6,7 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
import { claimSingletonOwner, handoffSingletonOwner, readSingletonOwner, releaseSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
|
|
8
8
|
import { readLiveServiceAdvert } from '../runtime/shared/service-discovery.mjs';
|
|
9
|
+
import { isPidAlive, parsePid } from '../runtime/shared/pid-liveness.mjs';
|
|
9
10
|
import { resolveRuntimeRoot } from '../runtime/shared/runtime-root.mjs';
|
|
10
11
|
import { sleep as delay } from '../runtime/shared/sleep.mjs';
|
|
11
12
|
import { scrubLoaderVars } from '../runtime/agent/orchestrator/tools/env-scrub.mjs';
|
|
@@ -40,22 +41,6 @@ function parsePort(value) {
|
|
|
40
41
|
return Number.isInteger(port) && port > 0 && port < 65536 ? port : null;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function parsePid(value) {
|
|
44
|
-
const pid = Number(value);
|
|
45
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function isPidAlive(pid) {
|
|
49
|
-
const n = parsePid(pid);
|
|
50
|
-
if (!n) return false;
|
|
51
|
-
try {
|
|
52
|
-
process.kill(n, 0);
|
|
53
|
-
return true;
|
|
54
|
-
} catch (error) {
|
|
55
|
-
return error?.code === 'EPERM';
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
44
|
const TRANSIENT_MEMORY_RPC_BACKOFF_MS = 400;
|
|
60
45
|
// A child that dies during startup with one of these signatures is a hard,
|
|
61
46
|
// deterministic failure (bad entry path, syntax/require error) — never a
|