@retaw106/mmc-oc 1.2.3 → 2.0.0
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/dist/cli.js +7095 -0
- package/dist/config/index.d.ts +0 -12
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/legacy-harness-blocks.d.ts +27 -0
- package/dist/config/legacy-harness-blocks.d.ts.map +1 -0
- package/dist/config/live-run-config.d.ts.map +1 -1
- package/dist/config/live-snapshot.d.ts +2 -1
- package/dist/config/live-snapshot.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +4 -5
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/raw-loader.d.ts +7 -26
- package/dist/config/raw-loader.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +164 -259
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/variable.d.ts +8 -0
- package/dist/config/variable.d.ts.map +1 -1
- package/dist/config-init.js +5595 -0
- package/dist/{context-a6px04s0.js → context-95zkksn3.js} +2471 -2438
- package/dist/{derive-budgets-fhk00zv8.js → derive-budgets-rjjz5f7y.js} +2 -2
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts +2 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migration-v3-perf-schema.d.ts +8 -0
- package/dist/features/magic-context/migration-v3-perf-schema.d.ts.map +1 -0
- package/dist/features/magic-context/migration-v4-temporal-decisions.d.ts +12 -0
- package/dist/features/magic-context/migration-v4-temporal-decisions.d.ts.map +1 -0
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +6 -0
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/reclaim-protection.d.ts +15 -10
- package/dist/features/magic-context/reclaim-protection.d.ts.map +1 -1
- package/dist/features/magic-context/session-cache-ttl.d.ts +4 -2
- package/dist/features/magic-context/session-cache-ttl.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-historian-runs.d.ts +2 -0
- package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
- package/dist/features/magic-context/storage-ops.d.ts +2 -0
- package/dist/features/magic-context/storage-ops.d.ts.map +1 -1
- package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +15 -2
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/temporal-decisions.d.ts +10 -0
- package/dist/features/magic-context/temporal-decisions.d.ts.map +1 -0
- package/dist/features/magic-context/tool-definition-tokens.d.ts +3 -0
- package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/active-anthropic-turn.d.ts +8 -0
- package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -0
- package/dist/hooks/magic-context/apply-operations.d.ts +15 -0
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-deadline.d.ts +10 -3
- package/dist/hooks/magic-context/auto-search-deadline.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-cycle.d.ts +2 -0
- package/dist/hooks/magic-context/channel2-cycle.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts +8 -0
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts +3 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +12 -0
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
- package/dist/hooks/magic-context/decision-calibration.d.ts +3 -0
- package/dist/hooks/magic-context/decision-calibration.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-drop.d.ts +2 -0
- package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts +21 -1
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +30 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -0
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +10 -8
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/latest-assistant-turn.d.ts +25 -0
- package/dist/hooks/magic-context/latest-assistant-turn.d.ts.map +1 -0
- package/dist/hooks/magic-context/lkg-measured-request.d.ts +69 -0
- package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts +3 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/reasoning-budget-status.d.ts +15 -0
- package/dist/hooks/magic-context/reasoning-budget-status.d.ts.map +1 -0
- package/dist/hooks/magic-context/reasoning-budget.d.ts +40 -0
- package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -0
- package/dist/hooks/magic-context/reasoning-removal.d.ts +6 -6
- package/dist/hooks/magic-context/reasoning-removal.d.ts.map +1 -1
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts +2 -0
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
- package/dist/hooks/magic-context/sentinel.d.ts +2 -0
- package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +7 -3
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/supersession-reclaim.d.ts +4 -0
- package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +13 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +5 -0
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-awareness.d.ts +6 -3
- package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-served-projection.d.ts +4 -0
- package/dist/hooks/magic-context/temporal-served-projection.d.ts.map +1 -0
- package/dist/hooks/magic-context/tool-reclaim.d.ts +2 -0
- package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +34 -12
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +11 -5
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
- package/dist/{index-wgt4gamw.js → index-1mjhzqq7.js} +422 -31
- package/dist/index-33gprt9g.js +30 -0
- package/dist/{index-zx63j2e2.js → index-5cep2r9n.js} +3 -2
- package/dist/{index-by6h2pav.js → index-bdt9my9y.js} +374 -239
- package/dist/{index-3wagyrnj.js → index-x37vg474.js} +5 -1
- package/dist/{index-1vs6anwz.js → index-xt7y10x6.js} +2 -1
- package/dist/{index-me3zcj5t.js → index-z2g20msd.js} +4 -8
- package/dist/index.js +1 -1
- package/dist/{message-time-backfill-5ewk0axe.js → message-time-backfill-zfernxbw.js} +1 -1
- package/dist/{model-resolution-5haaxnpt.js → model-resolution-wazv26n0.js} +1 -1
- package/dist/plugin/rpc-handlers.d.ts +4 -0
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/{read-session-chunk-apjwrttc.js → read-session-chunk-hp464g13.js} +2 -2
- package/dist/{recomp-orchestrator-gyeevxaw.js → recomp-orchestrator-3qxeqwb9.js} +6 -6
- package/dist/shared/auto-search-lifecycle.d.ts +7 -0
- package/dist/shared/auto-search-lifecycle.d.ts.map +1 -0
- package/dist/shared/cache-ttl-display.d.ts +6 -4
- package/dist/shared/cache-ttl-display.d.ts.map +1 -1
- package/dist/shared/model-cache-ttl.d.ts +1 -1
- package/dist/shared/model-cache-ttl.d.ts.map +1 -1
- package/dist/shared/model-resolution.d.ts +5 -2
- package/dist/shared/model-resolution.d.ts.map +1 -1
- package/dist/shared/model-suggestion-retry.d.ts +2 -0
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/protected-tools-policy.d.ts +6 -0
- package/dist/shared/protected-tools-policy.d.ts.map +1 -0
- package/dist/shared/provider-output-failure.d.ts +19 -0
- package/dist/shared/provider-output-failure.d.ts.map +1 -0
- package/dist/shared/temporal-upgrade-fixture.d.ts +19 -0
- package/dist/shared/temporal-upgrade-fixture.d.ts.map +1 -0
- package/dist/shared/window-geometry.d.ts +4 -0
- package/dist/shared/window-geometry.d.ts.map +1 -1
- package/dist/shared/write-transaction-timing.d.ts +8 -1
- package/dist/shared/write-transaction-timing.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/tools.d.ts +2 -0
- package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
- package/dist/v2/hidden-child-native.d.ts +7 -0
- package/dist/v2/hidden-child-native.d.ts.map +1 -1
- package/dist/v2/hidden-child-record.d.ts +13 -0
- package/dist/v2/hidden-child-record.d.ts.map +1 -1
- package/dist/v2/hidden-completion.d.ts.map +1 -1
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/hidden-child.d.ts +46 -2
- package/dist/v2/hooks/hidden-child.d.ts.map +1 -1
- package/dist/v2/hooks/payload.d.ts.map +1 -1
- package/dist/v2/hooks/store.d.ts.map +1 -1
- package/dist/v2/hooks/tools.d.ts.map +1 -1
- package/dist/v2/hooks/types.d.ts +8 -0
- package/dist/v2/hooks/types.d.ts.map +1 -1
- package/dist/v2/server.js +4540 -3889
- package/dist/v2/tool-result.d.ts +8 -0
- package/dist/v2/tool-result.d.ts.map +1 -0
- package/dist/web/api.d.ts +6 -0
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/context-store.d.ts +13 -0
- package/dist/web/context-store.d.ts.map +1 -1
- package/dist/web/display-path.d.ts +28 -0
- package/dist/web/display-path.d.ts.map +1 -0
- package/dist/web/entry-args.d.ts +33 -0
- package/dist/web/entry-args.d.ts.map +1 -0
- package/dist/web/host-pi.d.ts +2 -0
- package/dist/web/host-pi.d.ts.map +1 -1
- package/dist/web/main.d.ts +4 -6
- package/dist/web/main.d.ts.map +1 -1
- package/dist/web/main.js +772 -77
- package/dist/web/message-stats.d.ts +45 -0
- package/dist/web/message-stats.d.ts.map +1 -0
- package/dist/web/ui/index.html +1 -0
- package/dist/web/ui/panel.css +5 -0
- package/dist/web/ui/panel.js +220 -70
- package/dist/web/views.d.ts +23 -0
- package/dist/web/views.d.ts.map +1 -1
- package/dist/{wrapup-orchestrator-z9sqpqjf.js → wrapup-orchestrator-wgb2nd19.js} +7 -6
- package/package.json +7 -2
- package/src/config/index.test.ts +257 -126
- package/src/config/index.ts +45 -39
- package/src/config/legacy-harness-blocks.test.ts +78 -0
- package/src/config/legacy-harness-blocks.ts +71 -0
- package/src/config/live-run-config.test.ts +49 -5
- package/src/config/live-run-config.ts +1 -0
- package/src/config/live-snapshot.ts +5 -1
- package/src/config/profiles.ts +1 -1
- package/src/config/project-config-trust-boundary.test.ts +2 -2
- package/src/config/project-security.hostile-matrix.test.ts +51 -65
- package/src/config/project-security.test.ts +6 -1
- package/src/config/project-security.ts +13 -12
- package/src/config/protected-tokens.test.ts +1 -1
- package/src/config/raw-loader.test.ts +29 -352
- package/src/config/raw-loader.ts +17 -406
- package/src/config/removed-config-keys.test.ts +1 -1
- package/src/config/schema/magic-context.test.ts +60 -64
- package/src/config/schema/magic-context.ts +105 -137
- package/src/config/variable.ts +21 -12
- package/src/shared/auto-search-lifecycle.ts +27 -0
- package/src/shared/cache-ttl-display.test.ts +1 -1
- package/src/shared/cache-ttl-display.ts +17 -16
- package/src/shared/model-cache-ttl.test.ts +2 -2
- package/src/shared/model-cache-ttl.ts +8 -4
- package/src/shared/model-resolution.test.ts +22 -32
- package/src/shared/model-resolution.ts +13 -18
- package/src/shared/model-suggestion-retry.ts +63 -7
- package/src/shared/protected-tools-policy.ts +25 -0
- package/src/shared/provider-output-failure.ts +176 -0
- package/src/shared/status-detail-text.test.ts +1 -1
- package/src/shared/status-view.test.ts +1 -1
- package/src/shared/temporal-upgrade-fixture.ts +50 -0
- package/src/shared/window-geometry.ts +10 -0
- package/src/shared/write-transaction-timing.ts +25 -1
- package/dist/features/magic-context/window-report-ledger.d.ts +0 -70
- package/dist/features/magic-context/window-report-ledger.d.ts.map +0 -1
- package/dist/index-e1q8002h.js +0 -16
- package/src/config/misplaced-agent-model.test.ts +0 -55
package/dist/web/main.js
CHANGED
|
@@ -426,6 +426,9 @@ ${data.stack}` : ""}`)}` : ` ${JSON.stringify(sanitizeConfigValue(data))}`;
|
|
|
426
426
|
}
|
|
427
427
|
} catch {}
|
|
428
428
|
}
|
|
429
|
+
function sessionLog(sessionId, message, data) {
|
|
430
|
+
log(`[magic-context][${sessionId}] ${message}`, data);
|
|
431
|
+
}
|
|
429
432
|
var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, MAX_BUFFERED_BYTES, bufferedBytes = 0, droppedLines = 0, MAX_LOG_FILE_BYTES, SIZE_CHECK_INTERVAL_FLUSHES = 64, activeLogFile = null, activeLogSize = null, flushesSinceSizeCheck = 0, swallowedWriteCount = 0, lastErrorMessage = null, lastErrorTime = null;
|
|
430
433
|
var init_logger = __esm(() => {
|
|
431
434
|
init_data_path();
|
|
@@ -822,12 +825,13 @@ function isKnownAutocommit(db) {
|
|
|
822
825
|
}
|
|
823
826
|
|
|
824
827
|
// src/shared/write-transaction-timing.ts
|
|
825
|
-
function logSlowWriteTransaction(site, startedAt, thresholdMs = SLOW_WRITE_TRANSACTION_THRESHOLD_MS, completedAtMs = performance.now()) {
|
|
828
|
+
function logSlowWriteTransaction(site, startedAt, thresholdMs = SLOW_WRITE_TRANSACTION_THRESHOLD_MS, completedAtMs = performance.now(), steps) {
|
|
826
829
|
try {
|
|
827
830
|
const durationMs = completedAtMs - startedAt;
|
|
828
831
|
if (durationMs < thresholdMs)
|
|
829
832
|
return;
|
|
830
|
-
|
|
833
|
+
const detail = steps ? Object.entries(steps).map(([name, ms]) => `${name}=${ms.toFixed(1)}ms`).join(" ") : "";
|
|
834
|
+
log(`[magic-context] slow write transaction: site=${site} held=${durationMs.toFixed(1)}ms${detail ? ` ${detail}` : ""}`);
|
|
831
835
|
} catch {}
|
|
832
836
|
}
|
|
833
837
|
var SLOW_WRITE_TRANSACTION_THRESHOLD_MS = 1000;
|
|
@@ -835,6 +839,35 @@ var init_write_transaction_timing = __esm(() => {
|
|
|
835
839
|
init_logger();
|
|
836
840
|
});
|
|
837
841
|
|
|
842
|
+
// src/features/magic-context/migration-v3-perf-schema.ts
|
|
843
|
+
function installV3PerfSchema(db) {
|
|
844
|
+
const schema = new Map(db.prepare("SELECT name, sql FROM sqlite_master").all().map((row) => [row.name, row.sql]));
|
|
845
|
+
for (const [table, name, columns] of indexes) {
|
|
846
|
+
if (schema.has(table) && !schema.has(name))
|
|
847
|
+
db.exec(`CREATE INDEX ${name} ON ${table}(${columns})`);
|
|
848
|
+
}
|
|
849
|
+
for (const name of V3_REDUNDANT_INDEXES) {
|
|
850
|
+
if (schema.has(name))
|
|
851
|
+
db.exec(`DROP INDEX ${name}`);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
var V3_REDUNDANT_INDEXES, indexes, tagOrderIndexes;
|
|
855
|
+
var init_migration_v3_perf_schema = __esm(() => {
|
|
856
|
+
V3_REDUNDANT_INDEXES = [
|
|
857
|
+
"idx_tags_session_tag_number",
|
|
858
|
+
"idx_compartments_session",
|
|
859
|
+
"idx_pending_ops_session",
|
|
860
|
+
"idx_source_contents_session",
|
|
861
|
+
"idx_compression_depth_session",
|
|
862
|
+
"idx_transform_decisions_session_harness",
|
|
863
|
+
"idx_project_key_files_project"
|
|
864
|
+
];
|
|
865
|
+
indexes = [
|
|
866
|
+
["message_fts_rowid_map", "idx_message_fts_rowid_map_session_rowid", "session_id, fts_rowid"]
|
|
867
|
+
];
|
|
868
|
+
tagOrderIndexes = new WeakMap;
|
|
869
|
+
});
|
|
870
|
+
|
|
838
871
|
// src/shared/commit-detection.ts
|
|
839
872
|
var HASH_HEX = "[0-9a-f]{7,12}", COMMIT_HASH_TEST_PATTERN;
|
|
840
873
|
var init_commit_detection = __esm(() => {
|
|
@@ -1010,6 +1043,17 @@ var init_opencode_db_path = __esm(() => {
|
|
|
1010
1043
|
claimedDiagnostics = new Set;
|
|
1011
1044
|
});
|
|
1012
1045
|
|
|
1046
|
+
// src/hooks/magic-context/host-served-rows.ts
|
|
1047
|
+
function markHostUnservedRow(target) {
|
|
1048
|
+
Object.defineProperty(target, HOST_UNSERVED_ROW, {
|
|
1049
|
+
value: true,
|
|
1050
|
+
enumerable: false,
|
|
1051
|
+
configurable: true
|
|
1052
|
+
});
|
|
1053
|
+
return target;
|
|
1054
|
+
}
|
|
1055
|
+
var HOST_UNSERVED_ROW = "hostUnservedRow";
|
|
1056
|
+
|
|
1013
1057
|
// src/features/magic-context/storage-db.ts
|
|
1014
1058
|
import { basename, dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
|
|
1015
1059
|
init_data_path();
|
|
@@ -1249,9 +1293,69 @@ function splitReplayDecisions(db) {
|
|
|
1249
1293
|
db.exec("DROP TABLE temp.v2_replay_documents");
|
|
1250
1294
|
}
|
|
1251
1295
|
|
|
1296
|
+
// src/features/magic-context/storage-db.ts
|
|
1297
|
+
init_migration_v3_perf_schema();
|
|
1298
|
+
|
|
1299
|
+
// src/features/magic-context/migration-v4-temporal-decisions.ts
|
|
1300
|
+
init_logger();
|
|
1301
|
+
|
|
1302
|
+
// src/features/magic-context/temporal-decisions.ts
|
|
1303
|
+
var PREFIX = "temporal-message-v1:";
|
|
1304
|
+
function decodeTemporalDecision(entry) {
|
|
1305
|
+
if (!entry.startsWith(PREFIX))
|
|
1306
|
+
return null;
|
|
1307
|
+
try {
|
|
1308
|
+
const pair = JSON.parse(entry.slice(PREFIX.length));
|
|
1309
|
+
if (Array.isArray(pair) && pair.length === 2 && typeof pair[0] === "string" && pair[0] && typeof pair[1] === "string")
|
|
1310
|
+
return [pair[0], pair[1]];
|
|
1311
|
+
} catch {}
|
|
1312
|
+
return null;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/features/magic-context/migration-v4-temporal-decisions.ts
|
|
1316
|
+
var TEMPORAL_DECISIONS_DDL = `CREATE TABLE IF NOT EXISTS temporal_decisions (
|
|
1317
|
+
session_id TEXT NOT NULL,
|
|
1318
|
+
message_id TEXT NOT NULL,
|
|
1319
|
+
marker TEXT,
|
|
1320
|
+
PRIMARY KEY (session_id, message_id)
|
|
1321
|
+
) WITHOUT ROWID`;
|
|
1322
|
+
function installV4TemporalDecisions(db) {
|
|
1323
|
+
const schema = new Map(db.prepare("SELECT name FROM sqlite_master").all().map((row) => [row.name, true]));
|
|
1324
|
+
if (!schema.has("temporal_decisions")) {
|
|
1325
|
+
db.exec(TEMPORAL_DECISIONS_DDL);
|
|
1326
|
+
if (schema.has("session_meta")) {
|
|
1327
|
+
const rows = db.prepare("SELECT session_id, merged_reasoning_stripped_ids AS entries FROM session_meta WHERE merged_reasoning_stripped_ids LIKE '%temporal-message-v1:%'").all();
|
|
1328
|
+
const insert = db.prepare("INSERT OR IGNORE INTO temporal_decisions(session_id,message_id,marker) VALUES (?,?,?)");
|
|
1329
|
+
for (const row of rows) {
|
|
1330
|
+
let entries;
|
|
1331
|
+
try {
|
|
1332
|
+
entries = JSON.parse(row.entries);
|
|
1333
|
+
} catch {
|
|
1334
|
+
sessionLog(row.session_id, "v4 temporal metadata: malformed JSON skipped; original blob retained");
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
if (!Array.isArray(entries)) {
|
|
1338
|
+
sessionLog(row.session_id, "v4 temporal metadata: non-array JSON skipped; original blob retained");
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
const kept = [];
|
|
1342
|
+
for (const entry of entries) {
|
|
1343
|
+
const decision = typeof entry === "string" ? decodeTemporalDecision(entry) : null;
|
|
1344
|
+
if (decision)
|
|
1345
|
+
insert.run(row.session_id, ...decision);
|
|
1346
|
+
else
|
|
1347
|
+
kept.push(entry);
|
|
1348
|
+
}
|
|
1349
|
+
db.prepare("UPDATE session_meta SET merged_reasoning_stripped_ids=? WHERE session_id=?").run(JSON.stringify(kept), row.session_id);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1252
1355
|
// src/features/magic-context/migrations.ts
|
|
1253
1356
|
init_logger();
|
|
1254
1357
|
init_write_transaction_timing();
|
|
1358
|
+
init_migration_v3_perf_schema();
|
|
1255
1359
|
var FORK_MIGRATION_VERSION_FLOOR = 1e4;
|
|
1256
1360
|
class MigrationLockBusyError extends Error {
|
|
1257
1361
|
constructor(message) {
|
|
@@ -1280,6 +1384,20 @@ var MIGRATIONS = [
|
|
|
1280
1384
|
splitLkgSlotPrefixes(db);
|
|
1281
1385
|
splitReplayDecisions(db);
|
|
1282
1386
|
}
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
version: 3,
|
|
1390
|
+
description: "drop redundant single-column session indexes; add FTS rowid-map lookup",
|
|
1391
|
+
up(db) {
|
|
1392
|
+
installV3PerfSchema(db);
|
|
1393
|
+
}
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
version: 4,
|
|
1397
|
+
description: "store temporal replay decisions per message identity",
|
|
1398
|
+
up(db) {
|
|
1399
|
+
installV4TemporalDecisions(db);
|
|
1400
|
+
}
|
|
1283
1401
|
}
|
|
1284
1402
|
];
|
|
1285
1403
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -1437,7 +1555,7 @@ var pendingAsyncOpens = new Map;
|
|
|
1437
1555
|
var persistenceByDatabase = new WeakMap;
|
|
1438
1556
|
var persistenceErrorByDatabase = new WeakMap;
|
|
1439
1557
|
var pathByDatabase = new WeakMap;
|
|
1440
|
-
var LATEST_SUPPORTED_VERSION =
|
|
1558
|
+
var LATEST_SUPPORTED_VERSION = 4;
|
|
1441
1559
|
var BOOT_SQLITE_BUSY_TIMEOUT_MS = 5000;
|
|
1442
1560
|
function resolveBootBusyTimeoutMs(value) {
|
|
1443
1561
|
if (value === undefined)
|
|
@@ -1534,7 +1652,6 @@ function initializeDatabase(db, busyTimeoutMs = BOOT_SQLITE_BUSY_TIMEOUT_MS) {
|
|
|
1534
1652
|
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
1535
1653
|
UNIQUE(session_id, sequence)
|
|
1536
1654
|
);
|
|
1537
|
-
CREATE INDEX IF NOT EXISTS idx_compartments_session ON compartments(session_id);
|
|
1538
1655
|
|
|
1539
1656
|
CREATE TABLE IF NOT EXISTS compartment_chunk_embeddings (
|
|
1540
1657
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -1595,7 +1712,6 @@ function initializeDatabase(db, busyTimeoutMs = BOOT_SQLITE_BUSY_TIMEOUT_MS) {
|
|
|
1595
1712
|
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
1596
1713
|
PRIMARY KEY(session_id, message_ordinal)
|
|
1597
1714
|
);
|
|
1598
|
-
CREATE INDEX IF NOT EXISTS idx_compression_depth_session ON compression_depth(session_id);
|
|
1599
1715
|
|
|
1600
1716
|
CREATE TABLE IF NOT EXISTS session_facts (
|
|
1601
1717
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -2114,7 +2230,6 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
2114
2230
|
stale_reason TEXT,
|
|
2115
2231
|
PRIMARY KEY (project_path, path)
|
|
2116
2232
|
);
|
|
2117
|
-
CREATE INDEX IF NOT EXISTS idx_project_key_files_project ON project_key_files(project_path);
|
|
2118
2233
|
CREATE INDEX IF NOT EXISTS idx_project_key_files_generated_at ON project_key_files(project_path, generated_at);
|
|
2119
2234
|
|
|
2120
2235
|
CREATE TABLE IF NOT EXISTS project_key_files_version (
|
|
@@ -2474,10 +2589,6 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
2474
2589
|
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
2475
2590
|
PRIMARY KEY (session_id, harness, message_id)
|
|
2476
2591
|
);
|
|
2477
|
-
CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
|
|
2478
|
-
ON transform_decisions(session_id, harness);
|
|
2479
|
-
|
|
2480
|
-
CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
|
|
2481
2592
|
CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
|
|
2482
2593
|
|
|
2483
2594
|
-- Clone/import paths can write tags before session bootstrap. Keep trigger-created
|
|
@@ -2523,10 +2634,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
2523
2634
|
WHERE NEW.session_id != OLD.session_id
|
|
2524
2635
|
ON CONFLICT(session_id) DO UPDATE SET tags_version = tags_version + 1;
|
|
2525
2636
|
END;
|
|
2526
|
-
CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
|
|
2527
2637
|
CREATE INDEX IF NOT EXISTS idx_pending_ops_session_tag_id ON pending_ops(session_id, tag_id);
|
|
2528
|
-
CREATE INDEX IF NOT EXISTS idx_source_contents_session ON source_contents(session_id);
|
|
2529
|
-
|
|
2530
2638
|
CREATE TABLE IF NOT EXISTS recomp_compartments (
|
|
2531
2639
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
2532
2640
|
session_id TEXT NOT NULL,
|
|
@@ -2871,8 +2979,6 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
2871
2979
|
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
2872
2980
|
PRIMARY KEY (session_id, harness, message_id)
|
|
2873
2981
|
);
|
|
2874
|
-
CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
|
|
2875
|
-
ON transform_decisions(session_id, harness);
|
|
2876
2982
|
`);
|
|
2877
2983
|
ensureColumn(db, "transform_decisions", "system_hash_prev", "TEXT");
|
|
2878
2984
|
ensureColumn(db, "transform_decisions", "system_hash_new", "TEXT");
|
|
@@ -2908,6 +3014,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
2908
3014
|
ON tags(session_id, message_id)
|
|
2909
3015
|
WHERE type = 'tool' AND tool_owner_message_id IS NULL;
|
|
2910
3016
|
`);
|
|
3017
|
+
installV3PerfSchema(db);
|
|
3018
|
+
installV4TemporalDecisions(db);
|
|
2911
3019
|
}
|
|
2912
3020
|
var CHANNEL2_CLAIM_TTL_MS = 10 * 60000;
|
|
2913
3021
|
var channel2HealStatements = new WeakMap;
|
|
@@ -2999,6 +3107,20 @@ function listWebProjects(store) {
|
|
|
2999
3107
|
};
|
|
3000
3108
|
});
|
|
3001
3109
|
}
|
|
3110
|
+
function listProjectSessionRefs(store) {
|
|
3111
|
+
return store.db.prepare(`SELECT project_path, session_id, harness, MAX(updated_at) AS last_activity
|
|
3112
|
+
FROM session_projects
|
|
3113
|
+
GROUP BY project_path, session_id, harness
|
|
3114
|
+
ORDER BY project_path ASC, session_id ASC, harness ASC`).all().map((row) => {
|
|
3115
|
+
const r = row;
|
|
3116
|
+
return {
|
|
3117
|
+
projectPath: r.project_path,
|
|
3118
|
+
sessionId: r.session_id,
|
|
3119
|
+
harness: r.harness,
|
|
3120
|
+
lastActivityAt: typeof r.last_activity === "number" ? r.last_activity : null
|
|
3121
|
+
};
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3002
3124
|
function listProjectSessions(store, projectPath) {
|
|
3003
3125
|
return store.db.prepare(`SELECT sp.session_id AS session_id,
|
|
3004
3126
|
sp.harness AS harness,
|
|
@@ -3032,6 +3154,76 @@ function closeQuietly2(db) {
|
|
|
3032
3154
|
} catch {}
|
|
3033
3155
|
}
|
|
3034
3156
|
|
|
3157
|
+
// src/web/entry-args.ts
|
|
3158
|
+
function webEntryUsage() {
|
|
3159
|
+
return `用法:mmc web [参数]
|
|
3160
|
+
` + `
|
|
3161
|
+
` + `参数:
|
|
3162
|
+
` + ` --port <整数> 监听端口(0..65535,0 表示随机端口);默认 4180,
|
|
3163
|
+
` + ` 也可用环境变量 MMC_WEB_PORT 指定(非法值静默忽略)
|
|
3164
|
+
` + ` --smoke 打印数据探针 JSON 后立即退出,不起服务
|
|
3165
|
+
` + ` --fixture 用隔离的合成演示数据启动(不读真实数据)
|
|
3166
|
+
` + ` --fixture-root <目录> 指定演示数据根目录(幂等,可跨重启复用)
|
|
3167
|
+
` + ` -h, --help 打印本帮助并退出(不读库、不监听)
|
|
3168
|
+
` + `
|
|
3169
|
+
` + `未知参数、缺值或非法值一律拒绝(退出码 2);运行期失败退出码 1。
|
|
3170
|
+
`;
|
|
3171
|
+
}
|
|
3172
|
+
function parseWebEntryArgs(args, env = process.env) {
|
|
3173
|
+
let port;
|
|
3174
|
+
let fixtureRoot;
|
|
3175
|
+
let fixture = false;
|
|
3176
|
+
let smoke = false;
|
|
3177
|
+
for (let i = 0;i < args.length; i++) {
|
|
3178
|
+
const token = args[i];
|
|
3179
|
+
if (token === "-h" || token === "--help") {
|
|
3180
|
+
return { kind: "help" };
|
|
3181
|
+
}
|
|
3182
|
+
if (token === "--smoke") {
|
|
3183
|
+
smoke = true;
|
|
3184
|
+
continue;
|
|
3185
|
+
}
|
|
3186
|
+
if (token === "--fixture") {
|
|
3187
|
+
fixture = true;
|
|
3188
|
+
continue;
|
|
3189
|
+
}
|
|
3190
|
+
if (token === "--port" || token === "--fixture-root") {
|
|
3191
|
+
const value = args[i + 1];
|
|
3192
|
+
if (value === undefined || value.startsWith("-")) {
|
|
3193
|
+
return { kind: "usage-error", message: `参数 ${token} 缺少值。` };
|
|
3194
|
+
}
|
|
3195
|
+
if (token === "--port") {
|
|
3196
|
+
const parsed = Number(value);
|
|
3197
|
+
if (value === "" || !Number.isSafeInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
3198
|
+
return {
|
|
3199
|
+
kind: "usage-error",
|
|
3200
|
+
message: `参数 --port 的值非法:${value}(需要 0..65535 的整数)。`
|
|
3201
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
port = parsed;
|
|
3204
|
+
} else {
|
|
3205
|
+
fixtureRoot = value;
|
|
3206
|
+
}
|
|
3207
|
+
i++;
|
|
3208
|
+
continue;
|
|
3209
|
+
}
|
|
3210
|
+
return { kind: "usage-error", message: `未知参数:${token}` };
|
|
3211
|
+
}
|
|
3212
|
+
if (port === undefined && env.MMC_WEB_PORT) {
|
|
3213
|
+
const fromEnv = Number(env.MMC_WEB_PORT);
|
|
3214
|
+
if (Number.isSafeInteger(fromEnv) && fromEnv >= 0 && fromEnv <= 65535) {
|
|
3215
|
+
port = fromEnv;
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
return {
|
|
3219
|
+
kind: "ok",
|
|
3220
|
+
mode: smoke ? "smoke" : "serve",
|
|
3221
|
+
port,
|
|
3222
|
+
fixture,
|
|
3223
|
+
fixtureRoot
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3035
3227
|
// src/web/fixtures.ts
|
|
3036
3228
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, mkdtempSync, writeFileSync } from "node:fs";
|
|
3037
3229
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
@@ -3195,24 +3387,26 @@ function seedOc2Store(dbPath, generation) {
|
|
|
3195
3387
|
session.run(FIXTURE_SEED.oc2Historian, "/fixture/project", "Magic Context historian", null, null, T0 + 8000, T0 + 8500);
|
|
3196
3388
|
session.run(FIXTURE_SEED.oc2HostOnly, "/fixture/project", "fixture: 未注册的宿主会话", null, null, T0, T0 + 1000);
|
|
3197
3389
|
const message = db.prepare("INSERT INTO session_message (id, session_id, type, seq, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
3198
|
-
const part = (id, type, text) => ({ id, type, text });
|
|
3199
3390
|
message.run("msg_fixture_idle", FIXTURE_SEED.oc2Session, "idle", 0, T0, T0, JSON.stringify({ outcome: "succeeded" }));
|
|
3200
|
-
message.run(OC2_LINKED_START, FIXTURE_SEED.oc2Session, "user", 1, T0 + 100, T0 + 100, JSON.stringify({
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3391
|
+
message.run(OC2_LINKED_START, FIXTURE_SEED.oc2Session, "user", 1, T0 + 100, T0 + 100, JSON.stringify({ text: "fixture: 审阅 web 面板的数据层,先看项目列表。" }));
|
|
3392
|
+
message.run("msg_fixture_switch", FIXTURE_SEED.oc2Session, "agent-switched", 2, T0 + 200, T0 + 200, JSON.stringify({ agent: "fixer" }));
|
|
3393
|
+
message.run(OC2_LINKED_END, FIXTURE_SEED.oc2Session, "assistant", 3, T0 + 300, T0 + 300, JSON.stringify({
|
|
3394
|
+
content: [
|
|
3395
|
+
{ type: "text", text: "fixture: 项目枚举与池语义已核对,继续会话列表。" },
|
|
3396
|
+
{ type: "reasoning", text: "fixture: 先确认分页锚点,再看工具输出。" },
|
|
3206
3397
|
{
|
|
3207
|
-
id: "pt",
|
|
3208
3398
|
type: "tool",
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
state: {
|
|
3399
|
+
name: "read",
|
|
3400
|
+
id: "call-1",
|
|
3401
|
+
state: {
|
|
3402
|
+
status: "completed",
|
|
3403
|
+
input: { path: "views.ts" },
|
|
3404
|
+
content: [{ type: "text", text: LONG_OUTPUT }]
|
|
3405
|
+
}
|
|
3212
3406
|
}
|
|
3213
3407
|
]
|
|
3214
3408
|
}));
|
|
3215
|
-
message.run("msg_hist_1", FIXTURE_SEED.oc2Historian, "user", 1, T0 + 8100, T0 + 8100, JSON.stringify({
|
|
3409
|
+
message.run("msg_hist_1", FIXTURE_SEED.oc2Historian, "user", 1, T0 + 8100, T0 + 8100, JSON.stringify({ text: "fixture: historian chunk prompt" }));
|
|
3216
3410
|
} finally {
|
|
3217
3411
|
db.close();
|
|
3218
3412
|
}
|
|
@@ -3823,11 +4017,14 @@ import { createHash as createHash4, randomUUID, timingSafeEqual } from "node:cry
|
|
|
3823
4017
|
import { createReadStream, existsSync as existsSync8, statSync as statSync3 } from "node:fs";
|
|
3824
4018
|
import { createServer } from "node:http";
|
|
3825
4019
|
import { extname, normalize, resolve as resolve4 } from "node:path";
|
|
3826
|
-
import { fileURLToPath } from "node:url";
|
|
4020
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3827
4021
|
|
|
3828
4022
|
// src/web/api.ts
|
|
3829
4023
|
import { existsSync as existsSync6 } from "node:fs";
|
|
3830
4024
|
|
|
4025
|
+
// src/web/display-path.ts
|
|
4026
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
4027
|
+
|
|
3831
4028
|
// src/web/host-pi.ts
|
|
3832
4029
|
import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readdirSync as readdirSync2, readFileSync, readSync as readSync2 } from "node:fs";
|
|
3833
4030
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -3897,6 +4094,7 @@ function readPiSessionHeader(filePath, fileName, directoryName) {
|
|
|
3897
4094
|
directoryName,
|
|
3898
4095
|
sessionId: null,
|
|
3899
4096
|
headerTimestamp: null,
|
|
4097
|
+
headerCwd: null,
|
|
3900
4098
|
headerError: null
|
|
3901
4099
|
};
|
|
3902
4100
|
let firstLine;
|
|
@@ -3940,7 +4138,8 @@ function readPiSessionHeader(filePath, fileName, directoryName) {
|
|
|
3940
4138
|
return {
|
|
3941
4139
|
...base,
|
|
3942
4140
|
sessionId: record.id,
|
|
3943
|
-
headerTimestamp: typeof record.timestamp === "string" ? record.timestamp : null
|
|
4141
|
+
headerTimestamp: typeof record.timestamp === "string" ? record.timestamp : null,
|
|
4142
|
+
headerCwd: typeof record.cwd === "string" && record.cwd.length > 0 ? record.cwd : null
|
|
3944
4143
|
};
|
|
3945
4144
|
}
|
|
3946
4145
|
function readPiSessionFileEntries(filePath) {
|
|
@@ -4082,6 +4281,103 @@ function resolvePiBoundary(messageId, messages, synthIdAliases) {
|
|
|
4082
4281
|
return { state: "unresolved", messageId };
|
|
4083
4282
|
}
|
|
4084
4283
|
|
|
4284
|
+
// src/web/display-path.ts
|
|
4285
|
+
function resolveProjectDisplayPaths(sources, projects, sessions) {
|
|
4286
|
+
const oc2Directories = oc2DirectoryIndex(sources);
|
|
4287
|
+
const piDirectories = piDirectoryIndex(sources);
|
|
4288
|
+
const candidatesByProject = new Map;
|
|
4289
|
+
for (const session of sessions) {
|
|
4290
|
+
if (session.harness === "pi") {
|
|
4291
|
+
for (const directory of piDirectories.get(session.sessionId) ?? []) {
|
|
4292
|
+
pushCandidate(candidatesByProject, session.projectPath, {
|
|
4293
|
+
directory,
|
|
4294
|
+
activityAt: session.lastActivityAt,
|
|
4295
|
+
sessionId: session.sessionId
|
|
4296
|
+
});
|
|
4297
|
+
}
|
|
4298
|
+
continue;
|
|
4299
|
+
}
|
|
4300
|
+
const host = oc2Directories.get(session.sessionId);
|
|
4301
|
+
if (!host)
|
|
4302
|
+
continue;
|
|
4303
|
+
pushCandidate(candidatesByProject, session.projectPath, {
|
|
4304
|
+
directory: host.directory,
|
|
4305
|
+
activityAt: host.timeUpdated ?? session.lastActivityAt,
|
|
4306
|
+
sessionId: session.sessionId
|
|
4307
|
+
});
|
|
4308
|
+
}
|
|
4309
|
+
const resolved = new Map;
|
|
4310
|
+
for (const project of projects) {
|
|
4311
|
+
const candidates = candidatesByProject.get(project.projectPath);
|
|
4312
|
+
if (candidates && candidates.length > 0) {
|
|
4313
|
+
resolved.set(project.projectPath, pickBestCandidate(candidates).directory);
|
|
4314
|
+
continue;
|
|
4315
|
+
}
|
|
4316
|
+
resolved.set(project.projectPath, isAbsolute3(project.projectPath) ? project.projectPath : null);
|
|
4317
|
+
}
|
|
4318
|
+
return resolved;
|
|
4319
|
+
}
|
|
4320
|
+
function oc2DirectoryIndex(sources) {
|
|
4321
|
+
const index = new Map;
|
|
4322
|
+
try {
|
|
4323
|
+
const opened = openOc2HostStore(sources.oc2Env ?? process.env);
|
|
4324
|
+
if (!opened.store)
|
|
4325
|
+
return index;
|
|
4326
|
+
try {
|
|
4327
|
+
for (const meta of listOc2SessionMeta(opened.store)) {
|
|
4328
|
+
if (meta.directory && meta.directory.trim().length > 0) {
|
|
4329
|
+
index.set(meta.sessionId, {
|
|
4330
|
+
directory: meta.directory,
|
|
4331
|
+
timeUpdated: meta.timeUpdated
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
} catch {} finally {
|
|
4336
|
+
opened.store.close();
|
|
4337
|
+
}
|
|
4338
|
+
} catch {}
|
|
4339
|
+
return index;
|
|
4340
|
+
}
|
|
4341
|
+
function piDirectoryIndex(sources) {
|
|
4342
|
+
const index = new Map;
|
|
4343
|
+
try {
|
|
4344
|
+
const listing = listPiSessionFiles(sources.piSessionsRoot ?? piSessionsRoot());
|
|
4345
|
+
for (const group of listing.groups) {
|
|
4346
|
+
const directories = new Set;
|
|
4347
|
+
for (const file of group.files) {
|
|
4348
|
+
if (file.headerCwd && file.headerCwd.trim().length > 0) {
|
|
4349
|
+
directories.add(file.headerCwd);
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
if (directories.size > 0)
|
|
4353
|
+
index.set(group.sessionId, directories);
|
|
4354
|
+
}
|
|
4355
|
+
} catch {}
|
|
4356
|
+
return index;
|
|
4357
|
+
}
|
|
4358
|
+
function pushCandidate(byProject, projectPath, candidate) {
|
|
4359
|
+
const list = byProject.get(projectPath);
|
|
4360
|
+
if (list)
|
|
4361
|
+
list.push(candidate);
|
|
4362
|
+
else
|
|
4363
|
+
byProject.set(projectPath, [candidate]);
|
|
4364
|
+
}
|
|
4365
|
+
function pickBestCandidate(candidates) {
|
|
4366
|
+
return [...candidates].sort(compareCandidates)[0];
|
|
4367
|
+
}
|
|
4368
|
+
function compareCandidates(a, b) {
|
|
4369
|
+
if (a.activityAt !== b.activityAt) {
|
|
4370
|
+
if (a.activityAt === null)
|
|
4371
|
+
return 1;
|
|
4372
|
+
if (b.activityAt === null)
|
|
4373
|
+
return -1;
|
|
4374
|
+
return b.activityAt - a.activityAt;
|
|
4375
|
+
}
|
|
4376
|
+
if (a.sessionId !== b.sessionId)
|
|
4377
|
+
return a.sessionId < b.sessionId ? -1 : 1;
|
|
4378
|
+
return a.directory < b.directory ? -1 : a.directory > b.directory ? 1 : 0;
|
|
4379
|
+
}
|
|
4380
|
+
|
|
4085
4381
|
// src/hooks/magic-context/decay-curve.ts
|
|
4086
4382
|
var H50 = 24;
|
|
4087
4383
|
var D = 25;
|
|
@@ -4156,6 +4452,374 @@ function computeTiers(compartments, historyBudgetTokens) {
|
|
|
4156
4452
|
return renderedTier(v2IndexByOriginalIndex.get(index) ?? 1, c.importance ?? 50, pressure, 0);
|
|
4157
4453
|
});
|
|
4158
4454
|
}
|
|
4455
|
+
// src/v2/fold/restore.ts
|
|
4456
|
+
import { fileURLToPath } from "node:url";
|
|
4457
|
+
|
|
4458
|
+
// src/v2/hooks/storage-notice.ts
|
|
4459
|
+
var STORAGE_NOTICE_PREFIX = "[Magic Context storage notice] ";
|
|
4460
|
+
function isStorageNoticeText(text) {
|
|
4461
|
+
return typeof text === "string" && text.startsWith(STORAGE_NOTICE_PREFIX);
|
|
4462
|
+
}
|
|
4463
|
+
|
|
4464
|
+
// src/v2/fold/restore.ts
|
|
4465
|
+
function unavailableAttachmentNote(name, mediaType) {
|
|
4466
|
+
return `[Attachment ${name ? `"${name}" ` : ""}(${mediaType}) is not available in this restored history]`;
|
|
4467
|
+
}
|
|
4468
|
+
var imageMimes = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
4469
|
+
function attachmentParts(files, rowID, media) {
|
|
4470
|
+
const seen = new Map;
|
|
4471
|
+
return files.flatMap((file) => {
|
|
4472
|
+
if (imageMimes.has(file.mime) && file.source.type === "inline" && file.mention?.text) {
|
|
4473
|
+
const key = JSON.stringify([
|
|
4474
|
+
file.mime,
|
|
4475
|
+
file.name ?? null,
|
|
4476
|
+
file.description ?? null,
|
|
4477
|
+
file.mention.text
|
|
4478
|
+
]);
|
|
4479
|
+
const payloads = seen.get(key) ?? new Set;
|
|
4480
|
+
if (payloads.has(file.data))
|
|
4481
|
+
return [];
|
|
4482
|
+
payloads.add(file.data);
|
|
4483
|
+
seen.set(key, payloads);
|
|
4484
|
+
}
|
|
4485
|
+
const uri = file.source.type === "uri" ? file.source.uri : undefined;
|
|
4486
|
+
const location = uri?.startsWith("file:") ? fileURLToPath(uri) : undefined;
|
|
4487
|
+
if (file.mime === "text/plain" || file.mime === "application/x-directory") {
|
|
4488
|
+
const directory = file.mime === "application/x-directory";
|
|
4489
|
+
return [
|
|
4490
|
+
{
|
|
4491
|
+
type: "text",
|
|
4492
|
+
text: `
|
|
4493
|
+
|
|
4494
|
+
${[
|
|
4495
|
+
directory ? `Attached directory: ${location ?? file.name ?? uri ?? "directory"}` : `Attached file: ${file.name ?? uri ?? "inline attachment"}`,
|
|
4496
|
+
file.description === undefined ? undefined : `Description: ${file.description}`,
|
|
4497
|
+
directory && !file.data.length ? undefined : "",
|
|
4498
|
+
directory && !file.data.length ? undefined : Buffer.from(file.data, "base64").toString("utf8")
|
|
4499
|
+
].filter((line) => line !== undefined).join(`
|
|
4500
|
+
`)}`,
|
|
4501
|
+
metadata: {
|
|
4502
|
+
attachment: {
|
|
4503
|
+
source: file.source,
|
|
4504
|
+
name: file.name,
|
|
4505
|
+
description: file.description
|
|
4506
|
+
}
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
];
|
|
4510
|
+
}
|
|
4511
|
+
if (!imageMimes.has(file.mime) && file.mime !== "application/pdf")
|
|
4512
|
+
return [];
|
|
4513
|
+
const metadata = file.description === undefined ? undefined : { description: file.description };
|
|
4514
|
+
const located = location ? [{ type: "text", text: `Attached file: ${location}` }] : [];
|
|
4515
|
+
if (!media)
|
|
4516
|
+
return [
|
|
4517
|
+
...located,
|
|
4518
|
+
{
|
|
4519
|
+
type: "media",
|
|
4520
|
+
mediaType: file.mime,
|
|
4521
|
+
data: file.data,
|
|
4522
|
+
filename: file.name,
|
|
4523
|
+
metadata
|
|
4524
|
+
}
|
|
4525
|
+
];
|
|
4526
|
+
const asset = media.asset(file.data, file.mime);
|
|
4527
|
+
if (typeof asset === "string") {
|
|
4528
|
+
media.unavailable({ rowID, name: file.name, mediaType: file.mime, reason: asset });
|
|
4529
|
+
return [
|
|
4530
|
+
...located,
|
|
4531
|
+
{ type: "text", text: unavailableAttachmentNote(file.name, file.mime) }
|
|
4532
|
+
];
|
|
4533
|
+
}
|
|
4534
|
+
return [...located, { type: "media", media: asset, filename: file.name, metadata }];
|
|
4535
|
+
});
|
|
4536
|
+
}
|
|
4537
|
+
function restoreRow(row, model, media) {
|
|
4538
|
+
const data = row.data;
|
|
4539
|
+
const make = (role, content, metadata = data.metadata) => ({
|
|
4540
|
+
id: row.id,
|
|
4541
|
+
role,
|
|
4542
|
+
content,
|
|
4543
|
+
...metadata === undefined ? {} : { metadata }
|
|
4544
|
+
});
|
|
4545
|
+
if (row.type === "user") {
|
|
4546
|
+
const skills = data.skills ?? [];
|
|
4547
|
+
const content = [
|
|
4548
|
+
...skills.flatMap((skill) => skill.text === undefined ? [] : [{ type: "text", text: skill.text }]),
|
|
4549
|
+
...data.text ? [{ type: "text", text: data.text }] : [],
|
|
4550
|
+
...attachmentParts(data.files ?? [], row.id, media)
|
|
4551
|
+
];
|
|
4552
|
+
return content.length ? [
|
|
4553
|
+
make("user", content, {
|
|
4554
|
+
...data.metadata ?? {},
|
|
4555
|
+
...data.agents?.length ? { agents: data.agents } : {}
|
|
4556
|
+
})
|
|
4557
|
+
] : [];
|
|
4558
|
+
}
|
|
4559
|
+
if (row.type === "system")
|
|
4560
|
+
return [{ role: "system", content: [{ type: "text", text: data.text ?? "" }] }];
|
|
4561
|
+
if (row.type === "synthetic" && isStorageNoticeText(data.text))
|
|
4562
|
+
return [];
|
|
4563
|
+
if (["synthetic", "skill"].includes(row.type))
|
|
4564
|
+
return [make("user", [{ type: "text", text: data.text ?? "" }])];
|
|
4565
|
+
if (row.type === "location-switched")
|
|
4566
|
+
return [
|
|
4567
|
+
make("user", [
|
|
4568
|
+
{
|
|
4569
|
+
type: "text",
|
|
4570
|
+
text: `The working directory has been changed to ${data.location.directory}.`
|
|
4571
|
+
}
|
|
4572
|
+
])
|
|
4573
|
+
];
|
|
4574
|
+
if (row.type === "shell")
|
|
4575
|
+
return data.metadata?.background === true ? [] : [
|
|
4576
|
+
make("user", [
|
|
4577
|
+
{
|
|
4578
|
+
type: "text",
|
|
4579
|
+
text: `The following shell command was executed by the user:
|
|
4580
|
+
|
|
4581
|
+
Command:
|
|
4582
|
+
${data.command}
|
|
4583
|
+
|
|
4584
|
+
Output:
|
|
4585
|
+
${data.output?.output ?? ""}`
|
|
4586
|
+
}
|
|
4587
|
+
])
|
|
4588
|
+
];
|
|
4589
|
+
if (row.type !== "assistant")
|
|
4590
|
+
return [];
|
|
4591
|
+
const previousModel = data.model;
|
|
4592
|
+
const sameProvider = previousModel?.providerID === model.providerID;
|
|
4593
|
+
const sameModel = sameProvider && previousModel?.id === model.id;
|
|
4594
|
+
const reuse = sameModel && data.error === undefined;
|
|
4595
|
+
const metadata = (state) => state === undefined ? undefined : { [model.providerID]: state };
|
|
4596
|
+
const content = [];
|
|
4597
|
+
const results = [];
|
|
4598
|
+
for (const part of data.content ?? []) {
|
|
4599
|
+
if (part.type === "text") {
|
|
4600
|
+
if (part.text !== "")
|
|
4601
|
+
content.push({
|
|
4602
|
+
type: "text",
|
|
4603
|
+
text: part.text,
|
|
4604
|
+
providerMetadata: reuse ? metadata(part.state) : undefined
|
|
4605
|
+
});
|
|
4606
|
+
continue;
|
|
4607
|
+
}
|
|
4608
|
+
if (part.type === "reasoning") {
|
|
4609
|
+
if (part.text !== "" || reuse && part.state !== undefined)
|
|
4610
|
+
content.push({
|
|
4611
|
+
type: reuse || data.error === undefined ? "reasoning" : "text",
|
|
4612
|
+
text: part.text,
|
|
4613
|
+
providerMetadata: reuse ? metadata(part.state) : undefined
|
|
4614
|
+
});
|
|
4615
|
+
continue;
|
|
4616
|
+
}
|
|
4617
|
+
const state = part.state;
|
|
4618
|
+
const completed = state.status === "completed" || state.status === "error";
|
|
4619
|
+
const toolReuse = reuse || sameModel && part.executed === true && completed;
|
|
4620
|
+
let input = state.input;
|
|
4621
|
+
if (state.status === "streaming" && typeof input === "string") {
|
|
4622
|
+
try {
|
|
4623
|
+
input = JSON.parse(input);
|
|
4624
|
+
} catch {}
|
|
4625
|
+
}
|
|
4626
|
+
content.push({
|
|
4627
|
+
type: "tool-call",
|
|
4628
|
+
id: part.id,
|
|
4629
|
+
name: part.name,
|
|
4630
|
+
input,
|
|
4631
|
+
providerExecuted: part.executed,
|
|
4632
|
+
providerMetadata: toolReuse ? metadata(part.providerState) : undefined
|
|
4633
|
+
});
|
|
4634
|
+
if (!completed)
|
|
4635
|
+
continue;
|
|
4636
|
+
const toolContent = state.content ?? [];
|
|
4637
|
+
const single = toolContent.length === 1 ? toolContent[0] : undefined;
|
|
4638
|
+
const result = {
|
|
4639
|
+
type: "tool-result",
|
|
4640
|
+
id: part.id,
|
|
4641
|
+
name: part.name,
|
|
4642
|
+
providerExecuted: part.executed,
|
|
4643
|
+
providerMetadata: toolReuse ? metadata(part.providerResultState ?? part.providerState) : sameProvider ? metadata(part.providerResultState) : undefined,
|
|
4644
|
+
result: state.status === "error" ? { error: state.error, content: toolContent } : single?.type === "text" ? { type: "text", value: single.text } : { type: "content", value: toolContent },
|
|
4645
|
+
...state.status === "error" ? { resultType: "error" } : {}
|
|
4646
|
+
};
|
|
4647
|
+
if (part.executed === true)
|
|
4648
|
+
content.push(result);
|
|
4649
|
+
else
|
|
4650
|
+
results.push({ role: "tool", content: [result] });
|
|
4651
|
+
}
|
|
4652
|
+
return [...content.length ? [make("assistant", content)] : [], ...results];
|
|
4653
|
+
}
|
|
4654
|
+
|
|
4655
|
+
// src/v2/tool-result.ts
|
|
4656
|
+
function toolErrorText(result) {
|
|
4657
|
+
const value = result.value === undefined ? result : result.value;
|
|
4658
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
4659
|
+
}
|
|
4660
|
+
function toolStateContent(state) {
|
|
4661
|
+
if (typeof state.output === "string")
|
|
4662
|
+
return state.output;
|
|
4663
|
+
if (state.status === "error")
|
|
4664
|
+
return toolErrorText({ error: state.error, content: state.content ?? [] });
|
|
4665
|
+
if (typeof state.content === "string")
|
|
4666
|
+
return state.content;
|
|
4667
|
+
if (!Array.isArray(state.content))
|
|
4668
|
+
return "";
|
|
4669
|
+
return state.content.map((part) => {
|
|
4670
|
+
if (typeof part === "string")
|
|
4671
|
+
return part;
|
|
4672
|
+
if (!part || typeof part !== "object")
|
|
4673
|
+
return "";
|
|
4674
|
+
const value = part;
|
|
4675
|
+
if (typeof value.text === "string")
|
|
4676
|
+
return value.text;
|
|
4677
|
+
if (typeof value.value === "string")
|
|
4678
|
+
return value.value;
|
|
4679
|
+
return "";
|
|
4680
|
+
}).filter(Boolean).join(`
|
|
4681
|
+
`);
|
|
4682
|
+
}
|
|
4683
|
+
|
|
4684
|
+
// src/v2/hooks/store.ts
|
|
4685
|
+
var rawMessageTypes = new Set(RAW_MESSAGE_TYPES);
|
|
4686
|
+
var isRawRow = (row) => rawMessageTypes.has(row.type);
|
|
4687
|
+
var NO_MODEL = { providerID: "", id: "" };
|
|
4688
|
+
function hostServesRowById(row) {
|
|
4689
|
+
return isRawRow(row) && restoreRow(row, NO_MODEL).some((message) => message.id === row.id);
|
|
4690
|
+
}
|
|
4691
|
+
function projectRawMessages(rows, ordinalFor) {
|
|
4692
|
+
return rows.filter(isRawRow).map((row, index) => {
|
|
4693
|
+
const message = {
|
|
4694
|
+
id: row.id,
|
|
4695
|
+
ordinal: ordinalFor(row, index),
|
|
4696
|
+
role: row.type === "assistant" ? "assistant" : "user",
|
|
4697
|
+
createdAt: row.time_created ?? row.data.time?.created,
|
|
4698
|
+
parts: row.type === "assistant" ? (row.data.content ?? []).map((part) => {
|
|
4699
|
+
if (part.type !== "tool")
|
|
4700
|
+
return { ...part };
|
|
4701
|
+
const state = part.state;
|
|
4702
|
+
const content = state.content;
|
|
4703
|
+
return {
|
|
4704
|
+
type: "tool",
|
|
4705
|
+
tool: part.name,
|
|
4706
|
+
callID: part.id,
|
|
4707
|
+
state: {
|
|
4708
|
+
...state,
|
|
4709
|
+
output: state.status === "error" ? toolStateContent(state) : content?.filter((p) => p.type === "text").map((p) => p.text).join(`
|
|
4710
|
+
`) ?? ""
|
|
4711
|
+
}
|
|
4712
|
+
};
|
|
4713
|
+
}) : [{ type: "text", text: row.data.text ?? "" }]
|
|
4714
|
+
};
|
|
4715
|
+
Object.defineProperty(message, "storeType", { value: row.type, enumerable: false });
|
|
4716
|
+
if (!hostServesRowById(row))
|
|
4717
|
+
markHostUnservedRow(message);
|
|
4718
|
+
return message;
|
|
4719
|
+
});
|
|
4720
|
+
}
|
|
4721
|
+
function rawMessagePage(rows, afterOrdinal) {
|
|
4722
|
+
return projectRawMessages(rows, (_row, index) => afterOrdinal + index + 1);
|
|
4723
|
+
}
|
|
4724
|
+
|
|
4725
|
+
// src/web/message-stats.ts
|
|
4726
|
+
var SWEEP_PAGE_ROWS = 1000;
|
|
4727
|
+
var rawMessageTypes2 = new Set(RAW_MESSAGE_TYPES);
|
|
4728
|
+
function codePointLength(text) {
|
|
4729
|
+
let count = 0;
|
|
4730
|
+
for (const _character of text)
|
|
4731
|
+
count += 1;
|
|
4732
|
+
return count;
|
|
4733
|
+
}
|
|
4734
|
+
function partsCharCount(parts) {
|
|
4735
|
+
if (!Array.isArray(parts))
|
|
4736
|
+
return 0;
|
|
4737
|
+
let total = 0;
|
|
4738
|
+
for (const part of parts) {
|
|
4739
|
+
if (!part || typeof part !== "object")
|
|
4740
|
+
continue;
|
|
4741
|
+
const record = part;
|
|
4742
|
+
if (typeof record.text === "string")
|
|
4743
|
+
total += codePointLength(record.text);
|
|
4744
|
+
const output = record.state?.output;
|
|
4745
|
+
if (typeof output === "string")
|
|
4746
|
+
total += codePointLength(output);
|
|
4747
|
+
}
|
|
4748
|
+
return total;
|
|
4749
|
+
}
|
|
4750
|
+
function sweepRangeCounts(page, queries) {
|
|
4751
|
+
const counts = new Map;
|
|
4752
|
+
if (queries.length === 0)
|
|
4753
|
+
return counts;
|
|
4754
|
+
const points = new Set;
|
|
4755
|
+
for (const query of queries) {
|
|
4756
|
+
points.add(query.end);
|
|
4757
|
+
points.add(query.start - 1);
|
|
4758
|
+
}
|
|
4759
|
+
const sorted = [...points].sort((left, right) => left - right);
|
|
4760
|
+
const through = sorted[sorted.length - 1];
|
|
4761
|
+
const cumulative = new Map;
|
|
4762
|
+
let pointIndex = 0;
|
|
4763
|
+
let total = 0;
|
|
4764
|
+
let cursor = -1;
|
|
4765
|
+
const settlePassedPoints = (rowSeq) => {
|
|
4766
|
+
while (pointIndex < sorted.length && sorted[pointIndex] < rowSeq) {
|
|
4767
|
+
cumulative.set(sorted[pointIndex], total);
|
|
4768
|
+
pointIndex += 1;
|
|
4769
|
+
}
|
|
4770
|
+
};
|
|
4771
|
+
for (;; ) {
|
|
4772
|
+
const batch = page(cursor, through);
|
|
4773
|
+
const rawRows = batch.rows.filter((row) => rawMessageTypes2.has(row.type));
|
|
4774
|
+
const rowCounts = new Map;
|
|
4775
|
+
for (const message of rawMessagePage(rawRows, 0)) {
|
|
4776
|
+
rowCounts.set(message.id, partsCharCount(message.parts));
|
|
4777
|
+
}
|
|
4778
|
+
for (const row of batch.rows) {
|
|
4779
|
+
settlePassedPoints(row.seq);
|
|
4780
|
+
const rowCount = rowCounts.get(row.id);
|
|
4781
|
+
if (rowCount !== undefined)
|
|
4782
|
+
total += rowCount;
|
|
4783
|
+
}
|
|
4784
|
+
if (batch.rows.length === 0 || batch.cursor === undefined)
|
|
4785
|
+
break;
|
|
4786
|
+
cursor = batch.cursor;
|
|
4787
|
+
}
|
|
4788
|
+
while (pointIndex < sorted.length) {
|
|
4789
|
+
cumulative.set(sorted[pointIndex], total);
|
|
4790
|
+
pointIndex += 1;
|
|
4791
|
+
}
|
|
4792
|
+
for (const query of queries) {
|
|
4793
|
+
counts.set(query.key, (cumulative.get(query.end) ?? 0) - (cumulative.get(query.start - 1) ?? 0));
|
|
4794
|
+
}
|
|
4795
|
+
return counts;
|
|
4796
|
+
}
|
|
4797
|
+
function buildOrdinalPrefix(messages) {
|
|
4798
|
+
const ordinals = [];
|
|
4799
|
+
const cumulative = [];
|
|
4800
|
+
let total = 0;
|
|
4801
|
+
for (const message of messages) {
|
|
4802
|
+
total += partsCharCount(message.parts);
|
|
4803
|
+
ordinals.push(message.ordinal);
|
|
4804
|
+
cumulative.push(total);
|
|
4805
|
+
}
|
|
4806
|
+
return { ordinals, cumulative };
|
|
4807
|
+
}
|
|
4808
|
+
function ordinalRangeCount(prefix, start, end) {
|
|
4809
|
+
return cumulativeThrough(prefix, end) - cumulativeThrough(prefix, start - 1);
|
|
4810
|
+
}
|
|
4811
|
+
function cumulativeThrough(prefix, through) {
|
|
4812
|
+
let low = 0;
|
|
4813
|
+
let high = prefix.ordinals.length;
|
|
4814
|
+
while (low < high) {
|
|
4815
|
+
const middle = low + high >> 1;
|
|
4816
|
+
if (prefix.ordinals[middle] <= through)
|
|
4817
|
+
low = middle + 1;
|
|
4818
|
+
else
|
|
4819
|
+
high = middle;
|
|
4820
|
+
}
|
|
4821
|
+
return low === 0 ? 0 : prefix.cumulative[low - 1];
|
|
4822
|
+
}
|
|
4159
4823
|
|
|
4160
4824
|
// src/web/views.ts
|
|
4161
4825
|
function openContextFor(sources) {
|
|
@@ -4268,10 +4932,11 @@ function oc2MessagePage(sources, sessionId, options) {
|
|
|
4268
4932
|
const limit = clampLimit(options.limit);
|
|
4269
4933
|
const outputLimit = options.fullOutputs ? undefined : DEFAULT_OUTPUT_LIMIT;
|
|
4270
4934
|
const kept = [];
|
|
4935
|
+
const end = options.end;
|
|
4271
4936
|
let after = options.after ?? -1;
|
|
4272
4937
|
let hasMore = false;
|
|
4273
4938
|
for (let guard = 0;guard < 200; guard++) {
|
|
4274
|
-
const page = reader.page(sessionId, { after, limit: 200 });
|
|
4939
|
+
const page = reader.page(sessionId, { after, limit: 200, through: end });
|
|
4275
4940
|
for (const row of page.rows) {
|
|
4276
4941
|
if (!isRawType(row.type))
|
|
4277
4942
|
continue;
|
|
@@ -4286,6 +4951,7 @@ function oc2MessagePage(sources, sessionId, options) {
|
|
|
4286
4951
|
after = page.cursor;
|
|
4287
4952
|
}
|
|
4288
4953
|
const last = kept.at(-1);
|
|
4954
|
+
const partsById = new Map(rawMessagePage(kept, 0).map((message) => [message.id, message.parts]));
|
|
4289
4955
|
return {
|
|
4290
4956
|
source: "oc2-host",
|
|
4291
4957
|
rows: kept.map((row) => ({
|
|
@@ -4293,7 +4959,7 @@ function oc2MessagePage(sources, sessionId, options) {
|
|
|
4293
4959
|
id: row.id,
|
|
4294
4960
|
role: row.type,
|
|
4295
4961
|
timeCreated: typeof row.time_created === "number" ? row.time_created : null,
|
|
4296
|
-
parts: truncateParts(row.
|
|
4962
|
+
parts: truncateParts(partsById.get(row.id), outputLimit)
|
|
4297
4963
|
})),
|
|
4298
4964
|
nextAfter: last ? last.seq : options.after ?? null,
|
|
4299
4965
|
hasMore
|
|
@@ -4318,7 +4984,8 @@ function piMessagePage(sources, sessionId, options) {
|
|
|
4318
4984
|
const limit = clampLimit(options.limit);
|
|
4319
4985
|
const outputLimit = options.fullOutputs ? undefined : DEFAULT_OUTPUT_LIMIT;
|
|
4320
4986
|
const after = options.after ?? 0;
|
|
4321
|
-
const
|
|
4987
|
+
const end = options.end;
|
|
4988
|
+
const selected = projection.messages.filter((message) => message.ordinal > after && (end === undefined || message.ordinal <= end));
|
|
4322
4989
|
const page = selected.slice(0, limit);
|
|
4323
4990
|
return {
|
|
4324
4991
|
source: "pi-jsonl",
|
|
@@ -4375,7 +5042,8 @@ function listSessionCompartmentsView(sources, projectPath, sessionId, options) {
|
|
|
4375
5042
|
throw new ContextStoreUnavailableError(opened.status);
|
|
4376
5043
|
try {
|
|
4377
5044
|
const rows = opened.store.db.prepare("SELECT id, sequence, title, importance, created_at, episode_type, legacy, content, p1, p2, p3, p4, start_message_id, end_message_id FROM compartments WHERE session_id = ? AND harness = ? ORDER BY sequence ASC").all(sessionId, harness);
|
|
4378
|
-
const
|
|
5045
|
+
const stats = harness === "pi" ? piAnchorsAndCounts(sources, sessionId, rows, options.branch) : oc2AnchorsAndCounts(sources, sessionId, rows);
|
|
5046
|
+
const anchors = stats.anchors;
|
|
4379
5047
|
const tiers = computeTiers(rows.map((row) => ({
|
|
4380
5048
|
startMessage: 0,
|
|
4381
5049
|
endMessage: 0,
|
|
@@ -4397,6 +5065,7 @@ function listSessionCompartmentsView(sources, projectPath, sessionId, options) {
|
|
|
4397
5065
|
legacy: row.legacy === 1,
|
|
4398
5066
|
episodeType: row.episode_type ?? null,
|
|
4399
5067
|
content: row.content,
|
|
5068
|
+
summaryCharCount: codePointLength(row.content),
|
|
4400
5069
|
details: { p1: row.p1, p2: row.p2, p3: row.p3, p4: row.p4 },
|
|
4401
5070
|
startMessageId: row.start_message_id,
|
|
4402
5071
|
endMessageId: row.end_message_id,
|
|
@@ -4404,6 +5073,7 @@ function listSessionCompartmentsView(sources, projectPath, sessionId, options) {
|
|
|
4404
5073
|
start: boundaryFor(row.start_message_id, anchors),
|
|
4405
5074
|
end: boundaryFor(row.end_message_id, anchors)
|
|
4406
5075
|
},
|
|
5076
|
+
messageCharCount: stats.counts.get(row.id) ?? null,
|
|
4407
5077
|
renderTier: tiers[index] <= 1 ? "full" : "decayed"
|
|
4408
5078
|
}));
|
|
4409
5079
|
} finally {
|
|
@@ -4415,32 +5085,52 @@ function boundaryFor(messageId, anchors) {
|
|
|
4415
5085
|
return { state: "unlinked" };
|
|
4416
5086
|
return anchors.get(messageId) ?? { state: "unresolved" };
|
|
4417
5087
|
}
|
|
4418
|
-
function
|
|
5088
|
+
function resolvedAnchor(boundary) {
|
|
5089
|
+
return boundary.state === "resolved" || boundary.state === "synth-resolved" ? boundary.anchor : null;
|
|
5090
|
+
}
|
|
5091
|
+
function oc2AnchorsAndCounts(sources, sessionId, rows) {
|
|
5092
|
+
const counts = new Map(rows.map((row) => [row.id, null]));
|
|
4419
5093
|
const anchors = new Map;
|
|
4420
5094
|
const opened = openOc2For(sources);
|
|
4421
5095
|
if (!opened.store)
|
|
4422
|
-
return anchors;
|
|
5096
|
+
return { anchors, counts };
|
|
4423
5097
|
try {
|
|
4424
|
-
const
|
|
4425
|
-
for (const
|
|
4426
|
-
anchors.set(
|
|
5098
|
+
const messages = opened.store.metaDb.prepare("SELECT id, seq FROM session_message WHERE session_id = ?").all(sessionId);
|
|
5099
|
+
for (const message of messages) {
|
|
5100
|
+
anchors.set(message.id, { state: "resolved", anchor: message.seq });
|
|
5101
|
+
}
|
|
5102
|
+
const queries = [];
|
|
5103
|
+
for (const row of rows) {
|
|
5104
|
+
const start = resolvedAnchor(boundaryFor(row.start_message_id, anchors));
|
|
5105
|
+
const end = resolvedAnchor(boundaryFor(row.end_message_id, anchors));
|
|
5106
|
+
if (start === null || end === null || start > end)
|
|
5107
|
+
continue;
|
|
5108
|
+
queries.push({ key: row.id, start, end });
|
|
5109
|
+
}
|
|
5110
|
+
if (queries.length > 0) {
|
|
5111
|
+
const reader = opened.store.reader;
|
|
5112
|
+
const computed = sweepRangeCounts((after, through) => reader.page(sessionId, { after, through, limit: SWEEP_PAGE_ROWS }), queries);
|
|
5113
|
+
for (const [key, count] of computed)
|
|
5114
|
+
counts.set(key, count);
|
|
5115
|
+
}
|
|
4427
5116
|
} catch {} finally {
|
|
4428
5117
|
opened.store.close();
|
|
4429
5118
|
}
|
|
4430
|
-
return anchors;
|
|
5119
|
+
return { anchors, counts };
|
|
4431
5120
|
}
|
|
4432
|
-
function
|
|
5121
|
+
function piAnchorsAndCounts(sources, sessionId, rows, branch) {
|
|
4433
5122
|
const anchors = new Map;
|
|
5123
|
+
const counts = new Map(rows.map((row) => [row.id, null]));
|
|
4434
5124
|
const converter = sources.piConverter;
|
|
4435
5125
|
if (!converter)
|
|
4436
|
-
return anchors;
|
|
5126
|
+
return { anchors, counts };
|
|
4437
5127
|
const group = resolvePiBranchGroup(sources, sessionId, branch);
|
|
4438
5128
|
const file = group.files[0];
|
|
4439
5129
|
if (!file)
|
|
4440
|
-
return anchors;
|
|
5130
|
+
return { anchors, counts };
|
|
4441
5131
|
const projection = projectPiSessionFile(file.filePath, converter);
|
|
4442
5132
|
if (projection.status.kind !== "ok")
|
|
4443
|
-
return anchors;
|
|
5133
|
+
return { anchors, counts };
|
|
4444
5134
|
const aliases = sources.piAliasBuilder?.(projection.entries);
|
|
4445
5135
|
for (const row of rows) {
|
|
4446
5136
|
for (const messageId of [row.start_message_id, row.end_message_id]) {
|
|
@@ -4452,7 +5142,15 @@ function piAnchors(sources, sessionId, rows, branch) {
|
|
|
4452
5142
|
anchors.set(messageId, boundaryFromResolution(resolved));
|
|
4453
5143
|
}
|
|
4454
5144
|
}
|
|
4455
|
-
|
|
5145
|
+
const prefix = buildOrdinalPrefix(projection.messages);
|
|
5146
|
+
for (const row of rows) {
|
|
5147
|
+
const start = resolvedAnchor(boundaryFor(row.start_message_id, anchors));
|
|
5148
|
+
const end = resolvedAnchor(boundaryFor(row.end_message_id, anchors));
|
|
5149
|
+
if (start === null || end === null || start > end)
|
|
5150
|
+
continue;
|
|
5151
|
+
counts.set(row.id, ordinalRangeCount(prefix, start, end));
|
|
5152
|
+
}
|
|
5153
|
+
return { anchors, counts };
|
|
4456
5154
|
}
|
|
4457
5155
|
function boundaryFromResolution(resolution) {
|
|
4458
5156
|
if (resolution.state === "resolved") {
|
|
@@ -4642,13 +5340,20 @@ function sourceStatuses(sources) {
|
|
|
4642
5340
|
}
|
|
4643
5341
|
function projectsHandler(sources) {
|
|
4644
5342
|
return (request) => {
|
|
5343
|
+
const fixtureMode = sources.fixtureMode === true;
|
|
4645
5344
|
const opened = openContextStoreReadonly(sources.contextDbPath ?? requireContextPath(sources));
|
|
4646
5345
|
if (!opened.store)
|
|
4647
|
-
return ok({ sources: sourceStatuses(sources), projects: [] }, request.now);
|
|
5346
|
+
return ok({ sources: sourceStatuses(sources), fixtureMode, projects: [] }, request.now);
|
|
4648
5347
|
try {
|
|
5348
|
+
const projects = listWebProjects(opened.store);
|
|
5349
|
+
const displayPaths = resolveProjectDisplayPaths(sources, projects, listProjectSessionRefs(opened.store));
|
|
4649
5350
|
return ok({
|
|
4650
5351
|
sources: sourceStatuses(sources),
|
|
4651
|
-
|
|
5352
|
+
fixtureMode,
|
|
5353
|
+
projects: projects.map((project) => ({
|
|
5354
|
+
...project,
|
|
5355
|
+
displayPath: displayPaths.get(project.projectPath) ?? null
|
|
5356
|
+
}))
|
|
4652
5357
|
}, request.now);
|
|
4653
5358
|
} finally {
|
|
4654
5359
|
opened.store.db.close();
|
|
@@ -4672,6 +5377,7 @@ function messagesHandler(sources) {
|
|
|
4672
5377
|
const page = listSessionMessages(sources, request.projectPath, sessionId, {
|
|
4673
5378
|
branch: optionalQuery(request.query, "branch"),
|
|
4674
5379
|
after: optionalInt(request.query, "after"),
|
|
5380
|
+
end: optionalNonNegativeInt(request.query, "end"),
|
|
4675
5381
|
limit: optionalInt(request.query, "limit"),
|
|
4676
5382
|
fullOutputs: request.query.get("fullOutputs") === "1"
|
|
4677
5383
|
});
|
|
@@ -4804,6 +5510,13 @@ function optionalInt(query, name) {
|
|
|
4804
5510
|
}
|
|
4805
5511
|
return parsed;
|
|
4806
5512
|
}
|
|
5513
|
+
function optionalNonNegativeInt(query, name) {
|
|
5514
|
+
const parsed = optionalInt(query, name);
|
|
5515
|
+
if (parsed !== undefined && parsed < 0) {
|
|
5516
|
+
throw new BadRequestError(`${name} must be a non-negative integer`);
|
|
5517
|
+
}
|
|
5518
|
+
return parsed;
|
|
5519
|
+
}
|
|
4807
5520
|
function badRequest(message) {
|
|
4808
5521
|
return { status: 400, body: { error: { code: "bad-request", message } } };
|
|
4809
5522
|
}
|
|
@@ -5499,7 +6212,7 @@ var CONTENT_TYPES = {
|
|
|
5499
6212
|
".map": "application/json; charset=utf-8"
|
|
5500
6213
|
};
|
|
5501
6214
|
function resolveDefaultUiRoot() {
|
|
5502
|
-
return
|
|
6215
|
+
return fileURLToPath2(new URL("./ui/", import.meta.url));
|
|
5503
6216
|
}
|
|
5504
6217
|
function startWebServer(options = {}) {
|
|
5505
6218
|
const host = options.host ?? DEFAULT_WEB_HOST;
|
|
@@ -6006,8 +6719,18 @@ function main() {
|
|
|
6006
6719
|
`);
|
|
6007
6720
|
}
|
|
6008
6721
|
var args = process.argv.slice(2);
|
|
6722
|
+
var parsed = parseWebEntryArgs(args);
|
|
6723
|
+
if (parsed.kind === "help") {
|
|
6724
|
+
process.stdout.write(webEntryUsage());
|
|
6725
|
+
process.exit(0);
|
|
6726
|
+
}
|
|
6727
|
+
if (parsed.kind === "usage-error") {
|
|
6728
|
+
process.stderr.write(`web panel: ${parsed.message}
|
|
6729
|
+
|
|
6730
|
+
${webEntryUsage()}`);
|
|
6731
|
+
process.exit(2);
|
|
6732
|
+
}
|
|
6009
6733
|
try {
|
|
6010
|
-
const parsed = parseEntryArgs(args);
|
|
6011
6734
|
if (parsed.mode === "smoke") {
|
|
6012
6735
|
main();
|
|
6013
6736
|
} else {
|
|
@@ -6033,6 +6756,7 @@ async function runServer(parsed) {
|
|
|
6033
6756
|
piSessionsRoot: fixture.piSessionsRoot
|
|
6034
6757
|
};
|
|
6035
6758
|
}
|
|
6759
|
+
sources.fixtureMode = parsed.fixture;
|
|
6036
6760
|
const handle = await startWebServer({ port: parsed.port, sources });
|
|
6037
6761
|
process.stdout.write(`mmc web panel (@retaw106/mmc-oc) listening on ${handle.url}
|
|
6038
6762
|
` + (fixture ? `fixture root: ${fixture.root}
|
|
@@ -6040,32 +6764,3 @@ async function runServer(parsed) {
|
|
|
6040
6764
|
` + `press Ctrl+C to stop
|
|
6041
6765
|
`);
|
|
6042
6766
|
}
|
|
6043
|
-
function parseEntryArgs(args) {
|
|
6044
|
-
let port;
|
|
6045
|
-
const portFlag = args.indexOf("--port");
|
|
6046
|
-
if (portFlag >= 0) {
|
|
6047
|
-
const value = Number(args[portFlag + 1]);
|
|
6048
|
-
if (!Number.isSafeInteger(value) || value < 0 || value > 65535) {
|
|
6049
|
-
throw new Error("--port must be an integer in [0, 65535]");
|
|
6050
|
-
}
|
|
6051
|
-
port = value;
|
|
6052
|
-
} else if (process.env.MMC_WEB_PORT) {
|
|
6053
|
-
const value = Number(process.env.MMC_WEB_PORT);
|
|
6054
|
-
if (Number.isSafeInteger(value) && value >= 0 && value <= 65535)
|
|
6055
|
-
port = value;
|
|
6056
|
-
}
|
|
6057
|
-
const fixtureRootFlag = args.indexOf("--fixture-root");
|
|
6058
|
-
const fixtureRoot = fixtureRootFlag >= 0 ? args[fixtureRootFlag + 1] : undefined;
|
|
6059
|
-
if (args.includes("--fixture-root") && !fixtureRoot) {
|
|
6060
|
-
throw new Error("--fixture-root requires a directory argument");
|
|
6061
|
-
}
|
|
6062
|
-
return {
|
|
6063
|
-
mode: args.includes("--smoke") ? "smoke" : "serve",
|
|
6064
|
-
port,
|
|
6065
|
-
fixture: args.includes("--fixture"),
|
|
6066
|
-
fixtureRoot
|
|
6067
|
-
};
|
|
6068
|
-
}
|
|
6069
|
-
export {
|
|
6070
|
-
parseEntryArgs
|
|
6071
|
-
};
|