sneakoscope 4.1.1 → 4.2.1
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 -10
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +1 -1
- package/dist/core/auto-review.js +1 -1
- package/dist/core/codex-control/codex-app-server-v2-client.js +86 -2
- package/dist/core/codex-control/codex-reliability-shield.js +26 -5
- package/dist/core/codex-control/codex-task-runner.js +7 -1
- package/dist/core/codex-control/model-call-concurrency.js +1 -1
- package/dist/core/commands/mad-db-command.js +146 -51
- package/dist/core/commands/mad-sks-command.js +15 -31
- package/dist/core/commands/qa-loop-command.js +23 -7
- package/dist/core/db-safety.js +35 -37
- package/dist/core/doctor/supabase-mcp-repair.js +2 -2
- package/dist/core/feature-registry.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +1 -1
- package/dist/core/init.js +5 -4
- package/dist/core/mad-db/mad-db-capability.js +203 -74
- package/dist/core/mad-db/mad-db-coordinator.js +287 -0
- package/dist/core/mad-db/mad-db-executor.js +156 -0
- package/dist/core/mad-db/mad-db-ledger.js +1 -1
- package/dist/core/mad-db/mad-db-lock.js +40 -0
- package/dist/core/mad-db/mad-db-operation-store.js +140 -0
- package/dist/core/mad-db/mad-db-policy-resolver.js +42 -22
- package/dist/core/mad-db/mad-db-policy.js +195 -0
- package/dist/core/mad-db/mad-db-postconditions.js +30 -0
- package/dist/core/mad-db/mad-db-recovery.js +27 -0
- package/dist/core/mad-db/mad-db-result-lifecycle.js +31 -102
- package/dist/core/mad-db/mad-db-runtime-profile.js +121 -0
- package/dist/core/mad-db/mad-db-target.js +64 -0
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
- package/dist/core/pipeline-internals/runtime-core.js +40 -0
- package/dist/core/providers/glm/bench/glm-benchmark-types.js +1 -1
- package/dist/core/qa-loop/qa-app-server-driver.js +134 -0
- package/dist/core/qa-loop/qa-contract-v2.js +231 -0
- package/dist/core/qa-loop/qa-gate-v2.js +132 -0
- package/dist/core/qa-loop/qa-runtime-artifacts.js +53 -0
- package/dist/core/qa-loop/qa-surface-router.js +114 -0
- package/dist/core/qa-loop/qa-types.js +18 -0
- package/dist/core/qa-loop.js +83 -26
- package/dist/core/release/gate-manifest.js +1 -0
- package/dist/core/release/release-gate-dag.js +6 -5
- package/dist/core/release/sla-scheduler.js +1 -1
- package/dist/core/routes.js +42 -12
- package/dist/core/triwiki/triwiki-affected-graph.js +3 -2
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-column-anchor.js +5 -1
- package/dist/scripts/check-dist-runtime.js +3 -2
- package/dist/scripts/codex-0142-manifest-check.js +2 -1
- package/dist/scripts/codex-control-all-pipelines-check.js +1 -0
- package/dist/scripts/codex-control-model-capacity-fallback-check.js +53 -0
- package/dist/scripts/config-managed-merge-callsite-coverage-check.js +7 -1
- package/dist/scripts/loop-directive-check-lib.js +78 -1
- package/dist/scripts/mad-db-capability-check.js +13 -2
- package/dist/scripts/mad-db-command-check.js +7 -5
- package/dist/scripts/mad-db-hook-idempotency-check.js +21 -0
- package/dist/scripts/mad-db-ledger-check.js +2 -1
- package/dist/scripts/mad-db-lifecycle-hook-decision-check.js +5 -4
- package/dist/scripts/mad-db-mad-command-check.js +29 -16
- package/dist/scripts/mad-db-mcp-result-lifecycle-check.js +11 -10
- package/dist/scripts/mad-db-one-cycle-bounded-check.js +15 -18
- package/dist/scripts/mad-db-one-cycle-consumption-check.js +3 -3
- package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +9 -9
- package/dist/scripts/mad-db-operation-lifecycle-ledger-check.js +6 -6
- package/dist/scripts/mad-db-parallel-lifecycle-check.js +24 -0
- package/dist/scripts/mad-db-policy-v2-check.js +20 -0
- package/dist/scripts/mad-db-priority-resolver-check.js +5 -5
- package/dist/scripts/mad-db-real-supabase-e2e.js +166 -0
- package/dist/scripts/mad-db-route-identity-check.js +28 -0
- package/dist/scripts/mad-db-runtime-profile-lifecycle-check.js +24 -0
- package/dist/scripts/mad-db-safety-conflict-matrix-check.js +3 -3
- package/dist/scripts/mad-db-skill-policy-snapshot-check.js +15 -0
- package/dist/scripts/qa-loop-app-server-driver-check.js +74 -0
- package/dist/scripts/qa-loop-surface-router-check.js +49 -0
- package/dist/scripts/release-check-dynamic-execute.js +1 -1
- package/dist/scripts/release-dag-full-coverage-check.js +6 -0
- package/dist/scripts/release-triwiki-first-runner-blackbox.js +5 -1
- package/dist/scripts/runtime-ts-rust-boundary-check.js +1 -1
- package/dist/scripts/triwiki-affected-graph-check.js +2 -2
- package/package.json +18 -5
- package/schemas/mad-db/mad-db-capability.schema.json +92 -19
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { exists, nowIso, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
export async function quarantineStaleMadDbRuntimeProfiles(root) {
|
|
5
|
+
const missionsDir = path.join(root, '.sneakoscope', 'missions');
|
|
6
|
+
const quarantined = [];
|
|
7
|
+
if (!(await exists(missionsDir)))
|
|
8
|
+
return { schema: 'sks.mad-db-recovery.v1', ok: true, quarantined, checked_at: nowIso() };
|
|
9
|
+
const missions = await fs.readdir(missionsDir, { withFileTypes: true }).catch(() => []);
|
|
10
|
+
for (const mission of missions) {
|
|
11
|
+
if (!mission.isDirectory())
|
|
12
|
+
continue;
|
|
13
|
+
const runtime = path.join(missionsDir, mission.name, 'mad-db', 'runtime');
|
|
14
|
+
const profile = path.join(runtime, 'codex-mad-db.config.toml');
|
|
15
|
+
if (!(await exists(profile)))
|
|
16
|
+
continue;
|
|
17
|
+
const dest = `${profile}.quarantined-${Date.now()}`;
|
|
18
|
+
await fs.rename(profile, dest).catch(async () => {
|
|
19
|
+
await fs.rm(profile, { force: true }).catch(() => undefined);
|
|
20
|
+
});
|
|
21
|
+
quarantined.push(path.relative(root, dest).split(path.sep).join('/'));
|
|
22
|
+
}
|
|
23
|
+
const report = { schema: 'sks.mad-db-recovery.v1', ok: true, quarantined, checked_at: nowIso() };
|
|
24
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'mad-db-recovery.json'), report).catch(() => undefined);
|
|
25
|
+
return report;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=mad-db-recovery.js.map
|
|
@@ -1,92 +1,59 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const PENDING_FILE = 'mad-db-lifecycle-pending.jsonl';
|
|
7
|
-
const PENDING_LATEST_FILE = 'mad-db-lifecycle-pending.latest.json';
|
|
8
|
-
export async function recordPendingMadDbLifecycleHook(root, missionId, hook) {
|
|
9
|
-
const dir = missionDir(root, missionId);
|
|
10
|
-
const row = {
|
|
11
|
-
schema: 'sks.mad-db-lifecycle-pending.v1',
|
|
12
|
-
ts: nowIso(),
|
|
13
|
-
mission_id: missionId,
|
|
1
|
+
import { extractCanonicalToolCallId, transitionMadDbOperation } from './mad-db-operation-store.js';
|
|
2
|
+
export async function recordPendingMadDbLifecycleHook(_root, _missionId, hook) {
|
|
3
|
+
return {
|
|
4
|
+
schema: 'sks.mad-db-lifecycle-pending.v2',
|
|
5
|
+
pending_latest_removed: true,
|
|
14
6
|
hook
|
|
15
7
|
};
|
|
16
|
-
await appendJsonlBounded(path.join(dir, PENDING_FILE), row);
|
|
17
|
-
await writeJsonAtomic(path.join(dir, PENDING_LATEST_FILE), row).catch(() => undefined);
|
|
18
|
-
return row;
|
|
19
8
|
}
|
|
20
|
-
export async function readLatestPendingMadDbLifecycleHook(
|
|
21
|
-
|
|
22
|
-
const embedded = lifecycleHookFromUnknown(payload);
|
|
23
|
-
if (embedded)
|
|
24
|
-
return embedded;
|
|
25
|
-
const latest = await readJson(path.join(dir, PENDING_LATEST_FILE), null).catch(() => null);
|
|
26
|
-
const latestHook = lifecycleHookFromUnknown(latest?.hook);
|
|
27
|
-
if (latestHook && hookMatchesPayload(latestHook, payload))
|
|
28
|
-
return latestHook;
|
|
29
|
-
const text = await readText(path.join(dir, PENDING_FILE), '').catch(() => '');
|
|
30
|
-
const rows = String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean).reverse();
|
|
31
|
-
for (const line of rows.slice(0, 50)) {
|
|
32
|
-
try {
|
|
33
|
-
const row = JSON.parse(line);
|
|
34
|
-
const hook = lifecycleHookFromUnknown(row?.hook);
|
|
35
|
-
if (hook && hookMatchesPayload(hook, payload))
|
|
36
|
-
return hook;
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// Ignore malformed pending rows.
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
9
|
+
export async function readLatestPendingMadDbLifecycleHook(_root, _missionId, payload = {}) {
|
|
10
|
+
return lifecycleHookFromUnknown(payload);
|
|
43
11
|
}
|
|
44
12
|
export async function recordMadDbToolResult(input) {
|
|
45
|
-
|
|
46
|
-
if (await hasTerminalLifecycleEvent(input.root, input.missionId, input.hook.operation_id)) {
|
|
13
|
+
if (!input.hook.tool_call_id) {
|
|
47
14
|
return {
|
|
48
|
-
schema: 'sks.mad-db-tool-result-lifecycle.
|
|
49
|
-
ok:
|
|
15
|
+
schema: 'sks.mad-db-tool-result-lifecycle.v2',
|
|
16
|
+
ok: false,
|
|
50
17
|
skipped: true,
|
|
51
|
-
reason: '
|
|
18
|
+
reason: 'tool_call_id_required_for_result_correlation',
|
|
52
19
|
operation_id: input.hook.operation_id
|
|
53
20
|
};
|
|
54
21
|
}
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
destructive: input.hook.destructive === true,
|
|
63
|
-
resultStatus: input.ok ? 'succeeded' : 'failed',
|
|
64
|
-
rowCount: input.rowCount ?? null,
|
|
65
|
-
error: input.error || null
|
|
22
|
+
const operation = await transitionMadDbOperation({
|
|
23
|
+
root: input.root,
|
|
24
|
+
missionId: input.missionId,
|
|
25
|
+
toolCallId: input.hook.tool_call_id,
|
|
26
|
+
state: input.ok ? 'succeeded' : 'failed',
|
|
27
|
+
result: { ok: input.ok, row_count: input.rowCount ?? null },
|
|
28
|
+
errorCode: input.ok ? null : input.error || 'tool_failed'
|
|
66
29
|
});
|
|
67
|
-
await markPendingHookResolved(input.root, input.missionId, input.hook, input.ok);
|
|
68
30
|
return {
|
|
69
|
-
schema: 'sks.mad-db-tool-result-lifecycle.
|
|
70
|
-
ok:
|
|
31
|
+
schema: 'sks.mad-db-tool-result-lifecycle.v2',
|
|
32
|
+
ok: Boolean(operation),
|
|
71
33
|
skipped: false,
|
|
72
34
|
operation_id: input.hook.operation_id,
|
|
35
|
+
tool_call_id: input.hook.tool_call_id,
|
|
73
36
|
result_status: input.ok ? 'succeeded' : 'failed',
|
|
74
|
-
|
|
37
|
+
operation
|
|
75
38
|
};
|
|
76
39
|
}
|
|
77
40
|
export async function maybeRecordMadDbToolResultFromToolUse(input) {
|
|
78
41
|
const payload = input.toolResult ?? input.toolCallPayload ?? {};
|
|
79
42
|
const hook = lifecycleHookFromUnknown(input.decision)
|
|
80
43
|
|| lifecycleHookFromUnknown(input.toolCallPayload)
|
|
81
|
-
|| lifecycleHookFromUnknown(input.toolResult)
|
|
82
|
-
|
|
83
|
-
if (!hook)
|
|
44
|
+
|| lifecycleHookFromUnknown(input.toolResult);
|
|
45
|
+
const toolCallId = extractCanonicalToolCallId(payload) || hook?.tool_call_id || null;
|
|
46
|
+
if (!toolCallId && !hook)
|
|
84
47
|
return null;
|
|
85
48
|
const ok = !madDbToolUseFailed(payload);
|
|
86
49
|
return recordMadDbToolResult({
|
|
87
50
|
root: input.root,
|
|
88
51
|
missionId: input.missionId,
|
|
89
|
-
hook
|
|
52
|
+
hook: hook || {
|
|
53
|
+
mission_id: input.missionId,
|
|
54
|
+
operation_id: `unknown-${toolCallId}`,
|
|
55
|
+
tool_call_id: toolCallId
|
|
56
|
+
},
|
|
90
57
|
ok,
|
|
91
58
|
rowCount: extractRowCount(payload),
|
|
92
59
|
error: ok ? null : extractToolError(payload)
|
|
@@ -101,6 +68,7 @@ export function lifecycleHookFromUnknown(value) {
|
|
|
101
68
|
return {
|
|
102
69
|
mission_id: missionId,
|
|
103
70
|
operation_id: operationId,
|
|
71
|
+
tool_call_id: stringOrNull(candidate?.tool_call_id || candidate?.toolCallId),
|
|
104
72
|
cycle_id: stringOrNull(candidate?.cycle_id || candidate?.cycleId),
|
|
105
73
|
tool_name: stringOrNull(candidate?.tool_name || candidate?.toolName),
|
|
106
74
|
sql_hash: stringOrNull(candidate?.sql_hash || candidate?.sqlHash),
|
|
@@ -108,23 +76,6 @@ export function lifecycleHookFromUnknown(value) {
|
|
|
108
76
|
destructive: candidate?.destructive === true
|
|
109
77
|
};
|
|
110
78
|
}
|
|
111
|
-
function hookMatchesPayload(hook, payload) {
|
|
112
|
-
if (!hook.tool_name)
|
|
113
|
-
return true;
|
|
114
|
-
const toolText = [
|
|
115
|
-
payload.tool_name,
|
|
116
|
-
payload.toolName,
|
|
117
|
-
payload.name,
|
|
118
|
-
payload.tool?.name,
|
|
119
|
-
payload.server,
|
|
120
|
-
payload.mcp_tool,
|
|
121
|
-
payload.tool,
|
|
122
|
-
payload.type
|
|
123
|
-
].filter(Boolean).join(' ').toLowerCase();
|
|
124
|
-
if (!toolText)
|
|
125
|
-
return true;
|
|
126
|
-
return toolText.includes(String(hook.tool_name).toLowerCase()) || String(hook.tool_name).toLowerCase().includes(toolText);
|
|
127
|
-
}
|
|
128
79
|
function madDbToolUseFailed(payload = {}) {
|
|
129
80
|
if (payload?.isError === true || payload?.tool_response?.isError === true || payload?.toolResponse?.isError === true || payload?.result?.isError === true)
|
|
130
81
|
return true;
|
|
@@ -178,28 +129,6 @@ function extractToolError(payload = {}) {
|
|
|
178
129
|
}
|
|
179
130
|
return String(payload.error || payload.message || payload.stderr || payload.tool_response?.stderr || payload.toolResponse?.stderr || payload.result?.stderr || payload.result?.error || 'tool_failed');
|
|
180
131
|
}
|
|
181
|
-
async function hasTerminalLifecycleEvent(root, missionId, operationId) {
|
|
182
|
-
const ledger = path.join(missionDir(root, missionId), 'mad-db-ledger.jsonl');
|
|
183
|
-
const text = await readText(ledger, '').catch(() => '');
|
|
184
|
-
return String(text).split(/\r?\n/).some((line) => {
|
|
185
|
-
if (!line.includes(operationId))
|
|
186
|
-
return false;
|
|
187
|
-
return line.includes('db_operation.succeeded') || line.includes('db_operation.failed');
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
async function markPendingHookResolved(root, missionId, hook, ok) {
|
|
191
|
-
const dir = missionDir(root, missionId);
|
|
192
|
-
const row = {
|
|
193
|
-
schema: 'sks.mad-db-lifecycle-pending-resolution.v1',
|
|
194
|
-
ts: nowIso(),
|
|
195
|
-
mission_id: missionId,
|
|
196
|
-
operation_id: hook.operation_id,
|
|
197
|
-
cycle_id: hook.cycle_id || null,
|
|
198
|
-
result_status: ok ? 'succeeded' : 'failed'
|
|
199
|
-
};
|
|
200
|
-
await appendJsonlBounded(path.join(dir, 'mad-db-lifecycle-resolved.jsonl'), row).catch(() => undefined);
|
|
201
|
-
await fs.rm(path.join(dir, PENDING_LATEST_FILE), { force: true }).catch(() => undefined);
|
|
202
|
-
}
|
|
203
132
|
function stringOrNull(value) {
|
|
204
133
|
const text = String(value || '').trim();
|
|
205
134
|
return text ? text : null;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { exists, nowIso, readText, sha256, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
4
|
+
import { missionDir } from '../mission.js';
|
|
5
|
+
export async function createMadDbRuntimeProfile(input) {
|
|
6
|
+
const dir = path.join(missionDir(input.root, input.missionId), 'mad-db', 'runtime');
|
|
7
|
+
await fs.mkdir(dir, { recursive: true });
|
|
8
|
+
const url = madDbMcpUrl(input.projectRef);
|
|
9
|
+
const text = [
|
|
10
|
+
'[mcp_servers.supabase_mad_db]',
|
|
11
|
+
`url = "${url}"`,
|
|
12
|
+
'enabled = true',
|
|
13
|
+
''
|
|
14
|
+
].join('\n');
|
|
15
|
+
const profilePath = path.join(dir, 'codex-mad-db.config.toml');
|
|
16
|
+
await writeTextAtomic(profilePath, text);
|
|
17
|
+
const profileHash = sha256(text);
|
|
18
|
+
const normalHash = await normalCodexConfigHash(input.root);
|
|
19
|
+
const profile = {
|
|
20
|
+
schema: 'sks.mad-db-runtime-profile.v1',
|
|
21
|
+
mission_id: input.missionId,
|
|
22
|
+
cycle_id: input.cycleId,
|
|
23
|
+
runtime_session_id: input.runtimeSessionId,
|
|
24
|
+
project_ref_hash: sha256(input.projectRef).slice(0, 16),
|
|
25
|
+
profile_path: path.relative(input.root, profilePath).split(path.sep).join('/'),
|
|
26
|
+
profile_sha256: profileHash,
|
|
27
|
+
server_url_redacted: redactSupabaseUrl(url),
|
|
28
|
+
server_url: url,
|
|
29
|
+
features: ['database'],
|
|
30
|
+
write_capable: true,
|
|
31
|
+
normal_config_hash_before: normalHash,
|
|
32
|
+
created_at: nowIso()
|
|
33
|
+
};
|
|
34
|
+
await writeJsonAtomic(path.join(dir, 'runtime-profile-manifest.json'), redactedRuntimeProfile(profile));
|
|
35
|
+
return profile;
|
|
36
|
+
}
|
|
37
|
+
export async function closeMadDbRuntimeProfile(input) {
|
|
38
|
+
const profilePath = input.profile?.profile_path ? path.join(input.root, input.profile.profile_path) : path.join(missionDir(input.root, input.missionId), 'mad-db', 'runtime', 'codex-mad-db.config.toml');
|
|
39
|
+
if (await exists(profilePath)) {
|
|
40
|
+
const quarantine = `${profilePath}.closed`;
|
|
41
|
+
await fs.rename(profilePath, quarantine).catch(async () => {
|
|
42
|
+
await fs.rm(profilePath, { force: true }).catch(() => undefined);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const proof = await verifyReadOnlyRestored(input.root, input.profile?.normal_config_hash_before || null, profilePath);
|
|
46
|
+
await writeJsonAtomic(path.join(missionDir(input.root, input.missionId), 'mad-db', 'runtime', 'read-only-restoration.json'), {
|
|
47
|
+
...proof,
|
|
48
|
+
close_reason: input.reason || 'cycle_finally'
|
|
49
|
+
});
|
|
50
|
+
return proof;
|
|
51
|
+
}
|
|
52
|
+
export async function verifyReadOnlyRestored(root, normalConfigHashBefore, profilePath) {
|
|
53
|
+
const after = await normalCodexConfigHash(root);
|
|
54
|
+
const text = await readText(path.join(root, '.codex', 'config.toml'), '');
|
|
55
|
+
const persistentSupabaseReadOnly = persistentSupabaseConfigReadOnly(text);
|
|
56
|
+
const runtimeExists = profilePath ? await exists(profilePath) : false;
|
|
57
|
+
const blockers = [
|
|
58
|
+
...(normalConfigHashBefore && after && normalConfigHashBefore !== after ? ['normal_codex_config_hash_changed'] : []),
|
|
59
|
+
...(persistentSupabaseReadOnly ? [] : ['persistent_supabase_mcp_not_read_only']),
|
|
60
|
+
...(runtimeExists ? ['runtime_write_profile_still_exists'] : [])
|
|
61
|
+
];
|
|
62
|
+
return {
|
|
63
|
+
schema: 'sks.mad-db-read-only-restoration.v1',
|
|
64
|
+
checked_at: nowIso(),
|
|
65
|
+
ok: blockers.length === 0,
|
|
66
|
+
normal_config_hash_before: normalConfigHashBefore,
|
|
67
|
+
normal_config_hash_after: after,
|
|
68
|
+
persistent_supabase_read_only: persistentSupabaseReadOnly,
|
|
69
|
+
runtime_profile_exists: runtimeExists,
|
|
70
|
+
blockers
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function redactedRuntimeProfile(profile) {
|
|
74
|
+
const { server_url: _serverUrl, ...rest } = profile;
|
|
75
|
+
return rest;
|
|
76
|
+
}
|
|
77
|
+
export function madDbMcpUrl(projectRef) {
|
|
78
|
+
const params = new URLSearchParams();
|
|
79
|
+
params.set('project_ref', projectRef);
|
|
80
|
+
params.set('features', 'database');
|
|
81
|
+
return `https://mcp.supabase.com/mcp?${params.toString()}`;
|
|
82
|
+
}
|
|
83
|
+
export function redactSupabaseUrl(url) {
|
|
84
|
+
try {
|
|
85
|
+
const parsed = new URL(url);
|
|
86
|
+
const ref = parsed.searchParams.get('project_ref') || '';
|
|
87
|
+
if (ref)
|
|
88
|
+
parsed.searchParams.set('project_ref', `<hash:${sha256(ref).slice(0, 12)}>`);
|
|
89
|
+
for (const key of ['access_token', 'token', 'apikey', 'key', 'password']) {
|
|
90
|
+
if (parsed.searchParams.has(key))
|
|
91
|
+
parsed.searchParams.set(key, '<redacted>');
|
|
92
|
+
}
|
|
93
|
+
return parsed.toString();
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return '<redacted-invalid-url>';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function normalCodexConfigHash(root) {
|
|
100
|
+
const file = path.join(root, '.codex', 'config.toml');
|
|
101
|
+
if (!(await exists(file)))
|
|
102
|
+
return null;
|
|
103
|
+
return sha256(await readText(file, ''));
|
|
104
|
+
}
|
|
105
|
+
function persistentSupabaseConfigReadOnly(text) {
|
|
106
|
+
if (!/supabase|mcp\.supabase\.com/i.test(text))
|
|
107
|
+
return true;
|
|
108
|
+
const urls = [...String(text).matchAll(/https:\/\/mcp\.supabase\.com\/mcp[^"'\s)>,]*/gi)].map((match) => match[0] || '');
|
|
109
|
+
if (!urls.length)
|
|
110
|
+
return /read[_-]?only\s*=\s*true|access_mode\s*=\s*"read-only"|--read-only/.test(text);
|
|
111
|
+
return urls.every((url) => {
|
|
112
|
+
try {
|
|
113
|
+
const parsed = new URL(url);
|
|
114
|
+
return parsed.searchParams.get('read_only') === 'true';
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=mad-db-runtime-profile.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { exists, readText, sha256 } from '../fsx.js';
|
|
3
|
+
export async function resolveMadDbTarget(root, input = {}) {
|
|
4
|
+
const args = input.args || [];
|
|
5
|
+
const explicit = input.projectRef || readOption(args, '--project-ref', '') || process.env.SKS_MAD_DB_PROJECT_REF || process.env.SKS_MAD_DB_E2E_PROJECT_REF || '';
|
|
6
|
+
const candidates = explicit ? [explicit] : await projectRefCandidates(root);
|
|
7
|
+
const projectRef = explicit || (candidates.length === 1 ? candidates[0] || '' : '');
|
|
8
|
+
const target = normalizeTarget(input.target || readOption(args, '--target', '') || process.env.SKS_MAD_DB_TARGET || process.env.SKS_MAD_DB_E2E_TARGET || 'production');
|
|
9
|
+
const allowedSchemas = input.allowedSchemas?.length
|
|
10
|
+
? input.allowedSchemas
|
|
11
|
+
: splitCsv(readOption(args, '--schema', readOption(args, '--schemas', process.env.SKS_MAD_DB_SCHEMAS || 'public')));
|
|
12
|
+
const blockers = [];
|
|
13
|
+
if (!projectRef)
|
|
14
|
+
blockers.push(candidates.length > 1 ? 'mad_db_project_ref_ambiguous' : 'mad_db_project_ref_missing');
|
|
15
|
+
return {
|
|
16
|
+
schema: 'sks.mad-db-target.v1',
|
|
17
|
+
project_ref: projectRef || null,
|
|
18
|
+
project_ref_hash: projectRef ? sha256(projectRef).slice(0, 16) : null,
|
|
19
|
+
target_environment: target,
|
|
20
|
+
allowed_schemas: allowedSchemas.length ? allowedSchemas : ['public'],
|
|
21
|
+
source: explicit ? 'explicit_or_environment' : candidates.length === 1 ? 'managed_config_single_candidate' : 'unresolved',
|
|
22
|
+
blockers,
|
|
23
|
+
candidates: candidates.map((candidate) => `${sha256(candidate).slice(0, 8)}:${candidate.slice(0, 2)}...`)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export async function projectRootHash(root) {
|
|
27
|
+
return sha256(path.resolve(root)).slice(0, 24);
|
|
28
|
+
}
|
|
29
|
+
async function projectRefCandidates(root) {
|
|
30
|
+
const files = ['.codex/config.toml', '.mcp.json', 'mcp.json', '.cursor/mcp.json', '.vscode/mcp.json'];
|
|
31
|
+
const out = new Set();
|
|
32
|
+
for (const rel of files) {
|
|
33
|
+
const file = path.join(root, rel);
|
|
34
|
+
if (!(await exists(file)))
|
|
35
|
+
continue;
|
|
36
|
+
const text = await readText(file, '');
|
|
37
|
+
for (const match of String(text).matchAll(/project_ref=([a-z0-9_-]+)/gi))
|
|
38
|
+
out.add(match[1] || '');
|
|
39
|
+
for (const match of String(text).matchAll(/project_ref["']?\s*[:=]\s*["']([a-z0-9_-]+)["']/gi))
|
|
40
|
+
out.add(match[1] || '');
|
|
41
|
+
}
|
|
42
|
+
return [...out].filter(Boolean);
|
|
43
|
+
}
|
|
44
|
+
function normalizeTarget(value) {
|
|
45
|
+
const text = String(value || '').toLowerCase();
|
|
46
|
+
if (text === 'local')
|
|
47
|
+
return 'local';
|
|
48
|
+
if (text === 'branch')
|
|
49
|
+
return 'branch';
|
|
50
|
+
if (text === 'preview')
|
|
51
|
+
return 'preview';
|
|
52
|
+
return 'production';
|
|
53
|
+
}
|
|
54
|
+
function splitCsv(value) {
|
|
55
|
+
return String(value || '').split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
56
|
+
}
|
|
57
|
+
function readOption(args, name, fallback) {
|
|
58
|
+
const index = args.indexOf(name);
|
|
59
|
+
if (index >= 0 && args[index + 1] && !String(args[index + 1]).startsWith('--'))
|
|
60
|
+
return String(args[index + 1]);
|
|
61
|
+
const prefixed = args.find((arg) => String(arg).startsWith(`${name}=`));
|
|
62
|
+
return prefixed ? prefixed.slice(name.length + 1) : fallback;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=mad-db-target.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { REQUIRED_CODEX_MODEL } from '../codex-model-guard.js';
|
|
2
2
|
export const MANAGED_ASSET_SCHEMA_VERSION = 1;
|
|
3
|
-
export const MANAGED_ASSET_VERSION = '4.
|
|
3
|
+
export const MANAGED_ASSET_VERSION = '4.2.0';
|
|
4
4
|
export const MANAGED_ASSET_MARKER = 'SKS-MANAGED-ASSET';
|
|
5
5
|
export const MANAGED_AGENT_ROLES = Object.freeze([
|
|
6
6
|
role('sks-explorer', 'analysis-scout.toml', 'analysis_scout', 'Read-only SKS analysis scout retained for stale Codex agent-role config repair.', 'read-only', ['analysis-scout', 'analysis_scout', 'native_agent']),
|
|
@@ -22,6 +22,7 @@ import { SPEED_LANE_POLICY } from '../proof-field.js';
|
|
|
22
22
|
import { validateRouteCompletionProof } from '../proof/route-proof-gate.js';
|
|
23
23
|
import { routeFromState, routeRequiresCompletionProof } from '../proof/route-proof-policy.js';
|
|
24
24
|
import { permissionGateSummary } from '../permission-gates.js';
|
|
25
|
+
import { prepareMadDbMission } from '../mad-db/mad-db-coordinator.js';
|
|
25
26
|
import { AGENT_INTAKE_STAGE_ID, AGENT_COUNT } from '../agents/agent-schema.js';
|
|
26
27
|
import { normalizeAgentPolicy, routeRequiresAgentIntake, agentPipelineStage } from '../agents/agent-plan.js';
|
|
27
28
|
import { readAgentGateStatus } from '../agents/agent-gate.js';
|
|
@@ -458,6 +459,8 @@ export function promptPipelineContext(prompt, route = null) {
|
|
|
458
459
|
lines.push('AutoResearch route: load autoresearch-loop plus seo-geo-optimizer when SEO/GEO, discoverability, README, npm, GitHub stars, ranking, or AI-search visibility is relevant.');
|
|
459
460
|
if (route?.id === 'DB')
|
|
460
461
|
lines.push('DB route: scan/check database risk first; destructive DB operations remain forbidden.');
|
|
462
|
+
if (route?.id === 'MadDB')
|
|
463
|
+
lines.push('MadDB route: explicit invocation is the SQL-plane approval boundary. Use the mission-local write-capable Supabase MCP profile only for the bound cycle, verify execute_sql/apply_migration inventory before claiming ready, execute requested SQL-plane mutations, read back postconditions, then close the capability/profile and prove normal read-only restoration. Supabase project/account/billing/credential control-plane actions remain denied.');
|
|
461
464
|
if (route?.id === 'GX')
|
|
462
465
|
lines.push('GX route: use deterministic vgraph/beta render, validate, drift, and snapshot artifacts.');
|
|
463
466
|
return lines.join('\n');
|
|
@@ -517,6 +520,8 @@ export async function prepareRoute(root, prompt, state = {}) {
|
|
|
517
520
|
return withSkillDreamContext(await prepareGoal(root, route, task, routeNeedsContext7(route, cleanPrompt)), dreamContext);
|
|
518
521
|
if (route.id === 'ImageUXReview')
|
|
519
522
|
return withSkillDreamContext(await prepareImageUxReview(root, route, task, routeNeedsContext7(route, cleanPrompt)), dreamContext);
|
|
523
|
+
if (route.id === 'MadDB')
|
|
524
|
+
return withSkillDreamContext(await prepareMadDb(root, route, task, routeNeedsContext7(route, cleanPrompt)), dreamContext);
|
|
520
525
|
const required = routeNeedsContext7(route, cleanPrompt);
|
|
521
526
|
const reasoning = routeReasoning(route, cleanPrompt);
|
|
522
527
|
const nativeSessionsRequired = routeRequiresSubagents(route, cleanPrompt);
|
|
@@ -1111,6 +1116,41 @@ async function prepareDb(root, route, task, required) {
|
|
|
1111
1116
|
await setCurrent(root, routeState(id, route, 'DB_REVIEW_REQUIRED', required, { prompt: task, pipeline_plan_ready: validatePipelinePlan(pipelinePlan).ok, pipeline_plan_path: PIPELINE_PLAN_ARTIFACT }));
|
|
1112
1117
|
return routeContext(route, id, task, required, 'Run sks db policy/scan/check as needed, keep DB operations read-only, record safe MCP policy, and pass db-review.json.');
|
|
1113
1118
|
}
|
|
1119
|
+
async function prepareMadDb(root, route, task, required) {
|
|
1120
|
+
const prepared = await prepareMadDbMission({ root, task, verifyTools: false });
|
|
1121
|
+
const dir = missionDir(root, prepared.mission_id);
|
|
1122
|
+
const pipelinePlan = await writePipelinePlan(dir, {
|
|
1123
|
+
missionId: prepared.mission_id,
|
|
1124
|
+
route,
|
|
1125
|
+
task,
|
|
1126
|
+
required,
|
|
1127
|
+
ambiguity: { required: true, slots: 0, auto_sealed: true, passed: true, contract_hash: prepared.capability.operator_intent_hash }
|
|
1128
|
+
});
|
|
1129
|
+
await appendJsonl(path.join(dir, 'events.jsonl'), {
|
|
1130
|
+
ts: nowIso(),
|
|
1131
|
+
type: 'mad_db.route_prepared',
|
|
1132
|
+
mission_id: prepared.mission_id,
|
|
1133
|
+
cycle_id: prepared.cycle_id,
|
|
1134
|
+
blockers: prepared.blockers
|
|
1135
|
+
});
|
|
1136
|
+
await setCurrent(root, routeState(prepared.mission_id, route, prepared.ok ? 'MADDB_SQL_PLANE_CAPABILITY_ACTIVE' : 'MADDB_BLOCKED', required, {
|
|
1137
|
+
prompt: task,
|
|
1138
|
+
questions_allowed: false,
|
|
1139
|
+
implementation_allowed: prepared.ok,
|
|
1140
|
+
ambiguity_gate_required: true,
|
|
1141
|
+
ambiguity_gate_passed: true,
|
|
1142
|
+
mad_db_active: prepared.ok,
|
|
1143
|
+
mad_db_cycle_id: prepared.cycle_id,
|
|
1144
|
+
mad_db_runtime_session_id: prepared.capability.runtime_session_id,
|
|
1145
|
+
mad_db_profile_sha256: prepared.capability.transport.profile_sha256,
|
|
1146
|
+
mad_db_capability_mission_id: prepared.mission_id,
|
|
1147
|
+
mad_db_capability_file: 'mad-db-capability.json',
|
|
1148
|
+
stop_gate: 'mad-db-gate.json',
|
|
1149
|
+
pipeline_plan_ready: validatePipelinePlan(pipelinePlan).ok,
|
|
1150
|
+
pipeline_plan_path: PIPELINE_PLAN_ARTIFACT
|
|
1151
|
+
}));
|
|
1152
|
+
return routeContext(route, prepared.mission_id, task, required, `MadDB mission/capability/profile were created atomically for cycle ${prepared.cycle_id}. Verify Supabase MCP tool inventory exposes execute_sql and apply_migration before claiming ready; after execution require read-back proof, finally close the profile/capability and prove read-only restoration.`);
|
|
1153
|
+
}
|
|
1114
1154
|
async function prepareGx(root, route, task, required) {
|
|
1115
1155
|
const { id, dir } = await createMission(root, { mode: 'gx', prompt: task });
|
|
1116
1156
|
await writeJsonAtomic(path.join(dir, 'gx-gate.json'), { passed: false, vgraph_beta_render: false, validation: false, drift_snapshot: false, context7_evidence: false });
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const GLM_BENCHMARK_VERSION = '4.
|
|
1
|
+
export const GLM_BENCHMARK_VERSION = '4.2.0';
|
|
2
2
|
//# sourceMappingURL=glm-benchmark-types.js.map
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { appendJsonlBounded, nowIso, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { QA_ACTION_LEDGER_ARTIFACT, QA_LIVE_SESSION_ARTIFACT, QA_RUNTIME_EVENT_LEDGER_ARTIFACT } from './qa-types.js';
|
|
4
|
+
export async function runQaAppServerDriver(input) {
|
|
5
|
+
const events = [];
|
|
6
|
+
const dispose = input.client.onEvent?.((event) => {
|
|
7
|
+
events.push(event);
|
|
8
|
+
});
|
|
9
|
+
const startedAt = nowIso();
|
|
10
|
+
const blockers = [];
|
|
11
|
+
let threadId = null;
|
|
12
|
+
let turnId = null;
|
|
13
|
+
try {
|
|
14
|
+
await input.client.initialize?.();
|
|
15
|
+
const thread = await input.client.startThread({
|
|
16
|
+
cwd: input.cwd,
|
|
17
|
+
...input.threadStartParams
|
|
18
|
+
});
|
|
19
|
+
threadId = extractThreadId(thread);
|
|
20
|
+
if (!threadId)
|
|
21
|
+
blockers.push('app_server_thread_id_missing');
|
|
22
|
+
if (threadId) {
|
|
23
|
+
const turn = await input.client.startTurn({
|
|
24
|
+
threadId,
|
|
25
|
+
cwd: input.cwd,
|
|
26
|
+
input: [{ type: 'text', text: input.prompt }],
|
|
27
|
+
...input.turnStartParams
|
|
28
|
+
});
|
|
29
|
+
turnId = extractTurnId(turn);
|
|
30
|
+
if (!turnId)
|
|
31
|
+
blockers.push('app_server_turn_id_missing');
|
|
32
|
+
if (input.client.waitForTurnCompletion) {
|
|
33
|
+
const completed = await input.client.waitForTurnCompletion(threadId, turnId, input.timeoutMs);
|
|
34
|
+
events.push({ method: 'turn/completed', params: completed, received_at: nowIso() });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
blockers.push(`app_server_driver_failed:${err instanceof Error ? err.message : String(err)}`);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
dispose?.();
|
|
43
|
+
}
|
|
44
|
+
await writeAppServerEventLedgers(input.missionDir, input.missionId, input.surfaceSelection.selected_surface, threadId, turnId, events);
|
|
45
|
+
const session = {
|
|
46
|
+
schema: 'sks.qa-loop-live-session.v2',
|
|
47
|
+
started_at: startedAt,
|
|
48
|
+
completed_at: nowIso(),
|
|
49
|
+
mission_id: input.missionId,
|
|
50
|
+
status: blockers.length ? 'blocked' : 'completed',
|
|
51
|
+
selected_surface: input.surfaceSelection.selected_surface,
|
|
52
|
+
thread_id: threadId,
|
|
53
|
+
turn_id: turnId,
|
|
54
|
+
event_count: events.length,
|
|
55
|
+
item_event_count: events.filter(isItemEvent).length,
|
|
56
|
+
action_event_count: events.filter(isActionLikeEvent).length,
|
|
57
|
+
observation_event_count: events.filter(isObservationLikeEvent).length,
|
|
58
|
+
blockers,
|
|
59
|
+
unverified: events.some(isActionLikeEvent) ? [] : ['app_server_action_event_unverified'],
|
|
60
|
+
artifacts: {
|
|
61
|
+
runtime_events: QA_RUNTIME_EVENT_LEDGER_ARTIFACT,
|
|
62
|
+
action_ledger: QA_ACTION_LEDGER_ARTIFACT
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
await writeJsonAtomic(path.join(input.missionDir, QA_LIVE_SESSION_ARTIFACT), session);
|
|
66
|
+
return session;
|
|
67
|
+
}
|
|
68
|
+
async function writeAppServerEventLedgers(missionDir, missionId, surface, threadId, turnId, events) {
|
|
69
|
+
for (const event of events) {
|
|
70
|
+
const method = String(event.method || event.type || event.params?.method || 'app_server_event');
|
|
71
|
+
await appendJsonlBounded(path.join(missionDir, QA_RUNTIME_EVENT_LEDGER_ARTIFACT), {
|
|
72
|
+
schema: 'sks.qa-loop-app-server-event.v2',
|
|
73
|
+
ts: nowIso(),
|
|
74
|
+
mission_id: missionId,
|
|
75
|
+
thread_id: event.params?.threadId || event.threadId || threadId,
|
|
76
|
+
turn_id: event.params?.turnId || event.params?.turn?.id || event.turnId || turnId,
|
|
77
|
+
item_id: event.params?.itemId || event.params?.item?.id || event.itemId || null,
|
|
78
|
+
surface,
|
|
79
|
+
kind: method,
|
|
80
|
+
status: 'observed',
|
|
81
|
+
data: redactEvent(event)
|
|
82
|
+
});
|
|
83
|
+
if (isActionLikeEvent(event)) {
|
|
84
|
+
await appendJsonlBounded(path.join(missionDir, QA_ACTION_LEDGER_ARTIFACT), {
|
|
85
|
+
schema: 'sks.qa-loop-action.v2',
|
|
86
|
+
ts: nowIso(),
|
|
87
|
+
mission_id: missionId,
|
|
88
|
+
thread_id: event.params?.threadId || event.threadId || threadId,
|
|
89
|
+
turn_id: event.params?.turnId || event.params?.turn?.id || event.turnId || turnId,
|
|
90
|
+
item_id: event.params?.itemId || event.params?.item?.id || event.itemId || null,
|
|
91
|
+
surface,
|
|
92
|
+
kind: method,
|
|
93
|
+
status: 'observed',
|
|
94
|
+
real: true,
|
|
95
|
+
data: redactEvent(event)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function extractThreadId(value) {
|
|
101
|
+
const obj = value;
|
|
102
|
+
return stringOrNull(obj?.thread?.id || obj?.threadId || obj?.id);
|
|
103
|
+
}
|
|
104
|
+
function extractTurnId(value) {
|
|
105
|
+
const obj = value;
|
|
106
|
+
return stringOrNull(obj?.turn?.id || obj?.turnId || obj?.id);
|
|
107
|
+
}
|
|
108
|
+
function stringOrNull(value) {
|
|
109
|
+
const text = String(value || '').trim();
|
|
110
|
+
return text || null;
|
|
111
|
+
}
|
|
112
|
+
function isItemEvent(event) {
|
|
113
|
+
return /^item\//.test(String(event.method || event.type || ''));
|
|
114
|
+
}
|
|
115
|
+
function isActionLikeEvent(event) {
|
|
116
|
+
const method = String(event.method || event.type || '');
|
|
117
|
+
return /^item\/.*(?:tool|action|commandExecution|computer|browser|chrome)/i.test(method)
|
|
118
|
+
|| /(?:tool|action|click|type|scroll|navigate|screenshot|observation)/i.test(JSON.stringify(event.params || event));
|
|
119
|
+
}
|
|
120
|
+
function isObservationLikeEvent(event) {
|
|
121
|
+
const method = String(event.method || event.type || '');
|
|
122
|
+
return /observation|completed|screenshot|browser|chrome|computer/i.test(method)
|
|
123
|
+
|| /observation|screenshot|visual|page|window/i.test(JSON.stringify(event.params || event));
|
|
124
|
+
}
|
|
125
|
+
function redactEvent(event) {
|
|
126
|
+
return JSON.parse(JSON.stringify(event, (key, value) => {
|
|
127
|
+
if (/(password|passwd|token|secret|cookie|authorization|credential)/i.test(String(key)))
|
|
128
|
+
return '[REDACTED]';
|
|
129
|
+
if (typeof value === 'string' && /(Bearer\s+[A-Za-z0-9._-]+|sk-[A-Za-z0-9_-]+)/.test(value))
|
|
130
|
+
return '[REDACTED]';
|
|
131
|
+
return value;
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=qa-app-server-driver.js.map
|