@aiwg/cli 2026.10.3 → 2026.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agentic/code/providers/model-capabilities.v1.json +96 -4
- package/bin/aiwg.mjs +50 -27
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +19 -2
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +19 -12
- package/dist/src/cli/handlers/diagnose.js +4 -3
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +4 -1
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/models.js +104 -8
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/refresh.js +12 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +1 -2
- package/dist/src/cli/handlers/script-runner.js +3 -2
- package/dist/src/cli/handlers/session.js +19 -9
- package/dist/src/cli/handlers/sessions.js +98 -37
- package/dist/src/cli/handlers/setup-manifest.js +39 -44
- package/dist/src/cli/handlers/setup.js +41 -0
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +57 -2
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +294 -55
- package/dist/src/cli/handlers/utilities.js +26 -10
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +87 -37
- package/dist/src/config/cli.js +111 -5
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +2 -1
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +13 -8
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +2 -15
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +5 -13
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +131 -28
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/gates/providers/loader.js +61 -17
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +14 -2
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +78 -3
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +7 -1
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +36 -11
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +1 -1
- package/dist/src/mcp/helpers.mjs +3 -2
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/registry.js +3 -93
- package/dist/src/mcp/registry.mjs +3 -87
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +37 -375
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tools/decision.mjs +12 -11
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +12 -2
- package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
- package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
- package/dist/src/mission-protocol/codecs.js +8 -0
- package/dist/src/models/config-loader.js +7 -2
- package/dist/src/models/model-capabilities.v1.json +96 -4
- package/dist/src/models/model-discovery.js +76 -11
- package/dist/src/models/provider-policy.js +101 -7
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/resolver.js +12 -10
- package/dist/src/models/worker-direct-readiness.js +65 -44
- package/dist/src/models/worker-dispatch-offer.js +3 -3
- package/dist/src/models/worker-health-readiness.js +16 -3
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +135 -28
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +43 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +12 -4
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/mission-conductor.js +38 -12
- package/dist/src/serve/worker-invocation.js +30 -10
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/sessions/adapter-registry.js +23 -19
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +10 -10
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot.js +24 -13
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
- package/dist/src/sessions/adapters/muse.js +150 -93
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +29 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +16 -8
- package/dist/src/sessions/batch-import.js +51 -11
- package/dist/src/sessions/contracts.js +40 -2
- package/dist/src/sessions/coverage-preflight.js +1 -1
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +60 -38
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +147 -52
- package/dist/src/sessions/workspace-discovery.js +221 -56
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/package.json +15 -2
- package/schemas/models/model-catalog.v1.schema.json +1 -1
- package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
- package/tools/_resolve-impl.mjs +13 -15
- package/tools/agents/deploy-agents.mjs +49 -21
- package/tools/agents/providers/base.mjs +104 -134
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/hermes.mjs +57 -47
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +6 -4
- package/tools/providers/omp-transport.mjs +4 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
- package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
- package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { open } from 'node:fs/promises';
|
|
2
|
-
import { SessionContractError } from '../contracts.js';
|
|
2
|
+
import { schemaIssueDetail, SessionContractError } from '../contracts.js';
|
|
3
3
|
import { authorizeSourceFile } from '../policy.js';
|
|
4
4
|
// Discovery inspects metadata, not an entire import. Bound allocation before
|
|
5
5
|
// assembling a line (readline would buffer an arbitrarily large single line).
|
|
@@ -9,18 +9,31 @@ export const MUSE_WORKSPACE_SCAN_LIMITS = Object.freeze({
|
|
|
9
9
|
maxTotalBytes: 32 * 1024 * 1024,
|
|
10
10
|
maxNestingDepth: 64,
|
|
11
11
|
});
|
|
12
|
-
export
|
|
12
|
+
export const MUSE_WORKSPACE_TAIL_BYTES = 1024 * 1024;
|
|
13
|
+
export class MuseWorkspaceLimitError extends SessionContractError {
|
|
14
|
+
limitName;
|
|
15
|
+
limitValue;
|
|
16
|
+
constructor(limitName, limitValue, observed) {
|
|
17
|
+
super('RESOURCE_LIMIT_EXCEEDED', `Muse workspace inspection exceeded ${limitName}=${limitValue}`, { limit: limitName, configured: limitValue, observed });
|
|
18
|
+
this.limitName = limitName;
|
|
19
|
+
this.limitValue = limitValue;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function* museWorkspaceScanLines(authorization, limits, progress, tailBytes) {
|
|
13
23
|
const allowed = await authorizeSourceFile(authorization);
|
|
14
24
|
const handle = await open(allowed.canonicalPath, 'r');
|
|
15
25
|
try {
|
|
16
26
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
17
|
-
let position = 0;
|
|
27
|
+
let position = tailBytes === undefined ? 0 : Math.max(0, allowed.size - tailBytes);
|
|
28
|
+
// A tail window may start inside a record (or a UTF-8 code point).
|
|
29
|
+
// Discard that fragment before parsing any complete JSONL records.
|
|
30
|
+
let skipPartialLine = position > 0;
|
|
18
31
|
let parts = [];
|
|
19
32
|
let length = 0;
|
|
20
33
|
while (position < allowed.size) {
|
|
21
34
|
const remaining = limits.maxTotalBytes - progress.bytesInspected;
|
|
22
35
|
if (remaining <= 0)
|
|
23
|
-
throw scanLimit();
|
|
36
|
+
throw scanLimit('maxTotalBytes', limits.maxTotalBytes, allowed.size);
|
|
24
37
|
const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, allowed.size - position, remaining), position);
|
|
25
38
|
if (bytesRead === 0) {
|
|
26
39
|
throw new SessionContractError('TRUNCATED_SOURCE', 'Muse source changed during inspection');
|
|
@@ -31,8 +44,15 @@ export async function* museWorkspaceScanLines(authorization, limits, progress) {
|
|
|
31
44
|
const newline = buffer.indexOf(10, start);
|
|
32
45
|
const end = newline >= 0 && newline < bytesRead ? newline + 1 : bytesRead;
|
|
33
46
|
const segment = buffer.subarray(start, end);
|
|
47
|
+
if (skipPartialLine) {
|
|
48
|
+
progress.bytesInspected += segment.length;
|
|
49
|
+
start = end;
|
|
50
|
+
if (newline >= 0 && newline < bytesRead)
|
|
51
|
+
skipPartialLine = false;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
34
54
|
if (length + segment.length > limits.maxRecordBytes)
|
|
35
|
-
throw scanLimit();
|
|
55
|
+
throw scanLimit('maxRecordBytes', limits.maxRecordBytes, length + segment.length);
|
|
36
56
|
parts.push(Buffer.from(segment));
|
|
37
57
|
length += segment.length;
|
|
38
58
|
progress.bytesInspected += segment.length;
|
|
@@ -43,7 +63,7 @@ export async function* museWorkspaceScanLines(authorization, limits, progress) {
|
|
|
43
63
|
length = 0;
|
|
44
64
|
if (line.trim() !== '') {
|
|
45
65
|
if (progress.recordsInspected >= limits.maxRecords)
|
|
46
|
-
throw scanLimit();
|
|
66
|
+
throw scanLimit('maxRecords', limits.maxRecords, progress.recordsInspected + 1);
|
|
47
67
|
const value = parseMuseScanJson(line, limits.maxNestingDepth);
|
|
48
68
|
progress.recordsInspected += 1;
|
|
49
69
|
yield value;
|
|
@@ -55,7 +75,7 @@ export async function* museWorkspaceScanLines(authorization, limits, progress) {
|
|
|
55
75
|
const line = Buffer.concat(parts, length).toString('utf8');
|
|
56
76
|
if (line.trim() !== '') {
|
|
57
77
|
if (progress.recordsInspected >= limits.maxRecords)
|
|
58
|
-
throw scanLimit();
|
|
78
|
+
throw scanLimit('maxRecords', limits.maxRecords, progress.recordsInspected + 1);
|
|
59
79
|
let value;
|
|
60
80
|
try {
|
|
61
81
|
value = parseMuseScanJson(line, limits.maxNestingDepth);
|
|
@@ -85,7 +105,7 @@ export function parseMuseScanJson(line, maxDepth) {
|
|
|
85
105
|
value = JSON.parse(line);
|
|
86
106
|
}
|
|
87
107
|
catch {
|
|
88
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered malformed JSON');
|
|
108
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered malformed JSON', schemaIssueDetail('<root>', 'invalid_json'));
|
|
89
109
|
}
|
|
90
110
|
// Check depth iteratively, including decoded retained children, before zod
|
|
91
111
|
// validation. Deep input must produce a limit diagnostic, not stack overflow.
|
|
@@ -93,7 +113,7 @@ export function parseMuseScanJson(line, maxDepth) {
|
|
|
93
113
|
while (pending.length > 0) {
|
|
94
114
|
const current = pending.pop();
|
|
95
115
|
if (current.depth > maxDepth)
|
|
96
|
-
throw scanLimit();
|
|
116
|
+
throw scanLimit('maxNestingDepth', maxDepth, current.depth);
|
|
97
117
|
if (current.value !== null && typeof current.value === 'object') {
|
|
98
118
|
for (const child of Object.values(current.value)) {
|
|
99
119
|
pending.push({ value: child, depth: current.depth + 1 });
|
|
@@ -102,7 +122,7 @@ export function parseMuseScanJson(line, maxDepth) {
|
|
|
102
122
|
}
|
|
103
123
|
return value;
|
|
104
124
|
}
|
|
105
|
-
function scanLimit() {
|
|
106
|
-
return new
|
|
125
|
+
function scanLimit(limit, configured, observed) {
|
|
126
|
+
return new MuseWorkspaceLimitError(limit, configured, observed);
|
|
107
127
|
}
|
|
108
128
|
//# sourceMappingURL=muse-workspace-scan.js.map
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
import { opendir } from 'node:fs/promises';
|
|
43
43
|
import { resolve } from 'node:path';
|
|
44
44
|
import { z } from 'zod';
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
45
|
+
import { matchSessionWorkspace, WORKSPACE_MATCH_KINDS } from '../workspace-matching.js';
|
|
46
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, SessionErrorCodeSchema, SessionRejectionDetailSchema, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
47
|
+
import { readBoundedJson, streamBoundedJsonLines, } from '../readers.js';
|
|
48
|
+
import { MUSE_WORKSPACE_SCAN_LIMITS, MUSE_WORKSPACE_TAIL_BYTES, MuseWorkspaceLimitError, museWorkspaceScanLines, parseMuseScanJson, } from './muse-workspace-scan.js';
|
|
48
49
|
export const MUSE_ADAPTER_VERSION = '1.0.0';
|
|
49
50
|
export const MUSE_EXPORT_SCHEMA_VERSION = '1.0.0';
|
|
50
51
|
export const MUSE_EXPORT_SCHEMA_MAJOR = 1;
|
|
@@ -173,7 +174,7 @@ export class MuseSessionAdapter {
|
|
|
173
174
|
for (const root of [...scope.allowedRoots].sort()) {
|
|
174
175
|
for await (const locator of museNativeSessionLogs(resolve(root))) {
|
|
175
176
|
if (++count > this.maxFiles) {
|
|
176
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Muse native session discovery exceeded the authorized file limit');
|
|
177
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Muse native session discovery exceeded the authorized file limit', { limit: 'maxFiles', configured: this.maxFiles, observed: count });
|
|
177
178
|
}
|
|
178
179
|
yield { provider: 'muse', locator, locatorClass: MUSE_NATIVE_LOCATOR_CLASS };
|
|
179
180
|
}
|
|
@@ -203,7 +204,7 @@ export class MuseSessionAdapter {
|
|
|
203
204
|
const native = await this.readNative(source);
|
|
204
205
|
const start = parseEventCursor(cursor?.value);
|
|
205
206
|
let outputIndex = 0;
|
|
206
|
-
for (const record of
|
|
207
|
+
for await (const record of this.readNativeRecords(source)) {
|
|
207
208
|
const position = outputIndex;
|
|
208
209
|
outputIndex += 1;
|
|
209
210
|
if (position < start)
|
|
@@ -254,13 +255,13 @@ export class MuseSessionAdapter {
|
|
|
254
255
|
}
|
|
255
256
|
catch (error) {
|
|
256
257
|
if (error instanceof SessionContractError && error.code === 'SCHEMA_DRIFT') {
|
|
257
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'muse export document is not valid JSON');
|
|
258
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'muse export document is not valid JSON', error.detail);
|
|
258
259
|
}
|
|
259
260
|
throw error;
|
|
260
261
|
}
|
|
261
262
|
const parsed = MuseTrajectorySchema.safeParse(value);
|
|
262
263
|
if (!parsed.success) {
|
|
263
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'input is not a declared muse export trajectory document');
|
|
264
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'input is not a declared muse export trajectory document', schemaRejectionDetail(parsed.error));
|
|
264
265
|
}
|
|
265
266
|
assertSupportedSchemaMajor(`${parsed.data.export_schema_version}.0.0`, MUSE_EXPORT_SCHEMA_MAJOR);
|
|
266
267
|
return { trajectory: parsed.data, bytesRead };
|
|
@@ -333,80 +334,89 @@ export class MuseSessionAdapter {
|
|
|
333
334
|
};
|
|
334
335
|
}
|
|
335
336
|
async readNative(source) {
|
|
336
|
-
this.
|
|
337
|
-
let
|
|
337
|
+
const records = this.readNativeRecords(source);
|
|
338
|
+
let result = await records.next();
|
|
339
|
+
while (!result.done)
|
|
340
|
+
result = await records.next();
|
|
341
|
+
return result.value;
|
|
342
|
+
}
|
|
343
|
+
async *readNativeRecords(source) {
|
|
338
344
|
try {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
for (const line of input.records) {
|
|
352
|
-
const value = line.value;
|
|
353
|
-
if (isOmissionMarker(value)) {
|
|
354
|
-
diagnostics.omitted_records = (diagnostics.omitted_records ?? 0) + 1;
|
|
355
|
-
if (asObject(value).retained_marker === 'omitted_live_only') {
|
|
356
|
-
diagnostics.omitted_live_only = (diagnostics.omitted_live_only ?? 0) + 1;
|
|
357
|
-
}
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
const retained = MuseRetainedFrameSchema.safeParse(value);
|
|
361
|
-
if (retained.success) {
|
|
362
|
-
diagnostics.retained_frames = (diagnostics.retained_frames ?? 0) + 1;
|
|
363
|
-
for (const child of retained.data.children) {
|
|
364
|
-
let decoded;
|
|
365
|
-
try {
|
|
366
|
-
decoded = JSON.parse(child.record_json);
|
|
345
|
+
this.assertSupportedLocator(source);
|
|
346
|
+
const input = await streamBoundedJsonLines({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'complete', limits: this.limits });
|
|
347
|
+
const diagnostics = {};
|
|
348
|
+
let records = 0;
|
|
349
|
+
let fallbackSessionId = nativeSessionIdFromLocator(source.locator) ?? source.sourceId;
|
|
350
|
+
let complete = false;
|
|
351
|
+
for await (const line of input) {
|
|
352
|
+
const value = line.value;
|
|
353
|
+
if (isOmissionMarker(value)) {
|
|
354
|
+
diagnostics.omitted_records = (diagnostics.omitted_records ?? 0) + 1;
|
|
355
|
+
if (asObject(value).retained_marker === 'omitted_live_only') {
|
|
356
|
+
diagnostics.omitted_live_only = (diagnostics.omitted_live_only ?? 0) + 1;
|
|
367
357
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const retained = MuseRetainedFrameSchema.safeParse(value);
|
|
361
|
+
if (retained.success) {
|
|
362
|
+
diagnostics.retained_frames = (diagnostics.retained_frames ?? 0) + 1;
|
|
363
|
+
for (const child of retained.data.children) {
|
|
364
|
+
let decoded;
|
|
365
|
+
try {
|
|
366
|
+
decoded = JSON.parse(child.record_json);
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Muse retained frame child record is malformed JSON', schemaIssueDetail('children.record_json', 'invalid_json'));
|
|
370
|
+
}
|
|
371
|
+
const envelope = MuseEnvelopeSchema.safeParse(decoded);
|
|
372
|
+
if (!envelope.success) {
|
|
373
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Muse retained frame child record is malformed', schemaRejectionDetail(envelope.error));
|
|
374
|
+
}
|
|
375
|
+
const event = { kind: 'record', envelope: envelope.data };
|
|
376
|
+
fallbackSessionId = streamSessionIdOf(event.envelope) ?? fallbackSessionId;
|
|
377
|
+
complete ||= event.envelope.payload_type === 'session.end';
|
|
378
|
+
diagnostics.retained_frame_children = (diagnostics.retained_frame_children ?? 0) + 1;
|
|
379
|
+
records += 1;
|
|
380
|
+
yield {
|
|
381
|
+
event,
|
|
382
|
+
line,
|
|
383
|
+
retainedFrame: retained.data.retained_frame,
|
|
384
|
+
retainedChildIndex: child.child_index,
|
|
385
|
+
};
|
|
374
386
|
}
|
|
375
|
-
|
|
376
|
-
complete ||= event.envelope.payload_type === 'session.end';
|
|
377
|
-
diagnostics.retained_frame_children = (diagnostics.retained_frame_children ?? 0) + 1;
|
|
378
|
-
records.push({
|
|
379
|
-
event,
|
|
380
|
-
line,
|
|
381
|
-
retainedFrame: retained.data.retained_frame,
|
|
382
|
-
retainedChildIndex: child.child_index,
|
|
383
|
-
});
|
|
387
|
+
continue;
|
|
384
388
|
}
|
|
385
|
-
|
|
389
|
+
const envelope = MuseEnvelopeSchema.safeParse(value);
|
|
390
|
+
if (!envelope.success) {
|
|
391
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log line is not a supported record shape', schemaRejectionDetail('retained_frame' in asObject(value) ? retained.error : envelope.error));
|
|
392
|
+
}
|
|
393
|
+
const event = { kind: 'record', envelope: envelope.data };
|
|
394
|
+
fallbackSessionId = streamSessionIdOf(event.envelope) ?? fallbackSessionId;
|
|
395
|
+
complete ||= event.envelope.payload_type === 'session.end';
|
|
396
|
+
records += 1;
|
|
397
|
+
yield { event, line };
|
|
386
398
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log line is not a supported record shape');
|
|
399
|
+
if (records === 0) {
|
|
400
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log contains no record envelopes', schemaIssueDetail('<root>'));
|
|
390
401
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
402
|
+
return {
|
|
403
|
+
complete,
|
|
404
|
+
context: {
|
|
405
|
+
locatorClass: MUSE_NATIVE_LOCATOR_CLASS,
|
|
406
|
+
sourceLabel: 'muse native session log',
|
|
407
|
+
bytesRead: input.bytesRead,
|
|
408
|
+
fallbackSessionId,
|
|
409
|
+
spawns: new Map(),
|
|
410
|
+
diagnostics,
|
|
411
|
+
},
|
|
412
|
+
};
|
|
394
413
|
}
|
|
395
|
-
|
|
396
|
-
|
|
414
|
+
catch (error) {
|
|
415
|
+
if (error instanceof SessionContractError && error.code === 'SCHEMA_DRIFT') {
|
|
416
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log contains malformed JSONL', error.detail);
|
|
417
|
+
}
|
|
418
|
+
throw error;
|
|
397
419
|
}
|
|
398
|
-
return {
|
|
399
|
-
records,
|
|
400
|
-
complete,
|
|
401
|
-
context: {
|
|
402
|
-
locatorClass: MUSE_NATIVE_LOCATOR_CLASS,
|
|
403
|
-
sourceLabel: 'muse native session log',
|
|
404
|
-
bytesRead: input.bytesRead,
|
|
405
|
-
fallbackSessionId,
|
|
406
|
-
spawns: new Map(),
|
|
407
|
-
diagnostics,
|
|
408
|
-
},
|
|
409
|
-
};
|
|
410
420
|
}
|
|
411
421
|
assertSupportedLocator(source) {
|
|
412
422
|
if (source.locatorClass !== MUSE_LOCATOR_CLASS && source.locatorClass !== MUSE_NATIVE_LOCATOR_CLASS) {
|
|
@@ -500,10 +510,18 @@ async function* museNativeSessionLogs(root) {
|
|
|
500
510
|
}
|
|
501
511
|
export const MuseWorkspaceInspectionSchema = z.object({
|
|
502
512
|
outcome: z.enum(['matched', 'mismatch', 'inconclusive']),
|
|
513
|
+
attribution: z.enum(['matched', 'mismatch', 'unattributed']),
|
|
514
|
+
matchKind: z.enum(WORKSPACE_MATCH_KINDS).optional(),
|
|
503
515
|
reasonCode: z.string().nullable(),
|
|
504
516
|
recordsInspected: z.number().int().nonnegative(),
|
|
505
517
|
bytesInspected: z.number().int().nonnegative(),
|
|
506
518
|
completeInspection: z.boolean(),
|
|
519
|
+
limit: z.object({
|
|
520
|
+
name: z.enum(['maxRecords', 'maxRecordBytes', 'maxTotalBytes', 'maxNestingDepth']),
|
|
521
|
+
value: z.number().int().positive(),
|
|
522
|
+
}).optional(),
|
|
523
|
+
fallback: z.literal('bounded-tail').optional(),
|
|
524
|
+
detail: SessionRejectionDetailSchema.optional(),
|
|
507
525
|
});
|
|
508
526
|
/** Positive metadata evidence selects a source; import validates its full history. */
|
|
509
527
|
export async function inspectMuseNativeWorkspace(source, workspacePath, limitsInput) {
|
|
@@ -511,25 +529,35 @@ export async function inspectMuseNativeWorkspace(source, workspacePath, limitsIn
|
|
|
511
529
|
const progress = { recordsInspected: 0, bytesInspected: 0 };
|
|
512
530
|
let sawWorkspaceEvidence = false;
|
|
513
531
|
let retainedChildren = 0;
|
|
514
|
-
|
|
532
|
+
let limit;
|
|
533
|
+
let fallback;
|
|
534
|
+
const authorization = { selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots };
|
|
535
|
+
const receipt = (outcome, reasonCode, completeInspection = false) => ({
|
|
536
|
+
outcome, attribution: outcome === 'inconclusive' ? 'unattributed' : outcome,
|
|
537
|
+
reasonCode, ...progress, completeInspection,
|
|
538
|
+
...(limit ? { limit } : {}), ...(fallback ? { fallback } : {}),
|
|
539
|
+
});
|
|
540
|
+
const envelopesFromLine = (value) => nativeEnvelopesFromLine(value, limits, (count) => {
|
|
541
|
+
retainedChildren += count;
|
|
542
|
+
if (retainedChildren > limits.maxRecords) {
|
|
543
|
+
throw new MuseWorkspaceLimitError('maxRecords', limits.maxRecords, retainedChildren);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
515
546
|
try {
|
|
516
|
-
if (
|
|
547
|
+
if ([limits.maxRecords, limits.maxRecordBytes, limits.maxTotalBytes, limits.maxNestingDepth]
|
|
548
|
+
.some((limit) => !Number.isSafeInteger(limit) || limit <= 0)) {
|
|
517
549
|
throw new SessionContractError('INVALID_ARGUMENT', 'Muse inspection limits must be positive safe integers');
|
|
518
550
|
}
|
|
519
|
-
for await (const value of museWorkspaceScanLines(
|
|
520
|
-
const envelopes =
|
|
521
|
-
retainedChildren += count;
|
|
522
|
-
if (retainedChildren > limits.maxRecords) {
|
|
523
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Muse retained child inspection limit exceeded');
|
|
524
|
-
}
|
|
525
|
-
});
|
|
551
|
+
for await (const value of museWorkspaceScanLines(authorization, limits, progress)) {
|
|
552
|
+
const envelopes = envelopesFromLine(value);
|
|
526
553
|
for (const envelope of envelopes) {
|
|
527
554
|
const candidate = workspaceEvidenceOf(envelope);
|
|
528
555
|
if (!candidate)
|
|
529
556
|
continue;
|
|
530
557
|
sawWorkspaceEvidence = true;
|
|
531
|
-
|
|
532
|
-
|
|
558
|
+
const matchKind = await matchSessionWorkspace(candidate, workspacePath);
|
|
559
|
+
if (matchKind)
|
|
560
|
+
return { ...receipt('matched', null), matchKind };
|
|
533
561
|
}
|
|
534
562
|
}
|
|
535
563
|
return sawWorkspaceEvidence
|
|
@@ -537,15 +565,47 @@ export async function inspectMuseNativeWorkspace(source, workspacePath, limitsIn
|
|
|
537
565
|
: receipt('inconclusive', 'MUSE_WORKSPACE_EVIDENCE_ABSENT', true);
|
|
538
566
|
}
|
|
539
567
|
catch (error) {
|
|
540
|
-
|
|
541
|
-
|
|
568
|
+
if (error instanceof MuseWorkspaceLimitError) {
|
|
569
|
+
limit = { name: error.limitName, value: error.limitValue };
|
|
570
|
+
fallback = 'bounded-tail';
|
|
571
|
+
const tailProgress = { recordsInspected: 0, bytesInspected: 0 };
|
|
572
|
+
const tailBytes = Math.min(MUSE_WORKSPACE_TAIL_BYTES, limits.maxTotalBytes);
|
|
573
|
+
retainedChildren = 0;
|
|
574
|
+
let matchKind;
|
|
575
|
+
try {
|
|
576
|
+
for await (const value of museWorkspaceScanLines(authorization, { ...limits, maxTotalBytes: tailBytes }, tailProgress, tailBytes)) {
|
|
577
|
+
for (const envelope of envelopesFromLine(value)) {
|
|
578
|
+
const candidate = workspaceEvidenceOf(envelope);
|
|
579
|
+
if (!candidate)
|
|
580
|
+
continue;
|
|
581
|
+
matchKind = await matchSessionWorkspace(candidate, workspacePath);
|
|
582
|
+
if (matchKind)
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
if (matchKind)
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
// Preserve the original limit receipt when the fallback cannot attribute.
|
|
591
|
+
}
|
|
592
|
+
progress.recordsInspected += tailProgress.recordsInspected;
|
|
593
|
+
progress.bytesInspected += tailProgress.bytesInspected;
|
|
594
|
+
if (matchKind)
|
|
595
|
+
return { ...receipt('matched', null), matchKind };
|
|
596
|
+
}
|
|
597
|
+
return {
|
|
598
|
+
...receipt('inconclusive', error instanceof SessionContractError
|
|
599
|
+
? error.code : 'MUSE_SOURCE_UNREADABLE'),
|
|
600
|
+
...(error instanceof SessionContractError && error.detail ? { detail: error.detail } : {}),
|
|
601
|
+
};
|
|
542
602
|
}
|
|
543
603
|
}
|
|
544
604
|
export async function museNativeSourceMatchesWorkspace(source, workspacePath) {
|
|
545
605
|
const result = await inspectMuseNativeWorkspace(source, workspacePath);
|
|
546
606
|
if (result.outcome === 'inconclusive') {
|
|
547
607
|
const code = SessionErrorCodeSchema.safeParse(result.reasonCode);
|
|
548
|
-
throw new SessionContractError(code.success ? code.data : 'SCHEMA_DRIFT', `Muse workspace inspection inconclusive: ${result.reasonCode}
|
|
608
|
+
throw new SessionContractError(code.success ? code.data : 'SCHEMA_DRIFT', `Muse workspace inspection inconclusive: ${result.reasonCode}`, result.detail);
|
|
549
609
|
}
|
|
550
610
|
return result.outcome === 'matched';
|
|
551
611
|
}
|
|
@@ -555,7 +615,7 @@ function nativeEnvelopesFromLine(value, limits, countChildren) {
|
|
|
555
615
|
const retained = MuseRetainedFrameSchema.safeParse(value);
|
|
556
616
|
if (!retained.success) {
|
|
557
617
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
558
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered a non-object native record');
|
|
618
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered a non-object native record', schemaIssueDetail('<root>'));
|
|
559
619
|
}
|
|
560
620
|
// Native logs carry record kinds the envelope schema does not model (for
|
|
561
621
|
// example runtime.session mode changes without payload.event.kind). They
|
|
@@ -567,7 +627,7 @@ function nativeEnvelopesFromLine(value, limits, countChildren) {
|
|
|
567
627
|
const envelopes = [];
|
|
568
628
|
for (const child of retained.data.children) {
|
|
569
629
|
if (Buffer.byteLength(child.record_json) > limits.maxRecordBytes) {
|
|
570
|
-
throw new
|
|
630
|
+
throw new MuseWorkspaceLimitError('maxRecordBytes', limits.maxRecordBytes, Buffer.byteLength(child.record_json));
|
|
571
631
|
}
|
|
572
632
|
const childEvent = recordEventFromEnvelope(parseMuseScanJson(child.record_json, limits.maxNestingDepth));
|
|
573
633
|
if (childEvent)
|
|
@@ -593,9 +653,6 @@ function workspaceEvidenceOf(envelope) {
|
|
|
593
653
|
}
|
|
594
654
|
return null;
|
|
595
655
|
}
|
|
596
|
-
function workspacePathEquals(candidate, workspacePath) {
|
|
597
|
-
return resolve(candidate) === workspacePath;
|
|
598
|
-
}
|
|
599
656
|
function nativeSessionIdFromLocator(locator) {
|
|
600
657
|
const parts = locator.replaceAll('\\', '/').split('/');
|
|
601
658
|
const leaf = parts.at(-1);
|
|
@@ -742,7 +799,7 @@ function parseEventCursor(value) {
|
|
|
742
799
|
if (value === undefined || value === '')
|
|
743
800
|
return 0;
|
|
744
801
|
if (!/^\d+$/.test(value))
|
|
745
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'muse event cursor is invalid');
|
|
802
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'muse event cursor is invalid', schemaIssueDetail('cursor'));
|
|
746
803
|
return Number(value);
|
|
747
804
|
}
|
|
748
805
|
//# sourceMappingURL=muse.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { opendir } from 'node:fs/promises';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { SessionContractError, } from '../contracts.js';
|
|
5
|
-
import {
|
|
4
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, } from '../contracts.js';
|
|
5
|
+
import { streamBoundedJsonLines } from '../readers.js';
|
|
6
6
|
export const OMP_ADAPTER_VERSION = '1.0.0';
|
|
7
7
|
export const OMP_SOURCE_SCHEMA_VERSION = '3.0.0';
|
|
8
8
|
const Title = z.object({ type: z.literal('title'), v: z.literal(1), title: z.string(), source: z.enum(['auto', 'user']).optional(), updatedAt: z.string(), pad: z.string() });
|
|
@@ -30,7 +30,7 @@ export class OmpSessionAdapter {
|
|
|
30
30
|
for (const root of [...scope.allowedRoots].sort()) {
|
|
31
31
|
for await (const locator of jsonlFiles(resolve(root))) {
|
|
32
32
|
if (++count > this.maxFiles)
|
|
33
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OMP discovery file limit exceeded');
|
|
33
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OMP discovery file limit exceeded', { limit: 'maxFiles', configured: this.maxFiles, observed: count });
|
|
34
34
|
yield { provider: 'omp', locator, locatorClass: 'omp-session-v3-jsonl' };
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -38,13 +38,23 @@ export class OmpSessionAdapter {
|
|
|
38
38
|
async inspect(source) {
|
|
39
39
|
let input;
|
|
40
40
|
try {
|
|
41
|
-
input = await
|
|
41
|
+
input = await streamBoundedJsonLines({ selectedPath: source.locator,
|
|
42
42
|
allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
|
|
43
43
|
}
|
|
44
44
|
catch (error) {
|
|
45
45
|
throw normalizeAuthorizationError(error);
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
let header;
|
|
48
|
+
let prefix = true;
|
|
49
|
+
for await (const line of input) {
|
|
50
|
+
if (prefix) {
|
|
51
|
+
prefix = false;
|
|
52
|
+
if (titleOffset(line))
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
header ??= parseHeader(line.value);
|
|
56
|
+
}
|
|
57
|
+
header ??= parseHeader(undefined);
|
|
48
58
|
if (input.incompleteTail)
|
|
49
59
|
throw new SessionContractError('TRUNCATED_SOURCE', 'OMP session has a truncated JSONL tail');
|
|
50
60
|
return { sourceSchemaVersion: `${header.version}.0.0`, consistency: 'complete', operationalState: 'available' };
|
|
@@ -63,13 +73,13 @@ export class OmpSessionAdapter {
|
|
|
63
73
|
let prefix = true;
|
|
64
74
|
const start = cursor?.value.replace(/^byte:/, '') ?? '0';
|
|
65
75
|
if (!/^\d+$/.test(start))
|
|
66
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OMP cursor');
|
|
76
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OMP cursor', schemaIssueDetail('cursor'));
|
|
67
77
|
let end = 0;
|
|
68
78
|
const ids = new Set();
|
|
69
79
|
for await (const line of input) {
|
|
70
80
|
end = line.byteOffset + line.byteLength;
|
|
71
81
|
if (Number(start) > line.byteOffset && Number(start) < end) {
|
|
72
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor is not a record boundary');
|
|
82
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor is not a record boundary', schemaIssueDetail('cursor'));
|
|
73
83
|
}
|
|
74
84
|
if (prefix) {
|
|
75
85
|
prefix = false;
|
|
@@ -83,7 +93,7 @@ export class OmpSessionAdapter {
|
|
|
83
93
|
}
|
|
84
94
|
const parsed = Entry.safeParse(line.value);
|
|
85
95
|
if (!parsed.success)
|
|
86
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OMP session entry is malformed');
|
|
96
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OMP session entry is malformed', schemaRejectionDetail(parsed.error));
|
|
87
97
|
const entry = parsed.data;
|
|
88
98
|
if (ids.has(entry.id))
|
|
89
99
|
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'OMP session contains a duplicate entry id');
|
|
@@ -107,9 +117,9 @@ export class OmpSessionAdapter {
|
|
|
107
117
|
nativeType: entry.type, parentId: entry.parentId, ...metadata(entry, message) } };
|
|
108
118
|
}
|
|
109
119
|
if (!sessionId)
|
|
110
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is missing');
|
|
120
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is missing', schemaIssueDetail('header'));
|
|
111
121
|
if (Number(start) > end)
|
|
112
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor exceeds source size');
|
|
122
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor exceeds source size', schemaIssueDetail('cursor'));
|
|
113
123
|
if (input.incompleteTail)
|
|
114
124
|
throw new SessionContractError('TRUNCATED_SOURCE', 'OMP session has a truncated JSONL tail');
|
|
115
125
|
}
|
|
@@ -125,7 +135,7 @@ const KNOWN = new Set(['message', 'thinking_level_change', 'model_change', 'comp
|
|
|
125
135
|
function parseHeader(value) {
|
|
126
136
|
const parsed = Header.safeParse(value);
|
|
127
137
|
if (!parsed.success)
|
|
128
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is malformed');
|
|
138
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is malformed', schemaRejectionDetail(parsed.error));
|
|
129
139
|
if (parsed.data.version !== 3)
|
|
130
140
|
throw new SessionContractError('UNKNOWN_SCHEMA_MAJOR', `unsupported OMP session version: ${parsed.data.version}`);
|
|
131
141
|
return parsed.data;
|
|
@@ -168,8 +178,12 @@ function normalizeAuthorizationError(error) {
|
|
|
168
178
|
function titleOffset(line) {
|
|
169
179
|
if (object(line?.value).type !== 'title')
|
|
170
180
|
return 0;
|
|
171
|
-
|
|
172
|
-
|
|
181
|
+
const parsed = Title.safeParse(line?.value);
|
|
182
|
+
if (!parsed.success) {
|
|
183
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OMP title slot is malformed', schemaRejectionDetail(parsed.error));
|
|
184
|
+
}
|
|
185
|
+
if (line?.byteLength !== 256) {
|
|
186
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OMP title slot is malformed', schemaIssueDetail('title'));
|
|
173
187
|
}
|
|
174
188
|
return 256;
|
|
175
189
|
}
|
|
@@ -198,6 +212,6 @@ export async function readOmpSessionHeader(source) {
|
|
|
198
212
|
}
|
|
199
213
|
return parseHeader(line.value);
|
|
200
214
|
}
|
|
201
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OMP header is missing');
|
|
215
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OMP header is missing', schemaIssueDetail('header'));
|
|
202
216
|
}
|
|
203
217
|
//# sourceMappingURL=omp.js.map
|