mixdog 0.7.11 → 0.7.12
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/.claude-plugin/marketplace.json +5 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +193 -249
- package/bin/statusline-launcher.mjs +5 -1
- package/bin/statusline-lib.mjs +14 -6
- package/bin/statusline.mjs +14 -6
- package/hooks/lib/settings-loader.cjs +4 -3
- package/hooks/pre-tool-subagent.cjs +7 -2
- package/hooks/session-start.cjs +52 -24
- package/lib/mixdog-debug.cjs +163 -0
- package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
- package/package.json +1 -1
- package/scripts/builtin-utils-smoke.mjs +14 -8
- package/scripts/bump.mjs +80 -0
- package/scripts/doctor.mjs +8 -3
- package/scripts/mutation-io-smoke.mjs +17 -1
- package/scripts/permission-eval-smoke.mjs +18 -1
- package/scripts/statusline-launcher-smoke.mjs +2 -2
- package/scripts/webhook-selfheal-smoke.mjs +1 -3
- package/server-main.mjs +57 -3
- package/setup/install.mjs +574 -574
- package/setup/setup-server.mjs +10 -2
- package/setup/setup.html +43 -8
- package/src/agent/orchestrator/providers/openai-oauth.mjs +9 -2
- package/src/agent/orchestrator/providers/openai-ws.mjs +23 -0
- package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +29 -8
- package/src/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/channels/index.mjs +27 -8
- package/src/channels/lib/event-queue.mjs +24 -1
- package/src/channels/lib/hook-pipe-server.mjs +21 -8
- package/src/channels/lib/webhook.mjs +142 -20
- package/src/memory/lib/memory-cycle1.mjs +7 -3
- package/src/memory/lib/memory-recall-store.mjs +27 -10
- package/src/search/lib/backends/openai-oauth.mjs +6 -2
- package/src/search/lib/cache.mjs +55 -7
- package/scripts/test-config-rmw-restore.mjs +0 -122
|
@@ -70,7 +70,23 @@ async function warmRead(path, scope, offset = 0, limit = 20) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
async function rmTree(path) {
|
|
73
|
-
|
|
73
|
+
// Windows CI occasionally keeps a just-used temp file locked briefly after the
|
|
74
|
+
// owning JS/native subprocess exits (EACCES/EBUSY/EPERM). Cleanup is not part
|
|
75
|
+
// of the smoke invariant, so retry with backoff and make final teardown
|
|
76
|
+
// best-effort instead of turning a passed mutation suite red.
|
|
77
|
+
const delays = [50, 100, 200, 400, 800];
|
|
78
|
+
let lastErr = null;
|
|
79
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
80
|
+
try {
|
|
81
|
+
await rm(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
82
|
+
return;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
lastErr = err;
|
|
85
|
+
if (!['EACCES', 'EBUSY', 'EPERM', 'ENOTEMPTY'].includes(err?.code)) break;
|
|
86
|
+
if (i < delays.length) await new Promise((resolve) => setTimeout(resolve, delays[i]));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
console.warn(`mutation-io smoke cleanup warning: failed to remove ${path}: ${lastErr?.code || ''} ${lastErr?.message || lastErr}`);
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
try {
|
|
@@ -10,6 +10,23 @@ import path from 'path';
|
|
|
10
10
|
import { createRequire } from 'module';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
|
|
13
|
+
// The evaluator intentionally merges user-global Claude settings. Local dev
|
|
14
|
+
// machines commonly have defaultMode=bypassPermissions, which made this smoke
|
|
15
|
+
// pass locally while CI's empty user config exercised stricter defaults. Pin
|
|
16
|
+
// the user tier to a throwaway Claude config dir so the smoke is hermetic and
|
|
17
|
+
// still verifies the intended invariant: hard-deny and explicit list rules win
|
|
18
|
+
// before bypass mode.
|
|
19
|
+
const smokeHome = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'perm-smoke-home-')));
|
|
20
|
+
const smokeConfigDir = path.join(smokeHome, '.claude');
|
|
21
|
+
fs.mkdirSync(smokeConfigDir, { recursive: true });
|
|
22
|
+
fs.writeFileSync(
|
|
23
|
+
path.join(smokeConfigDir, 'settings.json'),
|
|
24
|
+
JSON.stringify({ permissions: { defaultMode: 'bypassPermissions' } }),
|
|
25
|
+
);
|
|
26
|
+
process.env.HOME = smokeHome;
|
|
27
|
+
process.env.USERPROFILE = smokeHome;
|
|
28
|
+
process.env.CLAUDE_CONFIG_DIR = smokeConfigDir;
|
|
29
|
+
|
|
13
30
|
const _require = createRequire(import.meta.url);
|
|
14
31
|
const evalPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../hooks/lib/permission-evaluator.cjs');
|
|
15
32
|
const { evaluatePermission } = _require(evalPath);
|
|
@@ -21,7 +38,7 @@ const { loadPermissions, clearSettingsCache } = _require(loaderPath);
|
|
|
21
38
|
let pass = 0, fail = 0;
|
|
22
39
|
|
|
23
40
|
function makeProject(settings = {}) {
|
|
24
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'perm-smoke-'));
|
|
41
|
+
const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'perm-smoke-')));
|
|
25
42
|
fs.mkdirSync(path.join(dir, '.claude'), { recursive: true });
|
|
26
43
|
if (Object.keys(settings).length) {
|
|
27
44
|
fs.writeFileSync(
|
|
@@ -27,7 +27,7 @@ const tmp = mkdtempSync(join(tmpdir(), 'mixdog-sl-launcher-'));
|
|
|
27
27
|
function run(homeDir) {
|
|
28
28
|
return spawnSync(process.execPath, [LAUNCHER], {
|
|
29
29
|
input: SAMPLE,
|
|
30
|
-
env: { ...process.env, HOME: homeDir, USERPROFILE: homeDir },
|
|
30
|
+
env: { ...process.env, HOME: homeDir, USERPROFILE: homeDir, CLAUDE_CONFIG_DIR: join(homeDir, '.claude') },
|
|
31
31
|
encoding: 'utf8',
|
|
32
32
|
});
|
|
33
33
|
}
|
|
@@ -60,7 +60,7 @@ try {
|
|
|
60
60
|
+ "process.stdout.write(await renderStatusLine(i));\n"
|
|
61
61
|
);
|
|
62
62
|
const directRun = spawnSync(process.execPath, [driver],
|
|
63
|
-
{ input: SAMPLE, env: { ...process.env, HOME: tmp, USERPROFILE: tmp }, encoding: 'utf8' });
|
|
63
|
+
{ input: SAMPLE, env: { ...process.env, HOME: tmp, USERPROFILE: tmp, CLAUDE_CONFIG_DIR: join(tmp, '.claude') }, encoding: 'utf8' });
|
|
64
64
|
const direct = directRun.stdout || '';
|
|
65
65
|
|
|
66
66
|
const ok = run(tmp);
|
|
@@ -5,9 +5,7 @@
|
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
process.env.CLAUDE_PLUGIN_DATA = join(tmpdir(), `mixdog-webhook-selfheal-smoke-${process.pid}`);
|
|
10
|
-
}
|
|
8
|
+
process.env.CLAUDE_PLUGIN_DATA = join(tmpdir(), `mixdog-webhook-selfheal-smoke-${process.pid}`);
|
|
11
9
|
|
|
12
10
|
const { decidePortReclaimAction } = await import('../src/channels/lib/webhook.mjs');
|
|
13
11
|
|
package/server-main.mjs
CHANGED
|
@@ -1040,10 +1040,62 @@ async function _getBridgeLlmFactory() {
|
|
|
1040
1040
|
// completion after the worker stopped waiting for the result.
|
|
1041
1041
|
const AGENT_IPC_MAX_CONCURRENT = 2
|
|
1042
1042
|
const _agentIpcInflight = new Map()
|
|
1043
|
-
/** @type {Array<{ msg: object, worker: string, proc: import('child_process').ChildProcess }>} */
|
|
1043
|
+
/** @type {Array<{ msg: object, worker: string, proc: import('child_process').ChildProcess, lane?: string }>} */
|
|
1044
1044
|
const _agentIpcQueue = []
|
|
1045
1045
|
let _agentIpcRunning = 0
|
|
1046
1046
|
|
|
1047
|
+
const AGENT_IPC_LANE_USER = 'user'
|
|
1048
|
+
const AGENT_IPC_LANE_MAINTENANCE = 'maintenance'
|
|
1049
|
+
/** @type {Set<string>} */
|
|
1050
|
+
const AGENT_IPC_MAINTENANCE_WORKERS = new Set(['memory'])
|
|
1051
|
+
|
|
1052
|
+
function _normalizeAgentIpcLaneToken(v) {
|
|
1053
|
+
return typeof v === 'string' ? v.trim().toLowerCase() : ''
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
function _isMaintenanceBridgeRole(role) {
|
|
1057
|
+
const r = _normalizeAgentIpcLaneToken(role)
|
|
1058
|
+
if (!r) return false
|
|
1059
|
+
if (r === 'maintenance' || r === 'scheduler-task' || r === 'webhook-handler' || r === 'memory-classification') {
|
|
1060
|
+
return true
|
|
1061
|
+
}
|
|
1062
|
+
if (r.startsWith('cycle')) return true
|
|
1063
|
+
return false
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/** @returns {'user'|'maintenance'} */
|
|
1067
|
+
function _classifyAgentIpcJobLane(msg, worker) {
|
|
1068
|
+
const lane = _normalizeAgentIpcLaneToken(msg?.lane)
|
|
1069
|
+
if (lane === AGENT_IPC_LANE_MAINTENANCE || lane === 'maint') return AGENT_IPC_LANE_MAINTENANCE
|
|
1070
|
+
if (lane === AGENT_IPC_LANE_USER) return AGENT_IPC_LANE_USER
|
|
1071
|
+
|
|
1072
|
+
const priority = _normalizeAgentIpcLaneToken(msg?.priority)
|
|
1073
|
+
if (priority === AGENT_IPC_LANE_MAINTENANCE || priority === 'maint') return AGENT_IPC_LANE_MAINTENANCE
|
|
1074
|
+
if (priority === AGENT_IPC_LANE_USER) return AGENT_IPC_LANE_USER
|
|
1075
|
+
|
|
1076
|
+
if (msg?.maintenance === true || msg?.isMaintenance === true) return AGENT_IPC_LANE_MAINTENANCE
|
|
1077
|
+
|
|
1078
|
+
const params = msg?.params && typeof msg.params === 'object' ? msg.params : {}
|
|
1079
|
+
if (_isMaintenanceBridgeRole(params.role)) return AGENT_IPC_LANE_MAINTENANCE
|
|
1080
|
+
|
|
1081
|
+
const w = _normalizeAgentIpcLaneToken(worker)
|
|
1082
|
+
if (AGENT_IPC_MAINTENANCE_WORKERS.has(w)) return AGENT_IPC_LANE_MAINTENANCE
|
|
1083
|
+
|
|
1084
|
+
return AGENT_IPC_LANE_USER
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function _dequeueHighestPriorityAgentIpcJob() {
|
|
1088
|
+
if (_agentIpcQueue.length === 0) return null
|
|
1089
|
+
for (let i = 0; i < _agentIpcQueue.length; i++) {
|
|
1090
|
+
const job = _agentIpcQueue[i]
|
|
1091
|
+
const lane = job.lane || _classifyAgentIpcJobLane(job.msg, job.worker)
|
|
1092
|
+
if (lane !== AGENT_IPC_LANE_MAINTENANCE) {
|
|
1093
|
+
return _agentIpcQueue.splice(i, 1)[0]
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return _agentIpcQueue.shift()
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1047
1099
|
function _sendAgentIpcResponse(proc, callId, body) {
|
|
1048
1100
|
try { proc.send({ type: 'agent_ipc_response', callId, ...body }) } catch {}
|
|
1049
1101
|
}
|
|
@@ -1066,14 +1118,16 @@ function _startAgentIpcJob(job) {
|
|
|
1066
1118
|
|
|
1067
1119
|
function _drainAgentIpcQueue() {
|
|
1068
1120
|
while (_agentIpcRunning < AGENT_IPC_MAX_CONCURRENT && _agentIpcQueue.length > 0) {
|
|
1069
|
-
const job =
|
|
1121
|
+
const job = _dequeueHighestPriorityAgentIpcJob()
|
|
1122
|
+
if (!job) break
|
|
1070
1123
|
_agentIpcRunning++
|
|
1071
1124
|
_startAgentIpcJob(job)
|
|
1072
1125
|
}
|
|
1073
1126
|
}
|
|
1074
1127
|
|
|
1075
1128
|
function _enqueueAgentIpcRequest(msg, worker, proc) {
|
|
1076
|
-
|
|
1129
|
+
const lane = _classifyAgentIpcJobLane(msg, worker)
|
|
1130
|
+
_agentIpcQueue.push({ msg, worker, proc, lane })
|
|
1077
1131
|
_drainAgentIpcQueue()
|
|
1078
1132
|
}
|
|
1079
1133
|
|