mixdog 0.9.166 → 0.9.167
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 +1 -1
- package/scripts/audit-usage-ledger.mjs +86 -0
- package/scripts/generate-voice-runtime-manifest.mjs +22 -17
- package/scripts/memory-chunk-quality.mjs +128 -0
- package/scripts/memory-layer2-check.mjs +133 -0
- package/scripts/model-catalog-audit.mjs +6 -0
- package/scripts/native-tool-download.mjs +14 -0
- package/scripts/prepare-native-assets.mjs +2 -7
- package/scripts/tool-failures.mjs +7 -2
- package/scripts/verify-release-assets.mjs +48 -90
- package/src/defaults/skills/browser-use/SKILL.md +33 -18
- package/src/defaults/skills/computer-use/SKILL.md +16 -8
- package/src/lib/keychain-cjs.cjs +0 -1
- package/src/lib/rules-builder.cjs +4 -1
- package/src/rules/agent/40-cycle1-agent.md +21 -13
- package/src/rules/lead/01-general.md +8 -3
- package/src/rules/shared/10-tool-workflow.md +20 -11
- package/src/rules/shared/30-exploration.md +11 -4
- package/src/rules/shared/40-editing.md +2 -3
- package/src/rules/shared/50-execution.md +6 -3
- package/src/rules/shared/60-verification.md +3 -2
- package/src/rules/shared/70-delivery.md +0 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +51 -45
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +3 -4
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +44 -7
- package/src/runtime/agent/orchestrator/agent-trace.mjs +9 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -4
- package/src/runtime/agent/orchestrator/context/collect.mjs +25 -1055
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +272 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +296 -0
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +523 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +13 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +214 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +220 -116
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +74 -44
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +191 -29
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +13 -12
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +1 -9
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +48 -25
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +28 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +1 -8
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/lib/grok-tool-schema.mjs +61 -13
- package/src/runtime/agent/orchestrator/providers/model-catalog-projection.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +123 -118
- package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-codex-endpoints.mjs +21 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +154 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +322 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +243 -702
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-startup-prewarm.mjs +221 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/provider-model-identities.mjs +40 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +120 -710
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +36 -15
- package/src/runtime/agent/orchestrator/session/cache/util.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +36 -31
- package/src/runtime/agent/orchestrator/session/compact.mjs +4 -5
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +6 -5
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +121 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/loop/diagnostics.mjs +74 -0
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +44 -17
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +266 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +111 -0
- package/src/runtime/agent/orchestrator/session/loop/request-projection.mjs +99 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +91 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +20 -11
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +3 -5
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +9 -3
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +39 -0
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +21 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -25
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +62 -10
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +17 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +61 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +13 -9
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +69 -6
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +14 -17
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
- package/src/runtime/bridge-discovery.mjs +8 -0
- package/src/runtime/browser-bridge/action-schema.mjs +37 -43
- package/src/runtime/browser-bridge/client.mjs +111 -99
- package/src/runtime/browser-bridge/input-fields.mjs +187 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +12 -227
- package/src/runtime/computer-bridge/action-schema.mjs +2 -46
- package/src/runtime/computer-bridge/client.mjs +69 -77
- package/src/runtime/computer-bridge/core-actions.mjs +7 -5
- package/src/runtime/computer-bridge/error-recovery.mjs +41 -37
- package/src/runtime/computer-bridge/tool-defs.mjs +1 -1
- package/src/runtime/github/commands.mjs +4 -1
- package/src/runtime/github/contract.mjs +1 -1
- package/src/runtime/github/tool.mjs +2 -2
- package/src/runtime/media/adapters/antigravity-image.mjs +72 -0
- package/src/runtime/media/adapters/gemini-image.mjs +20 -11
- package/src/runtime/media/auth.mjs +12 -0
- package/src/runtime/media/catalog.mjs +30 -10
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +40 -31
- package/src/runtime/memory/index.mjs +2 -9
- package/src/runtime/memory/lib/compact-handoff.mjs +26 -18
- package/src/runtime/memory/lib/memory-chunk-audit.mjs +41 -0
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +388 -0
- package/src/runtime/memory/lib/memory-cycle1.mjs +95 -394
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +29 -4
- package/src/runtime/memory/lib/memory-recall-store.mjs +8 -12
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -11
- package/src/runtime/memory/lib/memory.mjs +2 -0
- package/src/runtime/memory/lib/query-handlers.mjs +98 -144
- package/src/runtime/memory/lib/query-ranking.mjs +23 -9
- package/src/runtime/memory/lib/recall-format.mjs +37 -27
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +14 -8
- package/src/runtime/memory/lib/session-ingest.mjs +45 -84
- package/src/runtime/memory/lib/transcript-ingest.mjs +19 -21
- package/src/runtime/memory/tool-defs.mjs +2 -2
- package/src/runtime/office/authoring/pptx-author-session.mjs +3 -7
- package/src/runtime/office/core/office-actions-batch.mjs +2 -8
- package/src/runtime/office/core/office-actions-open.mjs +124 -0
- package/src/runtime/office/core/office-actions-read.mjs +127 -0
- package/src/runtime/office/core/office-core.mjs +80 -1
- package/src/runtime/office/core/office-sessions.mjs +116 -138
- package/src/runtime/office/index.mjs +18 -271
- package/src/runtime/office/pdf/pdf-analysis.mjs +2 -2
- package/src/runtime/office/pdf/pdf-limits.mjs +2 -0
- package/src/runtime/office/tool-defs.mjs +7 -6
- package/src/runtime/shared/agent-route-config.mjs +8 -8
- package/src/runtime/shared/child-spawn-gate.mjs +10 -13
- package/src/runtime/shared/config.mjs +2 -7
- package/src/runtime/shared/env.mjs +3 -3
- package/src/runtime/shared/err-text.mjs +1 -1
- package/src/runtime/shared/extension-scopes.mjs +3 -2
- package/src/runtime/shared/heap-cap.mjs +10 -6
- package/src/runtime/shared/llm/cost.mjs +35 -10
- package/src/runtime/shared/llm/usage-accounting.mjs +11 -1
- package/src/runtime/shared/llm/usage-context.mjs +6 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +5 -0
- package/src/runtime/shared/llm/usage-ledger-repair.mjs +109 -0
- package/src/runtime/shared/llm/usage-ledger.mjs +59 -20
- package/src/runtime/shared/llm/usage-pricing-refresh.mjs +26 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +17 -15
- package/src/runtime/shared/native-asset.mjs +3 -4
- package/src/runtime/shared/object.mjs +4 -0
- package/src/runtime/shared/pg-schema-db.mjs +21 -0
- package/src/runtime/shared/plugin-metadata.mjs +3 -1
- package/src/runtime/shared/resource-admission.mjs +1 -6
- package/src/runtime/shared/schedules-db.mjs +8 -24
- package/src/runtime/shared/schema-value-error.d.mts +2 -0
- package/src/runtime/shared/schema-value-error.mjs +68 -0
- package/src/runtime/shared/singleton-owner.mjs +11 -24
- package/src/runtime/shared/staged-update.mjs +1 -10
- package/src/runtime/shared/tool-card-model.mjs +8 -5
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +1 -5
- package/src/runtime/shared/user-data-guard.mjs +1 -4
- package/src/runtime/shared/webhooks-db.mjs +8 -24
- package/src/runtime/web-search/index.mjs +32 -8
- package/src/runtime/web-search/tool-defs.mjs +4 -4
- package/src/session-runtime/config-lifecycle.mjs +34 -0
- package/src/session-runtime/context-status.mjs +19 -0
- package/src/session-runtime/cwd-plugins.mjs +12 -29
- package/src/session-runtime/goal-tasks.mjs +2 -2
- package/src/session-runtime/goal-tool-defs.mjs +3 -2
- package/src/session-runtime/internal-tool-executor.mjs +7 -0
- package/src/session-runtime/lifecycle-api.mjs +5 -11
- package/src/session-runtime/notification-bus.mjs +41 -0
- package/src/session-runtime/prewarm.mjs +22 -0
- package/src/session-runtime/runtime-bootstrap.mjs +90 -0
- package/src/session-runtime/runtime-core.mjs +88 -202
- package/src/session-runtime/runtime-feature-gates.mjs +37 -0
- package/src/session-runtime/session-lifecycle.mjs +5 -5
- package/src/session-runtime/session-turn-api.mjs +1 -12
- package/src/session-runtime/setup-tool/executor.mjs +13 -5
- package/src/session-runtime/setup-tool/tool-defs.mjs +52 -46
- package/src/session-runtime/tool-defs.mjs +73 -4
- package/src/session-runtime/usage-stats-api.mjs +4 -1
- package/src/session-runtime/warmup-schedulers.mjs +8 -2
- package/src/session-runtime/workflow.mjs +14 -0
- package/src/standalone/channel-session-router.mjs +3 -1
- package/src/standalone/channel-worker.mjs +18 -9
- package/src/standalone/daemon-crash-capture.mjs +534 -0
- package/src/standalone/daemon-telemetry.mjs +175 -0
- package/src/standalone/daemon.mjs +31 -21
- package/src/standalone/session-client.mjs +31 -23
- package/src/standalone/usage-dashboard-model.mjs +2 -0
- package/src/standalone/usage-stats-hours.mjs +18 -11
- package/src/standalone/usage-stats-model.mjs +39 -28
- package/src/standalone/usage-stats-period.mjs +43 -45
- package/src/tui/app/use-transcript-window.mjs +1 -1
- package/src/tui/components/StatusLine.jsx +13 -42
- package/src/tui/dist/index.mjs +256 -239
- package/src/tui/session/session-api-ext.mjs +44 -41
- package/src/tui/session/session-api.mjs +4 -0
- package/src/tui/session/tool-card-results.mjs +25 -78
- package/src/tui/session/tool-result-status.mjs +94 -1
- package/src/tui/session/turn-aggregate-cards.mjs +17 -54
- package/src/ui/statusline-format.mjs +6 -26
- package/src/workflows/default/WORKFLOW.md +5 -0
package/package.json
CHANGED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { copyFileSync, mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
import { DatabaseSync, backup } from 'node:sqlite';
|
|
5
|
+
import { UsageLedger } from '../src/runtime/shared/llm/usage-ledger.mjs';
|
|
6
|
+
import { repairUsageLedger, usageLedgerIntegrity } from '../src/runtime/shared/llm/usage-ledger-repair.mjs';
|
|
7
|
+
import { usageStatsSnapshot } from '../src/standalone/usage-stats-model.mjs';
|
|
8
|
+
|
|
9
|
+
// Usage: node scripts/audit-usage-ledger.mjs --ledger=<absolute file>
|
|
10
|
+
// [--reattribute-xai-to-grok] [--apply]
|
|
11
|
+
// Raw files and a consistent SQLite snapshot survive both dry runs and apply.
|
|
12
|
+
const options = process.argv.slice(2);
|
|
13
|
+
const ledgerArg = options.find((arg) => arg.startsWith('--ledger='))?.slice(9);
|
|
14
|
+
if (!ledgerArg || !isAbsolute(ledgerArg) || !/\.sqlite$/i.test(ledgerArg)) {
|
|
15
|
+
throw new Error('Supply an exact absolute --ledger=<file.sqlite> path');
|
|
16
|
+
}
|
|
17
|
+
for (const arg of options) {
|
|
18
|
+
if (!arg.startsWith('--ledger=') && !['--reattribute-xai-to-grok', '--apply'].includes(arg)) {
|
|
19
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const source = resolve(ledgerArg);
|
|
23
|
+
const auditRoot = join(dirname(source), 'audits');
|
|
24
|
+
mkdirSync(auditRoot, { recursive: true });
|
|
25
|
+
const workspace = mkdtempSync(join(auditRoot, 'usage-'));
|
|
26
|
+
const raw = join(workspace, 'original-files');
|
|
27
|
+
mkdirSync(raw);
|
|
28
|
+
for (const suffix of ['', '-wal', '-shm']) {
|
|
29
|
+
try { copyFileSync(source + suffix, join(raw, basename(source) + suffix)); }
|
|
30
|
+
catch (error) { if (!suffix || error.code !== 'ENOENT') throw error; }
|
|
31
|
+
}
|
|
32
|
+
const snapshotPath = join(workspace, 'backup.sqlite');
|
|
33
|
+
const original = new DatabaseSync(source, { readOnly: true });
|
|
34
|
+
try { await backup(original, snapshotPath); } finally { original.close(); }
|
|
35
|
+
const workingPath = join(workspace, 'working.sqlite');
|
|
36
|
+
copyFileSync(snapshotPath, workingPath);
|
|
37
|
+
const throughTs = Date.now();
|
|
38
|
+
const providerOverrides = options.includes('--reattribute-xai-to-grok') ? { xai: 'grok-oauth' } : {};
|
|
39
|
+
const summarize = (ledger) => {
|
|
40
|
+
const stats = usageStatsSnapshot({ rollup: ledger.rollup(), now: throughTs, source: 'all' });
|
|
41
|
+
const sum = (rows, key) => rows.reduce((n, row) => n + Number(row[key] || 0), 0);
|
|
42
|
+
for (const key of ['turns', 'input', 'output', 'cacheRead', 'cacheWrite', 'costKnownTurns']) {
|
|
43
|
+
assert.equal(sum(stats.providers, key), stats.totals[key], `provider sum: ${key}`);
|
|
44
|
+
}
|
|
45
|
+
for (const rows of [stats.providers, stats.daily]) {
|
|
46
|
+
assert.ok(Math.abs(sum(rows, 'costUsd') - stats.totals.costUsd) <= rows.length * 0.000001 + 0.000001, 'cost sum');
|
|
47
|
+
assert.equal(sum(rows, 'tokens'), stats.totals.tokens, 'token sum');
|
|
48
|
+
assert.equal(sum(rows, 'turns'), stats.totals.turns, 'record sum');
|
|
49
|
+
}
|
|
50
|
+
assert.ok(Math.abs(stats.totals.costBilled + stats.totals.costEstimated - stats.totals.costUsd) < 0.000002, 'cost categories');
|
|
51
|
+
assert.ok(stats.totals.costKnownTurns <= stats.totals.turns, 'price coverage');
|
|
52
|
+
for (const p of stats.providers) {
|
|
53
|
+
assert.equal(sum(p.models, 'turns'), p.turns, `model sum: ${p.provider}`);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
integrity: usageLedgerIntegrity(ledger.db), totals: stats.totals,
|
|
57
|
+
providers: stats.providers.map(({ provider, providerKind, turns, tokens, costUsd, costUnpricedTurns }) =>
|
|
58
|
+
({ provider, providerKind, turns, tokens, costUsd, costUnpricedTurns })),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const working = new UsageLedger(workingPath);
|
|
62
|
+
let before, preview, after;
|
|
63
|
+
try {
|
|
64
|
+
assert.deepEqual(working.db.prepare('PRAGMA quick_check').all().map((r) => r.quick_check), ['ok']);
|
|
65
|
+
before = summarize(working);
|
|
66
|
+
preview = repairUsageLedger(working, { throughTs, providerOverrides });
|
|
67
|
+
after = summarize(working);
|
|
68
|
+
assert.deepEqual(before.integrity, after.integrity);
|
|
69
|
+
} finally { working.close(); }
|
|
70
|
+
const report = { source, workspace, backup: snapshotPath, throughTs, providerOverrides, before, preview, after, applied: false };
|
|
71
|
+
writeFileSync(join(workspace, 'report.json'), JSON.stringify(report, null, 2));
|
|
72
|
+
if (options.includes('--apply')) {
|
|
73
|
+
const live = new UsageLedger(source);
|
|
74
|
+
try {
|
|
75
|
+
// The transaction repairs only the bounded history. Concurrent new
|
|
76
|
+
// requests are retained and indexed too; no database file is replaced.
|
|
77
|
+
report.appliedRepair = repairUsageLedger(live, { throughTs, providerOverrides });
|
|
78
|
+
report.live = summarize(live);
|
|
79
|
+
assert.deepEqual(live.db.prepare('PRAGMA quick_check').all().map((r) => r.quick_check), ['ok']);
|
|
80
|
+
report.applied = true;
|
|
81
|
+
} finally { live.close(); }
|
|
82
|
+
writeFileSync(join(workspace, 'report.json'), JSON.stringify(report, null, 2));
|
|
83
|
+
}
|
|
84
|
+
console.log(JSON.stringify({ workspace, backup: snapshotPath, applied: report.applied,
|
|
85
|
+
preview, ...(report.appliedRepair ? { appliedRepair: report.appliedRepair } : {}),
|
|
86
|
+
totals: (report.live || after).totals, providers: (report.live || after).providers }, null, 2));
|
|
@@ -82,6 +82,24 @@ export function compareSemver(left, right) {
|
|
|
82
82
|
return 0
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
export function isHiddenDraftReleaseUrl(sourceUrl, { repository, asset }) {
|
|
86
|
+
try {
|
|
87
|
+
const parsed = new URL(sourceUrl)
|
|
88
|
+
const prefix = `/${repository}/releases/download/`
|
|
89
|
+
const parts = parsed.pathname.startsWith(prefix)
|
|
90
|
+
? parsed.pathname.slice(prefix.length).split('/')
|
|
91
|
+
: []
|
|
92
|
+
return parsed.origin === 'https://github.com'
|
|
93
|
+
&& !parsed.search
|
|
94
|
+
&& !parsed.hash
|
|
95
|
+
&& parts.length === 2
|
|
96
|
+
&& parts[0].startsWith('untagged-')
|
|
97
|
+
&& parts[1] === asset
|
|
98
|
+
} catch {
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
85
103
|
export function buildVoiceRuntimePlatforms(
|
|
86
104
|
releaseAssets,
|
|
87
105
|
{ repository = 'tribgames/mixdog', tag = VOICE_RUNTIME_TAG } = {},
|
|
@@ -103,23 +121,10 @@ export function buildVoiceRuntimePlatforms(
|
|
|
103
121
|
}
|
|
104
122
|
const sourceUrl = String(asset.browser_download_url || '')
|
|
105
123
|
const url = `https://github.com/${repository}/releases/download/${tag}/${spec.asset}`
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const parts = parsed.pathname.startsWith(prefix)
|
|
111
|
-
? parsed.pathname.slice(prefix.length).split('/')
|
|
112
|
-
: []
|
|
113
|
-
validDraftUrl = parsed.origin === 'https://github.com'
|
|
114
|
-
&& !parsed.search
|
|
115
|
-
&& !parsed.hash
|
|
116
|
-
&& parts.length === 2
|
|
117
|
-
&& parts[0].startsWith('untagged-')
|
|
118
|
-
&& parts[1] === spec.asset
|
|
119
|
-
} catch {
|
|
120
|
-
validDraftUrl = false
|
|
121
|
-
}
|
|
122
|
-
if (sourceUrl !== url && !validDraftUrl) {
|
|
124
|
+
if (sourceUrl !== url && !isHiddenDraftReleaseUrl(sourceUrl, {
|
|
125
|
+
repository,
|
|
126
|
+
asset: spec.asset,
|
|
127
|
+
})) {
|
|
123
128
|
throw new Error(`release asset ${spec.asset} has invalid download URL`)
|
|
124
129
|
}
|
|
125
130
|
platforms[spec.key] = {
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Audit/simulate on a separate snapshot. This command NEVER updates the
|
|
3
|
+
// live DB, starts PG, migrates a schema or replaces the supplied input file.
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { createHash } from 'node:crypto'
|
|
6
|
+
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
|
|
7
|
+
import { tmpdir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { parseArgs } from 'node:util'
|
|
10
|
+
import { auditChunkEntries, collectChunkRoots } from '../src/runtime/memory/lib/memory-chunk-audit.mjs'
|
|
11
|
+
import {
|
|
12
|
+
generateCycle1Chunks,
|
|
13
|
+
} from '../src/runtime/memory/lib/memory-chunk-quality.mjs'
|
|
14
|
+
import { compactHandoffRows } from '../src/runtime/memory/lib/compact-handoff.mjs'
|
|
15
|
+
import { renderEntryLines } from '../src/runtime/memory/lib/recall-format.mjs'
|
|
16
|
+
import { readServiceAdvert } from '../src/runtime/shared/service-discovery.mjs'
|
|
17
|
+
|
|
18
|
+
const { values } = parseArgs({
|
|
19
|
+
options: {
|
|
20
|
+
input: { type: 'string' },
|
|
21
|
+
'simulate-limit': { type: 'string', default: '0' },
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
const simulationLimit = Number(values['simulate-limit'])
|
|
25
|
+
if (!Number.isSafeInteger(simulationLimit) || simulationLimit < 0) throw new Error('simulate-limit must be a nonnegative integer')
|
|
26
|
+
const directory = await mkdtemp(join(tmpdir(), 'mixdog-chunk-quality-'))
|
|
27
|
+
console.log(`Artifacts: ${directory}`)
|
|
28
|
+
let snapshot
|
|
29
|
+
if (values.input) {
|
|
30
|
+
snapshot = JSON.parse(await readFile(values.input, 'utf8'))
|
|
31
|
+
} else {
|
|
32
|
+
const advert = readServiceAdvert('pg')
|
|
33
|
+
if (!Number.isInteger(Number(advert?.pg_port)) || Number(advert.pg_port) < 1) throw new Error('No advertised PG port; live audit blocked')
|
|
34
|
+
const { default: pg } = await import('pg')
|
|
35
|
+
const client = new pg.Client({
|
|
36
|
+
host: '127.0.0.1', port: Number(advert.pg_port), user: 'postgres',
|
|
37
|
+
database: 'mixdog', password: '', connectionTimeoutMillis: 10000,
|
|
38
|
+
})
|
|
39
|
+
await client.connect()
|
|
40
|
+
try {
|
|
41
|
+
await client.query('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY')
|
|
42
|
+
const { rows } = await client.query(`
|
|
43
|
+
SELECT id, ts, role, content, source_ref, session_id, source_turn, time_source,
|
|
44
|
+
project_id, chunk_root, is_root, element, category, summary, status, error_count,
|
|
45
|
+
to_jsonb(e)->'chunk_quality' AS chunk_quality
|
|
46
|
+
FROM memory.entries e ORDER BY id`)
|
|
47
|
+
snapshot = { capturedAt: new Date().toISOString(), entries: rows }
|
|
48
|
+
await client.query('COMMIT')
|
|
49
|
+
} catch (error) {
|
|
50
|
+
await client.query('ROLLBACK')
|
|
51
|
+
throw error
|
|
52
|
+
} finally {
|
|
53
|
+
await client.end()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
assert.ok(Array.isArray(snapshot.entries), 'snapshot.entries must be an array')
|
|
57
|
+
const sourceBytes = JSON.stringify(snapshot)
|
|
58
|
+
const sourceHash = createHash('sha256').update(sourceBytes).digest('hex')
|
|
59
|
+
await writeFile(join(directory, 'snapshot.json'), sourceBytes, { flag: 'wx' })
|
|
60
|
+
const working = structuredClone(snapshot)
|
|
61
|
+
const before = auditChunkEntries(snapshot.entries)
|
|
62
|
+
const outcomes = []
|
|
63
|
+
let blocked = false
|
|
64
|
+
const renderOptions = { chronologicalOrder: true, compactTimestamps: true, pendingMarks: false, maxBodyChars: null }
|
|
65
|
+
function assertCoverage(projection, expectedRows) {
|
|
66
|
+
const represented = new Set()
|
|
67
|
+
for (const row of projection) {
|
|
68
|
+
const ids = row._compactBody ? row._compactMemberIds : [String(row.id)]
|
|
69
|
+
for (const id of ids) {
|
|
70
|
+
assert.equal(represented.has(String(id)), false, `duplicate projected source ${id}`)
|
|
71
|
+
represented.add(String(id))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
assert.deepEqual([...represented].sort(), expectedRows.map(entry => String(entry.id)).sort())
|
|
75
|
+
return represented.size
|
|
76
|
+
}
|
|
77
|
+
if (simulationLimit) {
|
|
78
|
+
const { callAgentDispatch } = await import('../src/runtime/memory/lib/agent-ipc.mjs')
|
|
79
|
+
const { resolveMaintenancePreset } = await import('../src/runtime/shared/llm/index.mjs')
|
|
80
|
+
const options = {
|
|
81
|
+
callLlm: callAgentDispatch,
|
|
82
|
+
request: { agent: 'cycle1-agent', taskType: 'maintenance', preset: resolveMaintenancePreset('memory'), timeout: 180000, cwd: null },
|
|
83
|
+
}
|
|
84
|
+
for (const root of collectChunkRoots(working.entries).slice(0, simulationLimit)) {
|
|
85
|
+
const generated = await generateCycle1Chunks(root.members, options)
|
|
86
|
+
const rawIds = new Set(generated.rawRowIds.map(String))
|
|
87
|
+
const projected = compactHandoffRows([
|
|
88
|
+
...generated.chunks.map(chunk => ({
|
|
89
|
+
...chunk.members[0], is_root: 1, members: chunk.members,
|
|
90
|
+
summary: chunk.summary, element: chunk.element, chunk_quality: chunk.quality,
|
|
91
|
+
})),
|
|
92
|
+
...root.members.filter(member => rawIds.has(String(member.id))).map(member => ({ ...member, is_root: 0 })),
|
|
93
|
+
])
|
|
94
|
+
const outcome = {
|
|
95
|
+
id: root.id, inputRows: root.members.length,
|
|
96
|
+
stats: generated.stats, chunks: generated.chunks.length, rawRowIds: generated.rawRowIds,
|
|
97
|
+
errors: generated.invalidChunks, projectionCoverage: assertCoverage(projected, root.members),
|
|
98
|
+
text: renderEntryLines(projected, renderOptions),
|
|
99
|
+
}
|
|
100
|
+
if (generated.invalidChunks.some(invalid => invalid.reason === 'llm_error')) blocked = true
|
|
101
|
+
outcomes.push(outcome)
|
|
102
|
+
if (blocked) break
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Independent integrity checks: the source snapshot is unchanged; no source
|
|
106
|
+
// identity, content, chronology, membership or status is rewritten in the copy.
|
|
107
|
+
assert.equal(createHash('sha256').update(JSON.stringify(snapshot)).digest('hex'), sourceHash)
|
|
108
|
+
assert.deepEqual(working.entries.map(({ id, content, ts, role, chunk_root, status }) => [id, content, ts, role, chunk_root, status]),
|
|
109
|
+
snapshot.entries.map(({ id, content, ts, role, chunk_root, status }) => [id, content, ts, role, chunk_root, status]))
|
|
110
|
+
const after = auditChunkEntries(working.entries)
|
|
111
|
+
const roots = collectChunkRoots(working.entries)
|
|
112
|
+
const rootIds = new Set(roots.map(root => String(root.id)))
|
|
113
|
+
const standalone = working.entries.filter(entry => !rootIds.has(String(entry.chunk_root)))
|
|
114
|
+
const projection = compactHandoffRows([...roots, ...standalone])
|
|
115
|
+
const projectionCoverage = assertCoverage(projection, working.entries)
|
|
116
|
+
const report = {
|
|
117
|
+
capturedAt: snapshot.capturedAt, sourceHash, before, after, outcomes, blocked,
|
|
118
|
+
integrity: { sourceUnchanged: true, sourceRowsUnchanged: true, projectionCoverage },
|
|
119
|
+
}
|
|
120
|
+
working.simulations = outcomes
|
|
121
|
+
await writeFile(join(directory, 'working.json'), JSON.stringify(working), { flag: 'wx' })
|
|
122
|
+
await writeFile(join(directory, 'report.json'), JSON.stringify(report, null, 2), { flag: 'wx' })
|
|
123
|
+
console.log(JSON.stringify({
|
|
124
|
+
directory, audit: { ...before, chunks: undefined },
|
|
125
|
+
outcomes: outcomes.map(outcome => ({ ...outcome, text: undefined })),
|
|
126
|
+
integrity: report.integrity, blocked,
|
|
127
|
+
}, null, 2))
|
|
128
|
+
if (blocked) process.exitCode = 1
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Real-AI second-layer simulation on immutable, prepared first-layer cases.
|
|
3
|
+
// Input: { cases: [{ name, rows: [{id,ts,role,session_id,content,sourceIds}],
|
|
4
|
+
// preservedRawIds?, reviewCriteria? }] }.
|
|
5
|
+
import assert from 'node:assert/strict'
|
|
6
|
+
import { createHash } from 'node:crypto'
|
|
7
|
+
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
|
|
8
|
+
import { tmpdir } from 'node:os'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { parseArgs } from 'node:util'
|
|
11
|
+
import { generateCycle1Chunks } from '../src/runtime/memory/lib/memory-chunk-quality.mjs'
|
|
12
|
+
import { estimateTokens } from '../src/runtime/agent/orchestrator/session/token-estimate.mjs'
|
|
13
|
+
import { makeAgentDispatch } from '../src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs'
|
|
14
|
+
import { loadConfig } from '../src/runtime/agent/orchestrator/config.mjs'
|
|
15
|
+
import { resolveMaintenanceRoute } from '../src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs'
|
|
16
|
+
import { initProviders } from '../src/runtime/agent/orchestrator/providers/registry.mjs'
|
|
17
|
+
import { loadScopedRoleInstructions } from '../src/runtime/agent/orchestrator/context/collect.mjs'
|
|
18
|
+
import { resolveMaintenancePreset } from '../src/runtime/shared/llm/index.mjs'
|
|
19
|
+
|
|
20
|
+
const { values } = parseArgs({ options: {
|
|
21
|
+
input: { type: 'string' }, case: { type: 'string', multiple: true },
|
|
22
|
+
} })
|
|
23
|
+
if (!values.input) throw new Error('--input must name a prepared first-layer case file')
|
|
24
|
+
const bytes = await readFile(values.input, 'utf8')
|
|
25
|
+
const sourceHash = createHash('sha256').update(bytes).digest('hex')
|
|
26
|
+
const source = JSON.parse(bytes)
|
|
27
|
+
assert.ok(Array.isArray(source.cases) && source.cases.length, 'cases must be nonempty')
|
|
28
|
+
const selectedCases = values.case ? new Set(values.case) : null
|
|
29
|
+
if (selectedCases) {
|
|
30
|
+
for (const name of selectedCases) assert.ok(source.cases.some(item => item.name === name), `unknown case: ${name}`)
|
|
31
|
+
}
|
|
32
|
+
const directory = await mkdtemp(join(tmpdir(), 'mixdog-layer2-once-'))
|
|
33
|
+
await writeFile(join(directory, 'input.json'), bytes, { flag: 'wx' })
|
|
34
|
+
await writeFile(join(directory, 'working.json'), bytes, { flag: 'wx' })
|
|
35
|
+
console.log(`Artifacts: ${directory}`)
|
|
36
|
+
const working = structuredClone(source)
|
|
37
|
+
const config = loadConfig()
|
|
38
|
+
const configuredRoute = resolveMaintenancePreset('memory')
|
|
39
|
+
|| resolveMaintenanceRoute({ agent: 'cycle1-agent', config })
|
|
40
|
+
const preset = typeof configuredRoute === 'object' ? configuredRoute
|
|
41
|
+
: config.presets.find(item => item.id === configuredRoute || item.name === configuredRoute)
|
|
42
|
+
assert.ok(preset?.provider && preset?.model, 'second-layer model route is unresolved')
|
|
43
|
+
const request = {
|
|
44
|
+
agent: 'cycle1-agent', taskType: 'maintenance', preset: { ...preset, effort: 'low' },
|
|
45
|
+
timeout: 180000, cwd: null,
|
|
46
|
+
}
|
|
47
|
+
// The installed broker can carry an older, first-layer-only system policy.
|
|
48
|
+
// A source-code benchmark must use the worktree's actual scoped role rules,
|
|
49
|
+
// not a new user prompt under that incompatible old system instruction.
|
|
50
|
+
const roleInstructions = loadScopedRoleInstructions(request.agent, request.preset?.provider)
|
|
51
|
+
assert.ok(roleInstructions.includes('SECOND_LAYER'), 'source runtime did not load the second-layer role policy')
|
|
52
|
+
const rolePolicyHash = createHash('sha256').update(roleInstructions).digest('hex')
|
|
53
|
+
await initProviders(config.providers)
|
|
54
|
+
const dispatch = makeAgentDispatch({
|
|
55
|
+
agent: request.agent, taskType: request.taskType, preset: request.preset,
|
|
56
|
+
cwd: null, brief: false, config,
|
|
57
|
+
})
|
|
58
|
+
const outcomes = []
|
|
59
|
+
let blocked = false
|
|
60
|
+
for (const item of working.cases) {
|
|
61
|
+
if (selectedCases && !selectedCases.has(item.name)) continue
|
|
62
|
+
if (blocked) {
|
|
63
|
+
outcomes.push({ name: item.name, status: 'not_run_after_provider_failure' })
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
const original = JSON.stringify(item)
|
|
67
|
+
const inputText = item.rows.map(row => row.content).join('\n')
|
|
68
|
+
const inputTokens = estimateTokens(inputText)
|
|
69
|
+
assert.ok(inputTokens >= 2)
|
|
70
|
+
assert.equal(new Set(item.rows.map(row => String(row.id))).size, item.rows.length)
|
|
71
|
+
assert.equal(new Set(item.rows.map(row => row.session_id)).size, 1)
|
|
72
|
+
const calls = []
|
|
73
|
+
const result = await generateCycle1Chunks(item.rows, {
|
|
74
|
+
// Prepared cases have no measured live-context budget. Report the writing
|
|
75
|
+
// target separately instead of inventing a hard half-size context limit.
|
|
76
|
+
layer: 2,
|
|
77
|
+
request,
|
|
78
|
+
callLlm: async (options, prompt) => {
|
|
79
|
+
const response = await dispatch({
|
|
80
|
+
prompt, preset: options.preset, cwd: null, parentSignal: options.signal,
|
|
81
|
+
idleTimeoutMs: options.timeout,
|
|
82
|
+
})
|
|
83
|
+
calls.push({ mode: options.mode, response })
|
|
84
|
+
return response
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
assert.equal(JSON.stringify(item), original, 'source input mutated')
|
|
88
|
+
assert.ok(result)
|
|
89
|
+
assert.ok(result.stats.groupingCalls <= 1, 'second layer must not make multiple AI calls')
|
|
90
|
+
const fallback = new Set(result.rawRowIds.map(String))
|
|
91
|
+
const units = [
|
|
92
|
+
...result.chunks.map(chunk => ({
|
|
93
|
+
position: Math.min(...chunk._idxList) - 1, text: chunk.summary,
|
|
94
|
+
parentIds: chunk.members.map(row => String(row.id)),
|
|
95
|
+
})),
|
|
96
|
+
...item.rows.flatMap((row, position) => fallback.has(String(row.id))
|
|
97
|
+
? [{ position, text: row.content, parentIds: [String(row.id)] }] : []),
|
|
98
|
+
].sort((a, b) => a.position - b.position)
|
|
99
|
+
const parentIds = units.flatMap(unit => unit.parentIds)
|
|
100
|
+
assert.equal(new Set(parentIds).size, parentIds.length)
|
|
101
|
+
assert.deepEqual(parentIds.slice().sort(), item.rows.map(row => String(row.id)).sort())
|
|
102
|
+
const text = units.map(unit => unit.text).join('\n')
|
|
103
|
+
const outputTokens = estimateTokens(text)
|
|
104
|
+
if (result.compression.used) assert.ok(outputTokens < inputTokens)
|
|
105
|
+
const sourceIds = item.rows.flatMap(row => row.sourceIds || [String(row.id)])
|
|
106
|
+
assert.equal(new Set(sourceIds).size, sourceIds.length)
|
|
107
|
+
assert.equal(result.stats.verificationCalls, 0)
|
|
108
|
+
assert.equal(result.stats.retries, 0)
|
|
109
|
+
outcomes.push({
|
|
110
|
+
name: item.name, applied: result.compression.used, targetMet: result.compression.targetMet,
|
|
111
|
+
inputChunks: item.rows.length, outputChunks: result.chunks.length, fallbackChunks: fallback.size,
|
|
112
|
+
inputTokens, outputTokens, reductionPercent: 100 * (inputTokens - outputTokens) / inputTokens,
|
|
113
|
+
stats: result.stats, compression: result.compression, errors: result.invalidChunks,
|
|
114
|
+
parentCoverage: parentIds.length, sourceRowCoverage: sourceIds.length,
|
|
115
|
+
preservedRawIds: item.preservedRawIds || [], reviewCriteria: item.reviewCriteria || [],
|
|
116
|
+
text, calls,
|
|
117
|
+
})
|
|
118
|
+
if (result.invalidChunks.some(error => error.reason === 'llm_error')) blocked = true
|
|
119
|
+
}
|
|
120
|
+
assert.equal(createHash('sha256').update(await readFile(values.input, 'utf8')).digest('hex'), sourceHash)
|
|
121
|
+
assert.deepEqual(working, source)
|
|
122
|
+
const report = {
|
|
123
|
+
sourceHash, sourceUnchanged: true, runtime: 'source', route: request.preset, rolePolicyHash, outcomes, blocked,
|
|
124
|
+
allSizeTargetsMet: outcomes.length > 0 && outcomes.every(outcome => outcome.targetMet === true),
|
|
125
|
+
scope: 'Single-call lossy second-layer compression with a soft half-size writing target. Reports time and approximate savings, not semantic equivalence. No AI verification, quality rejection, retry, live DB update or automatic compaction deployment.',
|
|
126
|
+
}
|
|
127
|
+
await writeFile(join(directory, 'report.json'), JSON.stringify(report, null, 2), { flag: 'wx' })
|
|
128
|
+
console.log(JSON.stringify({
|
|
129
|
+
directory, sourceUnchanged: report.sourceUnchanged, route: report.route,
|
|
130
|
+
allSizeTargetsMet: report.allSizeTargetsMet, blocked,
|
|
131
|
+
outcomes: outcomes.map(({ calls, reviewCriteria, ...outcome }) => outcome),
|
|
132
|
+
}, null, 2))
|
|
133
|
+
process.exitCode = blocked ? 1 : 0
|
|
@@ -4,6 +4,7 @@ import { initProviders, getAllProviders } from '../src/runtime/agent/orchestrato
|
|
|
4
4
|
import {
|
|
5
5
|
loadModelsDevCatalog,
|
|
6
6
|
warmModelMetadataCatalogs,
|
|
7
|
+
auditModelPricing,
|
|
7
8
|
} from '../src/runtime/agent/orchestrator/providers/model-catalog.mjs';
|
|
8
9
|
import { QUICK_WEB_SEARCH_MODELS } from '../src/session-runtime/quick-web-search-models.mjs';
|
|
9
10
|
|
|
@@ -151,6 +152,7 @@ for (const [name, provider] of getAllProviders()) {
|
|
|
151
152
|
error: listed.error,
|
|
152
153
|
count: listed.models.length,
|
|
153
154
|
rows,
|
|
155
|
+
pricing: auditModelPricing(listed.models, name),
|
|
154
156
|
});
|
|
155
157
|
}
|
|
156
158
|
|
|
@@ -178,6 +180,7 @@ const report = {
|
|
|
178
180
|
generatedAt: new Date().toISOString(),
|
|
179
181
|
enabledProviders: Object.entries(cfg.providers || {}).filter(([, v]) => v?.enabled).map(([k]) => k),
|
|
180
182
|
providerResults,
|
|
183
|
+
unpricedRoutes: providerResults.flatMap((p) => p.pricing.filter((row) => !row.priced)),
|
|
181
184
|
mismatches,
|
|
182
185
|
exactOneM,
|
|
183
186
|
nearOneM,
|
|
@@ -198,6 +201,9 @@ if (json) {
|
|
|
198
201
|
].filter(Boolean).join('; ');
|
|
199
202
|
console.log(` ${r.id}: ${r.liveContextLabel} ctx, out=${r.liveOutput ?? '-'}${flags ? ` [${flags}]` : ''}`);
|
|
200
203
|
}
|
|
204
|
+
for (const row of p.pricing.filter((r) => !r.priced)) {
|
|
205
|
+
console.log(` UNPRICED ${row.model} -> ${row.pricingProvider}/${row.pricingModel}: ${row.missingRates.join(', ')}`);
|
|
206
|
+
}
|
|
201
207
|
}
|
|
202
208
|
console.log(`\nsummary: mismatches=${mismatches.length}, exact1M=${exactOneM.length}, near1MNotExact=${nearOneM.length}, staticContextRows=${quickRows.length}`);
|
|
203
209
|
if (mismatches.length) {
|
|
@@ -27,6 +27,13 @@ const RELEASE_ROOT = 'https://github.com/tribgames/mixdog/releases/download'
|
|
|
27
27
|
const TOOLS_DIR = new URL('../src/runtime/agent/orchestrator/tools/', import.meta.url)
|
|
28
28
|
|
|
29
29
|
export const NATIVE_TOOL_KINDS = Object.freeze(['graph', 'patch', 'spawn'])
|
|
30
|
+
export const NATIVE_TOOL_PLATFORM_KEYS = Object.freeze([
|
|
31
|
+
'darwin-arm64',
|
|
32
|
+
'darwin-x64',
|
|
33
|
+
'linux-arm64',
|
|
34
|
+
'linux-x64',
|
|
35
|
+
'win32-x64',
|
|
36
|
+
])
|
|
30
37
|
|
|
31
38
|
/** Released asset name. */
|
|
32
39
|
export function nativeToolAssetName(kind, target) {
|
|
@@ -43,6 +50,13 @@ export function nativeToolInstalledName(kind, target) {
|
|
|
43
50
|
return target.platform === 'win32' ? `mixdog-${kind}.exe` : `mixdog-${kind}`
|
|
44
51
|
}
|
|
45
52
|
|
|
53
|
+
export function nativeToolPlatformAssets(kind) {
|
|
54
|
+
return Object.fromEntries(NATIVE_TOOL_PLATFORM_KEYS.map((key) => {
|
|
55
|
+
const [platform, arch] = key.split('-')
|
|
56
|
+
return [key, nativeToolAssetName(kind, { platform, arch })]
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
|
|
46
60
|
async function sha256File(path) {
|
|
47
61
|
return createHash('sha256').update(await readFile(path)).digest('hex')
|
|
48
62
|
}
|
|
@@ -18,16 +18,11 @@ import {
|
|
|
18
18
|
import { ensureGraphBinary } from '../src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs';
|
|
19
19
|
import { ensurePatchBinary } from '../src/runtime/agent/orchestrator/tools/patch-binary-fetcher.mjs';
|
|
20
20
|
import { ensureSpawnBinary } from '../src/runtime/agent/orchestrator/tools/spawn-binary-fetcher.mjs';
|
|
21
|
+
import { NATIVE_TOOL_PLATFORM_KEYS } from './native-tool-download.mjs';
|
|
21
22
|
|
|
22
23
|
const DEFAULT_PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
23
24
|
|
|
24
|
-
export const NATIVE_ASSET_PLATFORMS =
|
|
25
|
-
'darwin-arm64',
|
|
26
|
-
'darwin-x64',
|
|
27
|
-
'linux-arm64',
|
|
28
|
-
'linux-x64',
|
|
29
|
-
'win32-x64',
|
|
30
|
-
]);
|
|
25
|
+
export const NATIVE_ASSET_PLATFORMS = NATIVE_TOOL_PLATFORM_KEYS;
|
|
31
26
|
|
|
32
27
|
export const REQUIRED_NATIVE_INSTALLERS = Object.freeze({
|
|
33
28
|
graph: ensureGraphBinary,
|
|
@@ -104,8 +104,13 @@ function normalizeRowCategory(row) {
|
|
|
104
104
|
let category = storedCategory;
|
|
105
105
|
if (isKnownTestFixture(row)) {
|
|
106
106
|
category = 'expected-test';
|
|
107
|
-
} else
|
|
108
|
-
const derived = classifyToolFailure(rowErrorText(row),
|
|
107
|
+
} else {
|
|
108
|
+
const derived = classifyToolFailure(rowErrorText(row), rowTool(row));
|
|
109
|
+
// The shell classifier's generic command-exit fallback cannot recover
|
|
110
|
+
// execution metadata from a short historical preview such as "SIGKILL".
|
|
111
|
+
// Keep recorded signals, timeouts and capture failures instead of hiding
|
|
112
|
+
// them among ordinary command exits.
|
|
113
|
+
if (derived === 'command-exit' && storedCategory !== 'command-exit') return row;
|
|
109
114
|
// Failure previews are bounded and may end before the nested cause. Never
|
|
110
115
|
// downgrade a stored specific category to the generic fallback merely
|
|
111
116
|
// because the historical preview lacks that tail.
|